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