• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
31 void
lws_spawn_timeout(struct lws_sorted_usec_list * sul)32 lws_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 
42 void
lws_spawn_sul_reap(struct lws_sorted_usec_list * sul)43 lws_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 
64 static struct lws *
lws_create_basic_wsi(struct lws_context * context,int tsi,const struct lws_role_ops * ops)65 lws_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 
110 void
lws_spawn_piped_destroy(struct lws_spawn_piped ** _lsp)111 lws_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 
148 int
lws_spawn_reap(struct lws_spawn_piped * lsp)149 lws_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 
228 int
lws_spawn_piped_kill_child_process(struct lws_spawn_piped * lsp)229 lws_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 
249 static void
windows_pipe_poll_hack(lws_sorted_usec_list_t * sul)250 windows_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 
339 struct lws_spawn_piped *
lws_spawn_piped(const struct lws_spawn_piped_info * i)340 lws_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 
528 bail3:
529 
530 	lws_sul_cancel(&lsp->sul_poll);
531 
532 	while (--n >= 0)
533 		__remove_wsi_socket_from_fds(lsp->stdwsi[n]);
534 bail2:
535 	for (n = 0; n < 3; n++)
536 		if (lsp->stdwsi[n])
537 			__lws_free_wsi(lsp->stdwsi[n]);
538 
539 bail1:
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 
554 void
lws_spawn_stdwsi_closed(struct lws_spawn_piped * lsp,struct lws * wsi)555 lws_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 
571 int
lws_spawn_get_stdfd(struct lws * wsi)572 lws_spawn_get_stdfd(struct lws *wsi)
573 {
574 	return wsi->lsp_channel;
575 }
576