1/* 2 * libwebsockets - small server side websockets and web server implementation 3 * 4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25#include "private-lib-core.h" 26 27#include <tchar.h> 28#include <stdio.h> 29#include <strsafe.h> 30 31void 32lws_spawn_timeout(struct lws_sorted_usec_list *sul) 33{ 34 struct lws_spawn_piped *lsp = lws_container_of(sul, 35 struct lws_spawn_piped, sul); 36 37 lwsl_warn("%s: spawn exceeded timeout, killing\n", __func__); 38 39 lws_spawn_piped_kill_child_process(lsp); 40} 41 42void 43lws_spawn_sul_reap(struct lws_sorted_usec_list *sul) 44{ 45 struct lws_spawn_piped *lsp = lws_container_of(sul, 46 struct lws_spawn_piped, sul_reap); 47 48 lwsl_notice("%s: reaping spawn after last stdpipe, tries left %d\n", 49 __func__, lsp->reap_retry_budget); 50 if (!lws_spawn_reap(lsp) && !lsp->pipes_alive) { 51 if (--lsp->reap_retry_budget) { 52 lws_sul_schedule(lsp->info.vh->context, lsp->info.tsi, 53 &lsp->sul_reap, lws_spawn_sul_reap, 54 250 * LWS_US_PER_MS); 55 } else { 56 lwsl_err("%s: Unable to reap lsp %p, killing\n", 57 __func__, lsp); 58 lsp->reap_retry_budget = 20; 59 lws_spawn_piped_kill_child_process(lsp); 60 } 61 } 62} 63 64static struct lws * 65lws_create_basic_wsi(struct lws_context *context, int tsi, 66 const struct lws_role_ops *ops) 67{ 68 struct lws_context_per_thread *pt = &context->pt[tsi]; 69 struct lws *new_wsi; 70 71 if (!context->vhost_list) 72 return NULL; 73 74 if ((unsigned int)context->pt[tsi].fds_count == 75 context->fd_limit_per_thread - 1) { 76 lwsl_err("no space for new conn\n"); 77 return NULL; 78 } 79 80 lws_context_lock(context, __func__); 81 new_wsi = __lws_wsi_create_with_role(context, tsi, ops, NULL); 82 lws_context_unlock(context); 83 if (new_wsi == NULL) { 84 lwsl_err("Out of memory for new connection\n"); 85 return NULL; 86 } 87 88 new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; 89 90 /* initialize the instance struct */ 91 92 lws_role_transition(new_wsi, 0, LRS_ESTABLISHED, ops); 93 94 new_wsi->hdr_parsing_completed = 0; 95 new_wsi->position_in_fds_table = LWS_NO_FDS_POS; 96 97 /* 98 * these can only be set once the protocol is known 99 * we set an unestablished connection's protocol pointer 100 * to the start of the defauly vhost supported list, so it can look 101 * for matching ones during the handshake 102 */ 103 104 new_wsi->user_space = NULL; 105 new_wsi->desc.sockfd = LWS_SOCK_INVALID; 106 107 return new_wsi; 108} 109 110void 111lws_spawn_piped_destroy(struct lws_spawn_piped **_lsp) 112{ 113 struct lws_spawn_piped *lsp = *_lsp; 114 struct lws *wsi; 115 int n; 116 117 if (!lsp) 118 return; 119 120 for (n = 0; n < 3; n++) { 121 if (lsp->pipe_fds[n][!!(n == 0)]) { 122 CloseHandle(lsp->pipe_fds[n][n == 0]); 123 lsp->pipe_fds[n][n == 0] = NULL; 124 } 125 126 for (n = 0; n < 3; n++) { 127 if (lsp->stdwsi[n]) { 128 lwsl_notice("%s: closing stdwsi %d\n", __func__, n); 129 wsi = lsp->stdwsi[n]; 130 lsp->stdwsi[n]->desc.filefd = NULL; 131 lsp->stdwsi[n] = NULL; 132 lws_set_timeout(wsi, 1, LWS_TO_KILL_SYNC); 133 } 134 } 135 } 136 137 lws_dll2_remove(&lsp->dll); 138 139 lws_sul_cancel(&lsp->sul); 140 lws_sul_cancel(&lsp->sul_reap); 141 lws_sul_cancel(&lsp->sul_poll); 142 143 lwsl_warn("%s: deleting lsp\n", __func__); 144 145 lws_free_set_NULL((*_lsp)); 146} 147 148int 149lws_spawn_reap(struct lws_spawn_piped *lsp) 150{ 151 152 void *opaque = lsp->info.opaque; 153 lsp_cb_t cb = lsp->info.reap_cb; 154 struct _lws_siginfo_t lsi; 155 lws_usec_t acct[4]; 156 DWORD ex; 157 158 if (!lsp->child_pid) 159 return 0; 160 161 if (!GetExitCodeProcess(lsp->child_pid, &ex)) { 162 lwsl_notice("%s: GetExitCodeProcess failed\n", __func__); 163 return 0; 164 } 165 166 /* nonzero = success */ 167 168 if (ex == STILL_ACTIVE) { 169 lwsl_notice("%s: still active\n", __func__); 170 return 0; 171 } 172 173 /* mark the earliest time we knew he had gone */ 174 if (!lsp->reaped) { 175 lsp->reaped = lws_now_usecs(); 176 177 /* 178 * Switch the timeout to restrict the amount of grace time 179 * to drain stdwsi 180 */ 181 182 lws_sul_schedule(lsp->info.vh->context, lsp->info.tsi, 183 &lsp->sul, lws_spawn_timeout, 184 5 * LWS_US_PER_SEC); 185 } 186 187 /* 188 * Stage finalizing our reaction to the process going down until the 189 * stdwsi flushed whatever is in flight and all noticed they were 190 * closed. For that reason, each stdwsi close must call lws_spawn_reap 191 * to check if that was the last one and we can proceed with the reap. 192 */ 193 194 if (!lsp->ungraceful && lsp->pipes_alive) { 195 lwsl_notice("%s: stdwsi alive, not reaping\n", __func__); 196 return 0; 197 } 198 199 /* we reached the reap point, no need for timeout wait */ 200 201 lws_sul_cancel(&lsp->sul); 202 203 /* 204 * All the stdwsi went down, nothing more is coming... it's over 205 * Collect the final information and then reap the dead process 206 */ 207 208 lsi.retcode = 0x10000 | (int)ex; 209 lwsl_notice("%s: process exit 0x%x\n", __func__, lsi.retcode); 210 lsp->child_pid = NULL; 211 212 /* destroy the lsp itself first (it's freed and plsp set NULL */ 213 214 if (lsp->info.plsp) 215 lws_spawn_piped_destroy(lsp->info.plsp); 216 217 /* then do the parent callback informing it's destroyed */ 218 219 memset(acct, 0, sizeof(acct)); 220 if (cb) 221 cb(opaque, acct, &lsi, 0); 222 223 lwsl_notice("%s: completed reap\n", __func__); 224 225 return 1; /* was reaped */ 226} 227 228int 229lws_spawn_piped_kill_child_process(struct lws_spawn_piped *lsp) 230{ 231 if (!lsp->child_pid) 232 return 1; 233 234 lsp->ungraceful = 1; /* don't wait for flushing, just kill it */ 235 236 if (lws_spawn_reap(lsp)) 237 /* that may have invalidated lsp */ 238 return 0; 239 240 lwsl_warn("%s: calling TerminateProcess on child pid\n", __func__); 241 TerminateProcess(lsp->child_pid, 252); 242 lws_spawn_reap(lsp); 243 244 /* that may have invalidated lsp */ 245 246 return 0; 247} 248 249static void 250windows_pipe_poll_hack(lws_sorted_usec_list_t *sul) 251{ 252 struct lws_spawn_piped *lsp = lws_container_of(sul, 253 struct lws_spawn_piped, sul_poll); 254 struct lws *wsi, *wsi1; 255 DWORD br; 256 char c; 257 258 /* 259 * Do it first, we know lsp exists and if it's destroyed inbetweentimes, 260 * it will already have cancelled this 261 */ 262 263 lws_sul_schedule(lsp->context, 0, &lsp->sul_poll, 264 windows_pipe_poll_hack, 50 * LWS_US_PER_MS); 265 266 wsi = lsp->stdwsi[LWS_STDOUT]; 267 wsi1 = lsp->stdwsi[LWS_STDERR]; 268 if (wsi && lsp->pipe_fds[LWS_STDOUT][0] != NULL) { 269 if (!PeekNamedPipe(lsp->pipe_fds[LWS_STDOUT][0], &c, 1, &br, 270 NULL, NULL)) { 271 272 lwsl_notice("%s: stdout pipe errored\n", __func__); 273 CloseHandle(lsp->stdwsi[LWS_STDOUT]->desc.filefd); 274 lsp->pipe_fds[LWS_STDOUT][0] = NULL; 275 lsp->stdwsi[LWS_STDOUT]->desc.filefd = NULL; 276 lsp->stdwsi[LWS_STDOUT] = NULL; 277 lws_set_timeout(wsi, 1, LWS_TO_KILL_SYNC); 278 279 if (lsp->stdwsi[LWS_STDIN]) { 280 lwsl_notice("%s: closing stdin from stdout close\n", 281 __func__); 282 CloseHandle(lsp->stdwsi[LWS_STDIN]->desc.filefd); 283 wsi = lsp->stdwsi[LWS_STDIN]; 284 lsp->stdwsi[LWS_STDIN]->desc.filefd = NULL; 285 lsp->stdwsi[LWS_STDIN] = NULL; 286 lsp->pipe_fds[LWS_STDIN][1] = NULL; 287 lws_set_timeout(wsi, 1, LWS_TO_KILL_SYNC); 288 } 289 290 /* 291 * lsp may be destroyed by here... if we wanted to 292 * handle a still-extant stderr we'll get it next time 293 */ 294 295 return; 296 } else 297 if (br) 298 wsi->a.protocol->callback(wsi, 299 LWS_CALLBACK_RAW_RX_FILE, 300 NULL, NULL, 0); 301 } 302 303 /* 304 * lsp may have been destroyed above 305 */ 306 307 if (wsi1 && lsp->pipe_fds[LWS_STDERR][0]) { 308 if (!PeekNamedPipe(lsp->pipe_fds[LWS_STDERR][0], &c, 1, &br, 309 NULL, NULL)) { 310 311 lwsl_notice("%s: stderr pipe errored\n", __func__); 312 CloseHandle(wsi1->desc.filefd); 313 /* 314 * Assume is stderr still extant on entry, lsp can't 315 * have been destroyed by stdout/stdin processing 316 */ 317 lsp->stdwsi[LWS_STDERR]->desc.filefd = NULL; 318 lsp->stdwsi[LWS_STDERR] = NULL; 319 lsp->pipe_fds[LWS_STDERR][0] = NULL; 320 lws_set_timeout(wsi1, 1, LWS_TO_KILL_SYNC); 321 /* 322 * lsp may have been destroyed above 323 */ 324 } else 325 if (br) 326 wsi1->a.protocol->callback(wsi1, 327 LWS_CALLBACK_RAW_RX_FILE, 328 NULL, NULL, 0); 329 } 330} 331 332 333 334/* 335 * Deals with spawning a subprocess and executing it securely with stdin/out/err 336 * diverted into pipes 337 */ 338 339struct lws_spawn_piped * 340lws_spawn_piped(const struct lws_spawn_piped_info *i) 341{ 342 const struct lws_protocols *pcol = i->vh->context->vhost_list->protocols; 343 struct lws_context *context = i->vh->context; 344 struct lws_spawn_piped *lsp; 345 PROCESS_INFORMATION pi; 346 SECURITY_ATTRIBUTES sa; 347 char cli[300], *p; 348 STARTUPINFO si; 349 int n; 350 351 if (i->protocol_name) 352 pcol = lws_vhost_name_to_protocol(i->vh, i->protocol_name); 353 if (!pcol) { 354 lwsl_err("%s: unknown protocol %s\n", __func__, 355 i->protocol_name ? i->protocol_name : "default"); 356 357 return NULL; 358 } 359 360 lsp = lws_zalloc(sizeof(*lsp), __func__); 361 if (!lsp) { 362 lwsl_err("%s: OOM\n", __func__); 363 return NULL; 364 } 365 366 /* wholesale take a copy of info */ 367 lsp->info = *i; 368 lsp->context = context; 369 lsp->reap_retry_budget = 20; 370 371 /* 372 * Prepare the stdin / out / err pipes 373 */ 374 375 for (n = 0; n < 3; n++) { 376 lsp->pipe_fds[n][0] = NULL; 377 lsp->pipe_fds[n][1] = NULL; 378 } 379 380 /* create pipes for [stdin|stdout] and [stderr] */ 381 382 memset(&sa, 0, sizeof(sa)); 383 sa.nLength = sizeof(SECURITY_ATTRIBUTES); 384 sa.bInheritHandle = TRUE; /* inherit the pipes */ 385 sa.lpSecurityDescriptor = NULL; 386 387 for (n = 0; n < 3; n++) { 388 DWORD waitmode = PIPE_NOWAIT; 389 390 if (!CreatePipe(&lsp->pipe_fds[n][0], &lsp->pipe_fds[n][1], 391 &sa, 0)) { 392 lwsl_err("%s: CreatePipe() failed\n", __func__); 393 goto bail1; 394 } 395 396 SetNamedPipeHandleState(lsp->pipe_fds[1][0], &waitmode, NULL, NULL); 397 SetNamedPipeHandleState(lsp->pipe_fds[2][0], &waitmode, NULL, NULL); 398 399 /* don't inherit the pipe side that belongs to the parent */ 400 401 if (!SetHandleInformation(&lsp->pipe_fds[n][!n], 402 HANDLE_FLAG_INHERIT, 0)) { 403 lwsl_err("%s: SetHandleInformation() failed\n", __func__); 404 //goto bail1; 405 } 406 } 407 408 /* create wsis for each stdin/out/err fd */ 409 410 for (n = 0; n < 3; n++) { 411 lsp->stdwsi[n] = lws_create_basic_wsi(i->vh->context, i->tsi, 412 i->ops ? i->ops : &role_ops_raw_file); 413 if (!lsp->stdwsi[n]) { 414 lwsl_err("%s: unable to create lsp stdwsi\n", __func__); 415 goto bail2; 416 } 417 418 __lws_lc_tag(i->vh->context, &i->vh->context->lcg[LWSLCG_WSI], 419 &lsp->stdwsi[n]->lc, "nspawn-stdwsi-%d", n); 420 421 lsp->stdwsi[n]->lsp_channel = n; 422 lws_vhost_bind_wsi(i->vh, lsp->stdwsi[n]); 423 lsp->stdwsi[n]->a.protocol = pcol; 424 lsp->stdwsi[n]->a.opaque_user_data = i->opaque; 425 426 lsp->stdwsi[n]->desc.filefd = lsp->pipe_fds[n][!n]; 427 lsp->stdwsi[n]->file_desc = 1; 428 429 lwsl_debug("%s: lsp stdwsi %p: pipe idx %d -> fd %d / %d\n", 430 __func__, lsp->stdwsi[n], n, 431 lsp->pipe_fds[n][!!(n == 0)], 432 lsp->pipe_fds[n][!(n == 0)]); 433 434#if 0 435 436 /* read side is 0, stdin we want the write side, others read */ 437 438 lsp->stdwsi[n]->desc.filefd = lsp->pipe_fds[n][!!(n == 0)]; 439 if (fcntl(lsp->pipe_fds[n][!!(n == 0)], F_SETFL, O_NONBLOCK) < 0) { 440 lwsl_err("%s: setting NONBLOCK failed\n", __func__); 441 goto bail2; 442 } 443#endif 444 } 445 446 for (n = 0; n < 3; n++) 447 if (i->opt_parent) { 448 lsp->stdwsi[n]->parent = i->opt_parent; 449 lsp->stdwsi[n]->sibling_list = i->opt_parent->child_list; 450 i->opt_parent->child_list = lsp->stdwsi[n]; 451 } 452 453 lwsl_notice("%s: pipe handles in %p, out %p, err %p\n", __func__, 454 lsp->stdwsi[LWS_STDIN]->desc.sockfd, 455 lsp->stdwsi[LWS_STDOUT]->desc.sockfd, 456 lsp->stdwsi[LWS_STDERR]->desc.sockfd); 457 458 /* 459 * Windows nonblocking pipe handling is a mess that is unable 460 * to interoperate with WSA-based wait as far as I can tell. 461 * 462 * Let's set up a sul to poll the pipes and synthesize the 463 * protocol callbacks if anything coming. 464 */ 465 lws_sul_schedule(context, 0, &lsp->sul_poll, windows_pipe_poll_hack, 466 50 * LWS_US_PER_MS); 467 468 469 /* 470 * Windows wants a single string commandline 471 */ 472 p = cli; 473 n = 0; 474 while (i->exec_array[n]) { 475 lws_strncpy(p, i->exec_array[n], 476 sizeof(cli) - lws_ptr_diff(p, cli)); 477 if (sizeof(cli) - lws_ptr_diff(p, cli) < 4) 478 break; 479 p += strlen(p); 480 *p++ = ' '; 481 *p = '\0'; 482 n++; 483 } 484 485 puts(cli); 486 487 memset(&pi, 0, sizeof(pi)); 488 memset(&si, 0, sizeof(si)); 489 490 si.cb = sizeof(STARTUPINFO); 491 si.hStdInput = lsp->pipe_fds[LWS_STDIN][0]; 492 si.hStdOutput = lsp->pipe_fds[LWS_STDOUT][1]; 493 si.hStdError = lsp->pipe_fds[LWS_STDERR][1]; 494 si.dwFlags = STARTF_USESTDHANDLES | CREATE_NO_WINDOW; 495 si.wShowWindow = TRUE; 496 497 if (!CreateProcess(NULL, cli, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { 498 lwsl_err("%s: CreateProcess failed 0x%x\n", __func__, 499 (unsigned long)GetLastError()); 500 goto bail3; 501 } 502 503 lsp->child_pid = pi.hProcess; 504 505 lwsl_notice("%s: lsp %p spawned PID %d\n", __func__, lsp, lsp->child_pid); 506 507 lws_sul_schedule(context, i->tsi, &lsp->sul, lws_spawn_timeout, 508 i->timeout_us ? i->timeout_us : 300 * LWS_US_PER_SEC); 509 510 /* 511 * close: stdin:r, stdout:w, stderr:w 512 */ 513 for (n = 0; n < 3; n++) 514 CloseHandle(lsp->pipe_fds[n][n != 0]); 515 516 lsp->pipes_alive = 3; 517 lsp->created = lws_now_usecs(); 518 519 if (i->owner) 520 lws_dll2_add_head(&lsp->dll, i->owner); 521 522 if (i->timeout_us) 523 lws_sul_schedule(context, i->tsi, &lsp->sul, 524 lws_spawn_timeout, i->timeout_us); 525 526 return lsp; 527 528bail3: 529 530 lws_sul_cancel(&lsp->sul_poll); 531 532 while (--n >= 0) 533 __remove_wsi_socket_from_fds(lsp->stdwsi[n]); 534bail2: 535 for (n = 0; n < 3; n++) 536 if (lsp->stdwsi[n]) 537 __lws_free_wsi(lsp->stdwsi[n]); 538 539bail1: 540 for (n = 0; n < 3; n++) { 541 if (lsp->pipe_fds[n][0] >= 0) 542 CloseHandle(lsp->pipe_fds[n][0]); 543 if (lsp->pipe_fds[n][1] >= 0) 544 CloseHandle(lsp->pipe_fds[n][1]); 545 } 546 547 lws_free(lsp); 548 549 lwsl_err("%s: failed\n", __func__); 550 551 return NULL; 552} 553 554void 555lws_spawn_stdwsi_closed(struct lws_spawn_piped *lsp, struct lws *wsi) 556{ 557 int n; 558 559 assert(lsp); 560 lsp->pipes_alive--; 561 lwsl_debug("%s: pipes alive %d\n", __func__, lsp->pipes_alive); 562 if (!lsp->pipes_alive) 563 lws_sul_schedule(lsp->info.vh->context, lsp->info.tsi, 564 &lsp->sul_reap, lws_spawn_sul_reap, 1); 565 566 for (n = 0; n < 3; n++) 567 if (lsp->stdwsi[n] == wsi) 568 lsp->stdwsi[n] = NULL; 569} 570 571int 572lws_spawn_get_stdfd(struct lws *wsi) 573{ 574 return wsi->lsp_channel; 575} 576