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