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