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