xref: /freebsd/contrib/ntp/libntp/work_fork.c (revision 076ad2f8)
1 /*
2  * work_fork.c - fork implementation for blocking worker child.
3  */
4 #include <config.h>
5 #include "ntp_workimpl.h"
6 
7 #ifdef WORK_FORK
8 #include <stdio.h>
9 #include <ctype.h>
10 #include <signal.h>
11 #include <sys/wait.h>
12 
13 #include "iosignal.h"
14 #include "ntp_stdlib.h"
15 #include "ntp_malloc.h"
16 #include "ntp_syslog.h"
17 #include "ntpd.h"
18 #include "ntp_io.h"
19 #include "ntp_assert.h"
20 #include "ntp_unixtime.h"
21 #include "ntp_worker.h"
22 
23 /* === variables === */
24 	int			worker_process;
25 	addremove_io_fd_func	addremove_io_fd;
26 static	volatile int		worker_sighup_received;
27 
28 /* === function prototypes === */
29 static	void		fork_blocking_child(blocking_child *);
30 static	RETSIGTYPE	worker_sighup(int);
31 static	void		send_worker_home_atexit(void);
32 static	void		cleanup_after_child(blocking_child *);
33 
34 /* === functions === */
35 /*
36  * exit_worker()
37  *
38  * On some systems _exit() is preferred to exit() for forked children.
39  * For example, http://netbsd.gw.com/cgi-bin/man-cgi?fork++NetBSD-5.0
40  * recommends _exit() to avoid double-flushing C runtime stream buffers
41  * and also to avoid calling the parent's atexit() routines in the
42  * child.  On those systems WORKER_CHILD_EXIT is _exit.  Since _exit
43  * bypasses CRT cleanup, fflush() files we know might have output
44  * buffered.
45  */
46 void
47 exit_worker(
48 	int	exitcode
49 	)
50 {
51 	if (syslog_file != NULL)
52 		fflush(syslog_file);
53 	fflush(stdout);
54 	fflush(stderr);
55 	WORKER_CHILD_EXIT (exitcode);	/* space before ( required */
56 }
57 
58 
59 static RETSIGTYPE
60 worker_sighup(
61 	int sig
62 	)
63 {
64 	if (SIGHUP == sig)
65 		worker_sighup_received = 1;
66 }
67 
68 
69 int
70 worker_sleep(
71 	blocking_child *	c,
72 	time_t			seconds
73 	)
74 {
75 	u_int sleep_remain;
76 
77 	sleep_remain = (u_int)seconds;
78 	do {
79 		if (!worker_sighup_received)
80 			sleep_remain = sleep(sleep_remain);
81 		if (worker_sighup_received) {
82 			TRACE(1, ("worker SIGHUP with %us left to sleep",
83 				  sleep_remain));
84 			worker_sighup_received = 0;
85 			return -1;
86 		}
87 	} while (sleep_remain);
88 
89 	return 0;
90 }
91 
92 
93 void
94 interrupt_worker_sleep(void)
95 {
96 	u_int			idx;
97 	blocking_child *	c;
98 	int			rc;
99 
100 	for (idx = 0; idx < blocking_children_alloc; idx++) {
101 		c = blocking_children[idx];
102 
103 		if (NULL == c || c->reusable == TRUE)
104 			continue;
105 
106 		rc = kill(c->pid, SIGHUP);
107 		if (rc < 0)
108 			msyslog(LOG_ERR,
109 				"Unable to signal HUP to wake child pid %d: %m",
110 				c->pid);
111 	}
112 }
113 
114 
115 /*
116  * harvest_child_status() runs in the parent.
117  *
118  * Note the error handling -- this is an interaction with SIGCHLD.
119  * SIG_IGN on SIGCHLD on some OSes means do not wait but reap
120  * automatically. Since we're not really interested in the result code,
121  * we simply ignore the error.
122  */
123 static void
124 harvest_child_status(
125 	blocking_child *	c
126 	)
127 {
128 	if (c->pid) {
129 		/* Wait on the child so it can finish terminating */
130 		if (waitpid(c->pid, NULL, 0) == c->pid)
131 			TRACE(4, ("harvested child %d\n", c->pid));
132 		else if (errno != ECHILD)
133 			msyslog(LOG_ERR, "error waiting on child %d: %m", c->pid);
134 		c->pid = 0;
135 	}
136 }
137 
138 /*
139  * req_child_exit() runs in the parent.
140  */
141 int
142 req_child_exit(
143 	blocking_child *	c
144 	)
145 {
146 	if (-1 != c->req_write_pipe) {
147 		close(c->req_write_pipe);
148 		c->req_write_pipe = -1;
149 		return 0;
150 	}
151 	/* Closing the pipe forces the child to exit */
152 	harvest_child_status(c);
153 	return -1;
154 }
155 
156 
157 /*
158  * cleanup_after_child() runs in parent.
159  */
160 static void
161 cleanup_after_child(
162 	blocking_child *	c
163 	)
164 {
165 	harvest_child_status(c);
166 	if (-1 != c->resp_read_pipe) {
167 		(*addremove_io_fd)(c->resp_read_pipe, c->ispipe, TRUE);
168 		close(c->resp_read_pipe);
169 		c->resp_read_pipe = -1;
170 	}
171 	c->resp_read_ctx = NULL;
172 	DEBUG_INSIST(-1 == c->req_read_pipe);
173 	DEBUG_INSIST(-1 == c->resp_write_pipe);
174 	c->reusable = TRUE;
175 }
176 
177 
178 static void
179 send_worker_home_atexit(void)
180 {
181 	u_int			idx;
182 	blocking_child *	c;
183 
184 	if (worker_process)
185 		return;
186 
187 	for (idx = 0; idx < blocking_children_alloc; idx++) {
188 		c = blocking_children[idx];
189 		if (NULL == c)
190 			continue;
191 		req_child_exit(c);
192 	}
193 }
194 
195 
196 int
197 send_blocking_req_internal(
198 	blocking_child *	c,
199 	blocking_pipe_header *	hdr,
200 	void *			data
201 	)
202 {
203 	int octets;
204 	int rc;
205 
206 	DEBUG_REQUIRE(hdr != NULL);
207 	DEBUG_REQUIRE(data != NULL);
208 	DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == hdr->magic_sig);
209 
210 	if (-1 == c->req_write_pipe) {
211 		fork_blocking_child(c);
212 		DEBUG_INSIST(-1 != c->req_write_pipe);
213 	}
214 
215 	octets = sizeof(*hdr);
216 	rc = write(c->req_write_pipe, hdr, octets);
217 
218 	if (rc == octets) {
219 		octets = hdr->octets - sizeof(*hdr);
220 		rc = write(c->req_write_pipe, data, octets);
221 
222 		if (rc == octets)
223 			return 0;
224 	}
225 
226 	if (rc < 0)
227 		msyslog(LOG_ERR,
228 			"send_blocking_req_internal: pipe write: %m");
229 	else
230 		msyslog(LOG_ERR,
231 			"send_blocking_req_internal: short write %d of %d",
232 			rc, octets);
233 
234 	/* Fatal error.  Clean up the child process.  */
235 	req_child_exit(c);
236 	exit(1);	/* otherwise would be return -1 */
237 }
238 
239 
240 blocking_pipe_header *
241 receive_blocking_req_internal(
242 	blocking_child *	c
243 	)
244 {
245 	blocking_pipe_header	hdr;
246 	blocking_pipe_header *	req;
247 	int			rc;
248 	long			octets;
249 
250 	DEBUG_REQUIRE(-1 != c->req_read_pipe);
251 
252 	req = NULL;
253 
254 	do {
255 		rc = read(c->req_read_pipe, &hdr, sizeof(hdr));
256 	} while (rc < 0 && EINTR == errno);
257 
258 	if (rc < 0) {
259 		msyslog(LOG_ERR,
260 			"receive_blocking_req_internal: pipe read %m");
261 	} else if (0 == rc) {
262 		TRACE(4, ("parent closed request pipe, child %d terminating\n",
263 			  c->pid));
264 	} else if (rc != sizeof(hdr)) {
265 		msyslog(LOG_ERR,
266 			"receive_blocking_req_internal: short header read %d of %lu",
267 			rc, (u_long)sizeof(hdr));
268 	} else {
269 		INSIST(sizeof(hdr) < hdr.octets && hdr.octets < 4 * 1024);
270 		req = emalloc(hdr.octets);
271 		memcpy(req, &hdr, sizeof(*req));
272 		octets = hdr.octets - sizeof(hdr);
273 		rc = read(c->req_read_pipe, (char *)req + sizeof(*req),
274 			  octets);
275 
276 		if (rc < 0)
277 			msyslog(LOG_ERR,
278 				"receive_blocking_req_internal: pipe data read %m");
279 		else if (rc != octets)
280 			msyslog(LOG_ERR,
281 				"receive_blocking_req_internal: short read %d of %ld",
282 				rc, octets);
283 		else if (BLOCKING_REQ_MAGIC != req->magic_sig)
284 			msyslog(LOG_ERR,
285 				"receive_blocking_req_internal: packet header mismatch (0x%x)",
286 				req->magic_sig);
287 		else
288 			return req;
289 	}
290 
291 	if (req != NULL)
292 		free(req);
293 
294 	return NULL;
295 }
296 
297 
298 int
299 send_blocking_resp_internal(
300 	blocking_child *	c,
301 	blocking_pipe_header *	resp
302 	)
303 {
304 	long	octets;
305 	int	rc;
306 
307 	DEBUG_REQUIRE(-1 != c->resp_write_pipe);
308 
309 	octets = resp->octets;
310 	rc = write(c->resp_write_pipe, resp, octets);
311 	free(resp);
312 
313 	if (octets == rc)
314 		return 0;
315 
316 	if (rc < 0)
317 		TRACE(1, ("send_blocking_resp_internal: pipe write %m\n"));
318 	else
319 		TRACE(1, ("send_blocking_resp_internal: short write %d of %ld\n",
320 			  rc, octets));
321 
322 	return -1;
323 }
324 
325 
326 blocking_pipe_header *
327 receive_blocking_resp_internal(
328 	blocking_child *	c
329 	)
330 {
331 	blocking_pipe_header	hdr;
332 	blocking_pipe_header *	resp;
333 	int			rc;
334 	long			octets;
335 
336 	DEBUG_REQUIRE(c->resp_read_pipe != -1);
337 
338 	resp = NULL;
339 	rc = read(c->resp_read_pipe, &hdr, sizeof(hdr));
340 
341 	if (rc < 0) {
342 		TRACE(1, ("receive_blocking_resp_internal: pipe read %m\n"));
343 	} else if (0 == rc) {
344 		/* this is the normal child exited indication */
345 	} else if (rc != sizeof(hdr)) {
346 		TRACE(1, ("receive_blocking_resp_internal: short header read %d of %lu\n",
347 			  rc, (u_long)sizeof(hdr)));
348 	} else if (BLOCKING_RESP_MAGIC != hdr.magic_sig) {
349 		TRACE(1, ("receive_blocking_resp_internal: header mismatch (0x%x)\n",
350 			  hdr.magic_sig));
351 	} else {
352 		INSIST(sizeof(hdr) < hdr.octets &&
353 		       hdr.octets < 16 * 1024);
354 		resp = emalloc(hdr.octets);
355 		memcpy(resp, &hdr, sizeof(*resp));
356 		octets = hdr.octets - sizeof(hdr);
357 		rc = read(c->resp_read_pipe,
358 			  (char *)resp + sizeof(*resp),
359 			  octets);
360 
361 		if (rc < 0)
362 			TRACE(1, ("receive_blocking_resp_internal: pipe data read %m\n"));
363 		else if (rc < octets)
364 			TRACE(1, ("receive_blocking_resp_internal: short read %d of %ld\n",
365 				  rc, octets));
366 		else
367 			return resp;
368 	}
369 
370 	cleanup_after_child(c);
371 
372 	if (resp != NULL)
373 		free(resp);
374 
375 	return NULL;
376 }
377 
378 
379 #if defined(HAVE_DROPROOT) && defined(WORK_FORK)
380 void
381 fork_deferred_worker(void)
382 {
383 	u_int			idx;
384 	blocking_child *	c;
385 
386 	REQUIRE(droproot && root_dropped);
387 
388 	for (idx = 0; idx < blocking_children_alloc; idx++) {
389 		c = blocking_children[idx];
390 		if (NULL == c)
391 			continue;
392 		if (-1 != c->req_write_pipe && 0 == c->pid)
393 			fork_blocking_child(c);
394 	}
395 }
396 #endif
397 
398 
399 static void
400 fork_blocking_child(
401 	blocking_child *	c
402 	)
403 {
404 	static int	atexit_installed;
405 	static int	blocking_pipes[4] = { -1, -1, -1, -1 };
406 	int		rc;
407 	int		was_pipe;
408 	int		is_pipe;
409 	int		saved_errno = 0;
410 	int		childpid;
411 	int		keep_fd;
412 	int		fd;
413 
414 	/*
415 	 * parent and child communicate via a pair of pipes.
416 	 *
417 	 * 0 child read request
418 	 * 1 parent write request
419 	 * 2 parent read response
420 	 * 3 child write response
421 	 */
422 	if (-1 == c->req_write_pipe) {
423 		rc = pipe_socketpair(&blocking_pipes[0], &was_pipe);
424 		if (0 != rc) {
425 			saved_errno = errno;
426 		} else {
427 			rc = pipe_socketpair(&blocking_pipes[2], &is_pipe);
428 			if (0 != rc) {
429 				saved_errno = errno;
430 				close(blocking_pipes[0]);
431 				close(blocking_pipes[1]);
432 			} else {
433 				INSIST(was_pipe == is_pipe);
434 			}
435 		}
436 		if (0 != rc) {
437 			errno = saved_errno;
438 			msyslog(LOG_ERR, "unable to create worker pipes: %m");
439 			exit(1);
440 		}
441 
442 		/*
443 		 * Move the descriptors the parent will keep open out of the
444 		 * low descriptors preferred by C runtime buffered FILE *.
445 		 */
446 		c->req_write_pipe = move_fd(blocking_pipes[1]);
447 		c->resp_read_pipe = move_fd(blocking_pipes[2]);
448 		/*
449 		 * wake any worker child on orderly shutdown of the
450 		 * daemon so that it can notice the broken pipes and
451 		 * go away promptly.
452 		 */
453 		if (!atexit_installed) {
454 			atexit(&send_worker_home_atexit);
455 			atexit_installed = TRUE;
456 		}
457 	}
458 
459 #if defined(HAVE_DROPROOT) && !defined(NEED_EARLY_FORK)
460 	/* defer the fork until after root is dropped */
461 	if (droproot && !root_dropped)
462 		return;
463 #endif
464 	if (syslog_file != NULL)
465 		fflush(syslog_file);
466 	fflush(stdout);
467 	fflush(stderr);
468 
469 	/* [BUG 3050] setting SIGCHLD to SIG_IGN likely causes unwanted
470 	 * or undefined effects. We don't do it and leave SIGCHLD alone.
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 void worker_global_lock(int inOrOut)
552 {
553 	(void)inOrOut;
554 }
555 
556 #else	/* !WORK_FORK follows */
557 char work_fork_nonempty_compilation_unit;
558 #endif
559