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