work_fork.c revision 285612
1289694Sngie/*
2289694Sngie * work_fork.c - fork implementation for blocking worker child.
3289694Sngie */
4289694Sngie#include <config.h>
5289694Sngie#include "ntp_workimpl.h"
6289694Sngie
7289694Sngie#ifdef WORK_FORK
8289694Sngie#include <stdio.h>
9289694Sngie#include <ctype.h>
10289694Sngie#include <signal.h>
11289694Sngie#include <sys/wait.h>
12289694Sngie
13289694Sngie#include "iosignal.h"
14289694Sngie#include "ntp_stdlib.h"
15289694Sngie#include "ntp_malloc.h"
16289694Sngie#include "ntp_syslog.h"
17289694Sngie#include "ntpd.h"
18289694Sngie#include "ntp_io.h"
19289694Sngie#include "ntp_assert.h"
20289694Sngie#include "ntp_unixtime.h"
21289694Sngie#include "ntp_worker.h"
22289694Sngie
23289694Sngie/* === variables === */
24289694Sngie	int			worker_process;
25289694Sngie	addremove_io_fd_func	addremove_io_fd;
26289694Sngiestatic	volatile int		worker_sighup_received;
27289694Sngie
28289694Sngie/* === function prototypes === */
29289694Sngiestatic	void		fork_blocking_child(blocking_child *);
30290586Sngiestatic	RETSIGTYPE	worker_sighup(int);
31290586Sngiestatic	void		send_worker_home_atexit(void);
32290594Sngiestatic	void		cleanup_after_child(blocking_child *);
33290594Sngie
34290594Sngie/* === functions === */
35290594Sngie/*
36290594Sngie * exit_worker()
37290594Sngie *
38290594Sngie * On some systems _exit() is preferred to exit() for forked children.
39290586Sngie * For example, http://netbsd.gw.com/cgi-bin/man-cgi?fork++NetBSD-5.0
40290586Sngie * recommends _exit() to avoid double-flushing C runtime stream buffers
41290586Sngie * and also to avoid calling the parent's atexit() routines in the
42290586Sngie * child.  On those systems WORKER_CHILD_EXIT is _exit.  Since _exit
43290586Sngie * bypasses CRT cleanup, fflush() files we know might have output
44289694Sngie * buffered.
45290586Sngie */
46289694Sngievoid
47290586Sngieexit_worker(
48290586Sngie	int	exitcode
49290594Sngie	)
50289694Sngie{
51290586Sngie	if (syslog_file != NULL)
52290586Sngie		fflush(syslog_file);
53290586Sngie	fflush(stdout);
54290586Sngie	fflush(stderr);
55290586Sngie	WORKER_CHILD_EXIT (exitcode);	/* space before ( required */
56290586Sngie}
57290586Sngie
58290586Sngie
59290586Sngiestatic RETSIGTYPE
60290586Sngieworker_sighup(
61290586Sngie	int sig
62290586Sngie	)
63290586Sngie{
64290586Sngie	if (SIGHUP == sig)
65290586Sngie		worker_sighup_received = 1;
66290586Sngie}
67290586Sngie
68290586Sngie
69290586Sngieint
70290586Sngieworker_sleep(
71289694Sngie	blocking_child *	c,
72290586Sngie	time_t			seconds
73290586Sngie	)
74290586Sngie{
75290586Sngie	u_int sleep_remain;
76289694Sngie
77290586Sngie	sleep_remain = (u_int)seconds;
78290586Sngie	do {
79290586Sngie		if (!worker_sighup_received)
80290586Sngie			sleep_remain = sleep(sleep_remain);
81290586Sngie		if (worker_sighup_received) {
82290586Sngie			TRACE(1, ("worker SIGHUP with %us left to sleep",
83289694Sngie				  sleep_remain));
84290586Sngie			worker_sighup_received = 0;
85290586Sngie			return -1;
86290586Sngie		}
87290586Sngie	} while (sleep_remain);
88290586Sngie
89290586Sngie	return 0;
90290586Sngie}
91290586Sngie
92290586Sngie
93290586Sngievoid
94290586Sngieinterrupt_worker_sleep(void)
95289694Sngie{
96289694Sngie	u_int			idx;
97289694Sngie	blocking_child *	c;
98289694Sngie	int			rc;
99289694Sngie
100289694Sngie	for (idx = 0; idx < blocking_children_alloc; idx++) {
101290586Sngie		c = blocking_children[idx];
102289694Sngie
103290586Sngie		if (NULL == c || c->reusable == TRUE)
104290586Sngie			continue;
105289694Sngie
106289694Sngie		rc = kill(c->pid, SIGHUP);
107289694Sngie		if (rc < 0)
108289694Sngie			msyslog(LOG_ERR,
109289694Sngie				"Unable to signal HUP to wake child pid %d: %m",
110289694Sngie				c->pid);
111290586Sngie	}
112290586Sngie}
113290586Sngie
114289694Sngie
115289694Sngie/*
116289694Sngie * harvest_child_status() runs in the parent.
117289694Sngie */
118289694Sngiestatic void
119289694Sngieharvest_child_status(
120289694Sngie	blocking_child *	c
121289694Sngie	)
122289694Sngie{
123289694Sngie	if (c->pid)
124289694Sngie	{
125289694Sngie		/* Wait on the child so it can finish terminating */
126289694Sngie		if (waitpid(c->pid, NULL, 0) == c->pid)
127289694Sngie			TRACE(4, ("harvested child %d\n", c->pid));
128289694Sngie		else msyslog(LOG_ERR, "error waiting on child %d: %m", c->pid);
129289694Sngie	}
130289694Sngie}
131289694Sngie
132289694Sngie/*
133290586Sngie * req_child_exit() runs in the parent.
134290586Sngie */
135290586Sngieint
136290586Sngiereq_child_exit(
137290586Sngie	blocking_child *	c
138290586Sngie	)
139290586Sngie{
140290586Sngie	if (-1 != c->req_write_pipe) {
141290586Sngie		close(c->req_write_pipe);
142290586Sngie		c->req_write_pipe = -1;
143289694Sngie		return 0;
144290586Sngie	}
145290586Sngie	/* Closing the pipe forces the child to exit */
146290586Sngie	harvest_child_status(c);
147290586Sngie	return -1;
148290586Sngie}
149290586Sngie
150290586Sngie
151290586Sngie/*
152290586Sngie * cleanup_after_child() runs in parent.
153 */
154static void
155cleanup_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
173static void
174send_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
191int
192send_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
235blocking_pipe_header *
236receive_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
293int
294send_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
321blocking_pipe_header *
322receive_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)
375void
376fork_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
394static void
395fork_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 */
544char work_fork_nonempty_compilation_unit;
545#endif
546