xref: /netbsd-src/external/bsd/ntp/dist/libntp/work_fork.c (revision b65092432b7907137a107da7d1098089432234aa)
1 /*	$NetBSD: work_fork.c,v 1.15 2020/10/10 13:41:14 christos Exp $	*/
2 
3 /*
4  * work_fork.c - fork implementation for blocking worker child.
5  */
6 #include <config.h>
7 #include "ntp_workimpl.h"
8 
9 #ifdef WORK_FORK
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <signal.h>
13 #include <sys/wait.h>
14 
15 #include "iosignal.h"
16 #include "ntp_stdlib.h"
17 #include "ntp_malloc.h"
18 #include "ntp_syslog.h"
19 #include "ntpd.h"
20 #include "ntp_io.h"
21 #include "ntp_assert.h"
22 #include "ntp_unixtime.h"
23 #include "ntp_worker.h"
24 
25 /* === variables === */
26 	int			worker_process;
27 	addremove_io_fd_func	addremove_io_fd;
28 static	volatile int		worker_sighup_received;
29 int	saved_argc = 0;
30 char	**saved_argv;
31 
32 /* === function prototypes === */
33 static	void		fork_blocking_child(blocking_child *);
34 static	RETSIGTYPE	worker_sighup(int);
35 static	void		send_worker_home_atexit(void);
36 static	void		cleanup_after_child(blocking_child *);
37 
38 /* === I/O helpers === */
39 /* Since we have signals enabled, there's a good chance that blocking IO
40  * via pipe suffers from EINTR -- and this goes for both directions.
41  * The next two wrappers will loop until either all the data is written
42  * or read, plus handling the EOF condition on read. They may return
43  * zero if no data was transferred at all, and effectively every return
44  * value that differs from the given transfer length signifies an error
45  * condition.
46  */
47 
48 static size_t
netread(int fd,void * vb,size_t l)49 netread(
50 	int		fd,
51 	void *		vb,
52 	size_t		l
53 	)
54 {
55 	char *		b = vb;
56 	ssize_t		r;
57 
58 	while (l) {
59 		r = read(fd, b, l);
60 		if (r > 0) {
61 			l -= r;
62 			b += r;
63 		} else if (r == 0 || errno != EINTR) {
64 			l = 0;
65 		}
66 	}
67 	return (size_t)(b - (char *)vb);
68 }
69 
70 
71 static size_t
netwrite(int fd,const void * vb,size_t l)72 netwrite(
73 	int		fd,
74 	const void *	vb,
75 	size_t		l
76 	)
77 {
78 	const char *	b = vb;
79 	ssize_t		w;
80 
81 	while (l) {
82 		w = write(fd, b, l);
83 		if (w > 0) {
84 			l -= w;
85 			b += w;
86 		} else if (errno != EINTR) {
87 			l = 0;
88 		}
89 	}
90 	return (size_t)(b - (const char *)vb);
91 }
92 
93 
94 #if defined(HAVE_DROPROOT)
95 extern int set_user_group_ids(void);
96 #endif
97 
98 /* === functions === */
99 /*
100  * exit_worker()
101  *
102  * On some systems _exit() is preferred to exit() for forked children.
103  * For example, http://netbsd.gw.com/cgi-bin/man-cgi?fork++NetBSD-5.0
104  * recommends _exit() to avoid double-flushing C runtime stream buffers
105  * and also to avoid calling the parent's atexit() routines in the
106  * child.  On those systems WORKER_CHILD_EXIT is _exit.  Since _exit
107  * bypasses CRT cleanup, fflush() files we know might have output
108  * buffered.
109  */
110 void
exit_worker(int exitcode)111 exit_worker(
112 	int	exitcode
113 	)
114 {
115 	if (syslog_file != NULL)
116 		fflush(syslog_file);
117 	fflush(stdout);
118 	fflush(stderr);
119 	WORKER_CHILD_EXIT (exitcode);	/* space before ( required */
120 }
121 
122 
123 static RETSIGTYPE
worker_sighup(int sig)124 worker_sighup(
125 	int sig
126 	)
127 {
128 	if (SIGHUP == sig)
129 		worker_sighup_received = 1;
130 }
131 
132 
133 int
worker_sleep(blocking_child * c,time_t seconds)134 worker_sleep(
135 	blocking_child *	c,
136 	time_t			seconds
137 	)
138 {
139 	u_int sleep_remain;
140 
141 	sleep_remain = (u_int)seconds;
142 	do {
143 		if (!worker_sighup_received)
144 			sleep_remain = sleep(sleep_remain);
145 		if (worker_sighup_received) {
146 			TRACE(1, ("worker SIGHUP with %us left to sleep",
147 				  sleep_remain));
148 			worker_sighup_received = 0;
149 			return -1;
150 		}
151 	} while (sleep_remain);
152 
153 	return 0;
154 }
155 
156 
157 void
interrupt_worker_sleep(void)158 interrupt_worker_sleep(void)
159 {
160 	u_int			idx;
161 	blocking_child *	c;
162 	int			rc;
163 
164 	for (idx = 0; idx < blocking_children_alloc; idx++) {
165 		c = blocking_children[idx];
166 
167 		if (NULL == c || c->reusable == TRUE)
168 			continue;
169 
170 		rc = kill(c->pid, SIGHUP);
171 		if (rc < 0)
172 			msyslog(LOG_ERR,
173 				"Unable to signal HUP to wake child pid %d: %m",
174 				c->pid);
175 	}
176 }
177 
178 
179 /*
180  * harvest_child_status() runs in the parent.
181  *
182  * Note the error handling -- this is an interaction with SIGCHLD.
183  * SIG_IGN on SIGCHLD on some OSes means do not wait but reap
184  * automatically. Since we're not really interested in the result code,
185  * we simply ignore the error.
186  */
187 static void
harvest_child_status(blocking_child * c)188 harvest_child_status(
189 	blocking_child *	c
190 	)
191 {
192 	if (c->pid) {
193 		/* Wait on the child so it can finish terminating */
194 		if (waitpid(c->pid, NULL, 0) == c->pid)
195 			TRACE(4, ("harvested child %d\n", c->pid));
196 		else if (errno != ECHILD)
197 			msyslog(LOG_ERR, "error waiting on child %d: %m", c->pid);
198 		c->pid = 0;
199 	}
200 }
201 
202 /*
203  * req_child_exit() runs in the parent.
204  */
205 int
req_child_exit(blocking_child * c)206 req_child_exit(
207 	blocking_child *	c
208 	)
209 {
210 	if (-1 != c->req_write_pipe) {
211 		close(c->req_write_pipe);
212 		c->req_write_pipe = -1;
213 		return 0;
214 	}
215 	/* Closing the pipe forces the child to exit */
216 	harvest_child_status(c);
217 	return -1;
218 }
219 
220 
221 /*
222  * cleanup_after_child() runs in parent.
223  */
224 static void
cleanup_after_child(blocking_child * c)225 cleanup_after_child(
226 	blocking_child *	c
227 	)
228 {
229 	harvest_child_status(c);
230 	if (-1 != c->resp_read_pipe) {
231 		(*addremove_io_fd)(c->resp_read_pipe, c->ispipe, TRUE);
232 		close(c->resp_read_pipe);
233 		c->resp_read_pipe = -1;
234 	}
235 	c->resp_read_ctx = NULL;
236 	DEBUG_INSIST(-1 == c->req_read_pipe);
237 	DEBUG_INSIST(-1 == c->resp_write_pipe);
238 	c->reusable = TRUE;
239 }
240 
241 
242 static void
send_worker_home_atexit(void)243 send_worker_home_atexit(void)
244 {
245 	u_int			idx;
246 	blocking_child *	c;
247 
248 	if (worker_process)
249 		return;
250 
251 	for (idx = 0; idx < blocking_children_alloc; idx++) {
252 		c = blocking_children[idx];
253 		if (NULL == c)
254 			continue;
255 		req_child_exit(c);
256 	}
257 }
258 
259 
260 int
send_blocking_req_internal(blocking_child * c,blocking_pipe_header * hdr,void * data)261 send_blocking_req_internal(
262 	blocking_child *	c,
263 	blocking_pipe_header *	hdr,
264 	void *			data
265 	)
266 {
267 	size_t	octets;
268 	size_t	rc;
269 
270 	DEBUG_REQUIRE(hdr != NULL);
271 	DEBUG_REQUIRE(data != NULL);
272 	DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == hdr->magic_sig);
273 
274 	if (-1 == c->req_write_pipe) {
275 		fork_blocking_child(c);
276 		DEBUG_INSIST(-1 != c->req_write_pipe);
277 	}
278 
279 	octets = sizeof(*hdr);
280 	rc = netwrite(c->req_write_pipe, hdr, octets);
281 
282 	if (rc == octets) {
283 		octets = hdr->octets - sizeof(*hdr);
284 		rc = netwrite(c->req_write_pipe, data, octets);
285 		if (rc == octets)
286 			return 0;
287 	}
288 
289 	msyslog(LOG_ERR,
290 		"send_blocking_req_internal: short write (%zu of %zu), %m",
291 		rc, octets);
292 
293 	/* Fatal error.  Clean up the child process.  */
294 	req_child_exit(c);
295 	exit(1);	/* otherwise would be return -1 */
296 }
297 
298 
299 blocking_pipe_header *
receive_blocking_req_internal(blocking_child * c)300 receive_blocking_req_internal(
301 	blocking_child *	c
302 	)
303 {
304 	blocking_pipe_header	hdr;
305 	blocking_pipe_header *	req;
306 	size_t			rc;
307 	size_t			octets;
308 
309 	DEBUG_REQUIRE(-1 != c->req_read_pipe);
310 
311 	req = NULL;
312 	rc = netread(c->req_read_pipe, &hdr, sizeof(hdr));
313 
314 	if (0 == rc) {
315 		TRACE(4, ("parent closed request pipe, child %d terminating\n",
316 			  c->pid));
317 	} else if (rc != sizeof(hdr)) {
318 		msyslog(LOG_ERR,
319 			"receive_blocking_req_internal: short header read (%zu of %zu), %m",
320 			rc, sizeof(hdr));
321 	} else {
322 		INSIST(sizeof(hdr) < hdr.octets && hdr.octets < 4 * 1024);
323 		req = emalloc(hdr.octets);
324 		memcpy(req, &hdr, sizeof(*req));
325 		octets = hdr.octets - sizeof(hdr);
326 		rc = netread(c->req_read_pipe, (char *)(req + 1),
327 			     octets);
328 
329 		if (rc != octets)
330 			msyslog(LOG_ERR,
331 				"receive_blocking_req_internal: short read (%zu of %zu), %m",
332 				rc, octets);
333 		else if (BLOCKING_REQ_MAGIC != req->magic_sig)
334 			msyslog(LOG_ERR,
335 				"receive_blocking_req_internal: packet header mismatch (0x%x)",
336 				req->magic_sig);
337 		else
338 			return req;
339 	}
340 
341 	if (req != NULL)
342 		free(req);
343 
344 	return NULL;
345 }
346 
347 
348 int
send_blocking_resp_internal(blocking_child * c,blocking_pipe_header * resp)349 send_blocking_resp_internal(
350 	blocking_child *	c,
351 	blocking_pipe_header *	resp
352 	)
353 {
354 	size_t	octets;
355 	size_t	rc;
356 
357 	DEBUG_REQUIRE(-1 != c->resp_write_pipe);
358 
359 	octets = resp->octets;
360 	rc = netwrite(c->resp_write_pipe, resp, octets);
361 	free(resp);
362 
363 	if (octets == rc)
364 		return 0;
365 
366 	TRACE(1, ("send_blocking_resp_internal: short write (%zu of %zu), %m\n",
367 		  rc, octets));
368 	return -1;
369 }
370 
371 
372 blocking_pipe_header *
receive_blocking_resp_internal(blocking_child * c)373 receive_blocking_resp_internal(
374 	blocking_child *	c
375 	)
376 {
377 	blocking_pipe_header	hdr;
378 	blocking_pipe_header *	resp;
379 	size_t			rc;
380 	size_t			octets;
381 
382 	DEBUG_REQUIRE(c->resp_read_pipe != -1);
383 
384 	resp = NULL;
385 	rc = netread(c->resp_read_pipe, &hdr, sizeof(hdr));
386 
387 	if (0 == rc) {
388 		/* this is the normal child exited indication */
389 	} else if (rc != sizeof(hdr)) {
390 		TRACE(1, ("receive_blocking_resp_internal: short header read (%zu of %zu), %m\n",
391 			  rc, sizeof(hdr)));
392 	} else if (BLOCKING_RESP_MAGIC != hdr.magic_sig) {
393 		TRACE(1, ("receive_blocking_resp_internal: header mismatch (0x%x)\n",
394 			  hdr.magic_sig));
395 	} else {
396 		INSIST(sizeof(hdr) < hdr.octets &&
397 		       hdr.octets < 16 * 1024);
398 		resp = emalloc(hdr.octets);
399 		memcpy(resp, &hdr, sizeof(*resp));
400 		octets = hdr.octets - sizeof(hdr);
401 		rc = netread(c->resp_read_pipe, (char *)(resp + 1),
402 			     octets);
403 
404 		if (rc != octets)
405 			TRACE(1, ("receive_blocking_resp_internal: short read (%zu of %zu), %m\n",
406 				  rc, octets));
407 		else
408 			return resp;
409 	}
410 
411 	cleanup_after_child(c);
412 
413 	if (resp != NULL)
414 		free(resp);
415 
416 	return NULL;
417 }
418 
419 
420 #if defined(HAVE_DROPROOT) && defined(WORK_FORK)
421 void
fork_deferred_worker(void)422 fork_deferred_worker(void)
423 {
424 	u_int			idx;
425 	blocking_child *	c;
426 
427 	REQUIRE(droproot && root_dropped);
428 
429 	for (idx = 0; idx < blocking_children_alloc; idx++) {
430 		c = blocking_children[idx];
431 		if (NULL == c)
432 			continue;
433 		if (-1 != c->req_write_pipe && 0 == c->pid)
434 			fork_blocking_child(c);
435 	}
436 }
437 #endif
438 
439 #if HAVE_SETPROCTITLE == 0
440 static void
setproctitle(const char * fmt,...)441 setproctitle(const char *fmt, ...)
442 {
443 	va_list ap;
444 	char b1[128];
445 	int argcc, argvlen, l;
446 
447 	if (saved_argc == 0)
448 		return;
449 
450 	va_start(ap, fmt);
451 	vsnprintf(b1, sizeof(b1), fmt, ap);
452 	va_end(ap);
453 
454 	/* Clear argv */
455 	for (argvlen = 0, argcc = 0; argcc < saved_argc; argcc++) {
456 		l = strlen(saved_argv[argcc]);
457 		argvlen += l + 1;
458 		memset(saved_argv[argcc], 0, l);
459 	}
460 	l = snprintf(saved_argv[0], argvlen, "ntpd: %s", b1);
461 	for (argcc = 1; argcc < saved_argc; argcc++)
462 		saved_argv[argcc] = &saved_argv[0][l];
463 }
464 #endif
465 
466 static void
fork_blocking_child(blocking_child * c)467 fork_blocking_child(
468 	blocking_child *	c
469 	)
470 {
471 	static int	atexit_installed;
472 	static int	blocking_pipes[4] = { -1, -1, -1, -1 };
473 	int		rc;
474 	int		was_pipe;
475 	int		is_pipe;
476 	int		saved_errno = 0;
477 	int		childpid;
478 	int		keep_fd;
479 	int		fd;
480 
481 	/*
482 	 * parent and child communicate via a pair of pipes.
483 	 *
484 	 * 0 child read request
485 	 * 1 parent write request
486 	 * 2 parent read response
487 	 * 3 child write response
488 	 */
489 	if (-1 == c->req_write_pipe) {
490 		rc = pipe_socketpair(&blocking_pipes[0], &was_pipe);
491 		if (0 != rc) {
492 			saved_errno = errno;
493 		} else {
494 			rc = pipe_socketpair(&blocking_pipes[2], &is_pipe);
495 			if (0 != rc) {
496 				saved_errno = errno;
497 				close(blocking_pipes[0]);
498 				close(blocking_pipes[1]);
499 			} else {
500 				INSIST(was_pipe == is_pipe);
501 			}
502 		}
503 		if (0 != rc) {
504 			errno = saved_errno;
505 			msyslog(LOG_ERR, "unable to create worker pipes: %m");
506 			exit(1);
507 		}
508 
509 		/*
510 		 * Move the descriptors the parent will keep open out of the
511 		 * low descriptors preferred by C runtime buffered FILE *.
512 		 */
513 		c->req_write_pipe = move_fd(blocking_pipes[1]);
514 		c->resp_read_pipe = move_fd(blocking_pipes[2]);
515 		/*
516 		 * wake any worker child on orderly shutdown of the
517 		 * daemon so that it can notice the broken pipes and
518 		 * go away promptly.
519 		 */
520 		if (!atexit_installed) {
521 			atexit(&send_worker_home_atexit);
522 			atexit_installed = TRUE;
523 		}
524 	}
525 
526 #if defined(HAVE_DROPROOT) && !defined(NEED_EARLY_FORK)
527 	/* defer the fork until after root is dropped */
528 	if (droproot && !root_dropped)
529 		return;
530 #endif
531 	if (syslog_file != NULL)
532 		fflush(syslog_file);
533 	fflush(stdout);
534 	fflush(stderr);
535 
536 	/* [BUG 3050] setting SIGCHLD to SIG_IGN likely causes unwanted
537 	 * or undefined effects. We don't do it and leave SIGCHLD alone.
538 	 */
539 	/* signal_no_reset(SIGCHLD, SIG_IGN); */
540 
541 	childpid = fork();
542 	if (-1 == childpid) {
543 		msyslog(LOG_ERR, "unable to fork worker: %m");
544 		exit(1);
545 	}
546 
547 	if (childpid) {
548 		/* this is the parent */
549 		TRACE(1, ("forked worker child (pid %d)\n", childpid));
550 		c->pid = childpid;
551 		c->ispipe = is_pipe;
552 
553 		/* close the child's pipe descriptors. */
554 		close(blocking_pipes[0]);
555 		close(blocking_pipes[3]);
556 
557 		memset(blocking_pipes, -1, sizeof(blocking_pipes));
558 
559 		/* wire into I/O loop */
560 		(*addremove_io_fd)(c->resp_read_pipe, is_pipe, FALSE);
561 
562 		/* wait until child is done */
563 		rc = netread(c->resp_read_pipe, &rc, sizeof(rc));
564 
565 		return;		/* parent returns */
566 	}
567 
568 	/*
569 	 * The parent gets the child pid as the return value of fork().
570 	 * The child must work for it.
571 	 */
572 	c->pid = getpid();
573 	worker_process = TRUE;
574 
575 	/*
576 	 * Change the process name of the child to avoid confusion
577 	 * about ntpd trunning twice.
578 	 */
579 	setproctitle("asynchronous dns resolver");
580 
581 	/*
582 	 * In the child, close all files except stdin, stdout, stderr,
583 	 * and the two child ends of the pipes.
584 	 */
585 	DEBUG_INSIST(-1 == c->req_read_pipe);
586 	DEBUG_INSIST(-1 == c->resp_write_pipe);
587 	c->req_read_pipe = blocking_pipes[0];
588 	c->resp_write_pipe = blocking_pipes[3];
589 
590 	kill_asyncio(0);
591 
592 	/* Tell parent we are ready */
593 	rc = netwrite(c->resp_write_pipe, &rc, sizeof(rc));
594 
595 	closelog();
596 	if (syslog_file != NULL) {
597 		fclose(syslog_file);
598 		syslog_file = NULL;
599 		syslogit = TRUE;
600 	}
601 	keep_fd = max(c->req_read_pipe, c->resp_write_pipe);
602 	for (fd = 3; fd < keep_fd; fd++)
603 		if (fd != c->req_read_pipe &&
604 		    fd != c->resp_write_pipe)
605 			close(fd);
606 	close_all_beyond(keep_fd);
607 	/*
608 	 * We get signals from refclock serial I/O on NetBSD in the
609 	 * worker if we do not reset SIGIO's handler to the default.
610 	 * It is not conditionalized for NetBSD alone because on
611 	 * systems where it is not needed, it is harmless, and that
612 	 * allows us to handle unknown others with NetBSD behavior.
613 	 * [Bug 1386]
614 	 */
615 #if defined(USE_SIGIO)
616 	signal_no_reset(SIGIO, SIG_DFL);
617 #elif defined(USE_SIGPOLL)
618 	signal_no_reset(SIGPOLL, SIG_DFL);
619 #endif
620 	signal_no_reset(SIGHUP, worker_sighup);
621 	init_logging("ntp_intres", 0, FALSE);
622 	setup_logfile(NULL);
623 
624 #ifdef HAVE_DROPROOT
625 	(void) set_user_group_ids();
626 #endif
627 
628 	/*
629 	 * And now back to the portable code
630 	 */
631 	exit_worker(blocking_child_common(c));
632 }
633 
634 
worker_global_lock(int inOrOut)635 void worker_global_lock(int inOrOut)
636 {
637 	(void)inOrOut;
638 }
639 
640 #else	/* !WORK_FORK follows */
641 char work_fork_nonempty_compilation_unit;
642 #endif
643