xref: /netbsd-src/external/bsd/ntp/dist/libntp/work_fork.c (revision b65092432b7907137a107da7d1098089432234aa)
1*b6509243Schristos /*	$NetBSD: work_fork.c,v 1.15 2020/10/10 13:41:14 christos Exp $	*/
28585484eSchristos 
38585484eSchristos /*
48585484eSchristos  * work_fork.c - fork implementation for blocking worker child.
58585484eSchristos  */
68585484eSchristos #include <config.h>
78585484eSchristos #include "ntp_workimpl.h"
88585484eSchristos 
98585484eSchristos #ifdef WORK_FORK
108585484eSchristos #include <stdio.h>
118585484eSchristos #include <ctype.h>
128585484eSchristos #include <signal.h>
135d681e99Schristos #include <sys/wait.h>
148585484eSchristos 
158585484eSchristos #include "iosignal.h"
168585484eSchristos #include "ntp_stdlib.h"
178585484eSchristos #include "ntp_malloc.h"
188585484eSchristos #include "ntp_syslog.h"
198585484eSchristos #include "ntpd.h"
208585484eSchristos #include "ntp_io.h"
218585484eSchristos #include "ntp_assert.h"
228585484eSchristos #include "ntp_unixtime.h"
238585484eSchristos #include "ntp_worker.h"
248585484eSchristos 
258585484eSchristos /* === variables === */
268585484eSchristos 	int			worker_process;
278585484eSchristos 	addremove_io_fd_func	addremove_io_fd;
288585484eSchristos static	volatile int		worker_sighup_received;
29ccc794f0Schristos int	saved_argc = 0;
30ccc794f0Schristos char	**saved_argv;
318585484eSchristos 
328585484eSchristos /* === function prototypes === */
338585484eSchristos static	void		fork_blocking_child(blocking_child *);
348585484eSchristos static	RETSIGTYPE	worker_sighup(int);
358585484eSchristos static	void		send_worker_home_atexit(void);
368585484eSchristos static	void		cleanup_after_child(blocking_child *);
378585484eSchristos 
38ccc794f0Schristos /* === I/O helpers === */
39ccc794f0Schristos /* Since we have signals enabled, there's a good chance that blocking IO
40ccc794f0Schristos  * via pipe suffers from EINTR -- and this goes for both directions.
41ccc794f0Schristos  * The next two wrappers will loop until either all the data is written
42ccc794f0Schristos  * or read, plus handling the EOF condition on read. They may return
43ccc794f0Schristos  * zero if no data was transferred at all, and effectively every return
44ccc794f0Schristos  * value that differs from the given transfer length signifies an error
45ccc794f0Schristos  * condition.
46ccc794f0Schristos  */
47ccc794f0Schristos 
48ccc794f0Schristos static size_t
netread(int fd,void * vb,size_t l)49ccc794f0Schristos netread(
50ccc794f0Schristos 	int		fd,
51ccc794f0Schristos 	void *		vb,
52ccc794f0Schristos 	size_t		l
53ccc794f0Schristos 	)
5403cfe0ffSchristos {
5503cfe0ffSchristos 	char *		b = vb;
5603cfe0ffSchristos 	ssize_t		r;
5703cfe0ffSchristos 
58ccc794f0Schristos 	while (l) {
59ccc794f0Schristos 		r = read(fd, b, l);
60ccc794f0Schristos 		if (r > 0) {
6103cfe0ffSchristos 			l -= r;
6203cfe0ffSchristos 			b += r;
63ccc794f0Schristos 		} else if (r == 0 || errno != EINTR) {
64ccc794f0Schristos 			l = 0;
6503cfe0ffSchristos 		}
6603cfe0ffSchristos 	}
67ccc794f0Schristos 	return (size_t)(b - (char *)vb);
68ccc794f0Schristos }
6903cfe0ffSchristos 
7003cfe0ffSchristos 
71ccc794f0Schristos static size_t
netwrite(int fd,const void * vb,size_t l)72ccc794f0Schristos netwrite(
73ccc794f0Schristos 	int		fd,
74ccc794f0Schristos 	const void *	vb,
75ccc794f0Schristos 	size_t		l
76ccc794f0Schristos 	)
7703cfe0ffSchristos {
7803cfe0ffSchristos 	const char *	b = vb;
7903cfe0ffSchristos 	ssize_t		w;
8003cfe0ffSchristos 
81ccc794f0Schristos 	while (l) {
82ccc794f0Schristos 		w = write(fd, b, l);
83ccc794f0Schristos 		if (w > 0) {
8403cfe0ffSchristos 			l -= w;
8503cfe0ffSchristos 			b += w;
86ccc794f0Schristos 		} else if (errno != EINTR) {
87ccc794f0Schristos 			l = 0;
8803cfe0ffSchristos 		}
8903cfe0ffSchristos 	}
90ccc794f0Schristos 	return (size_t)(b - (const char *)vb);
91ccc794f0Schristos }
9203cfe0ffSchristos 
9303cfe0ffSchristos 
94cdfa2a7eSchristos #if defined(HAVE_DROPROOT)
95cdfa2a7eSchristos extern int set_user_group_ids(void);
96cdfa2a7eSchristos #endif
9779045f13Schristos 
988585484eSchristos /* === functions === */
998585484eSchristos /*
1008585484eSchristos  * exit_worker()
1018585484eSchristos  *
1028585484eSchristos  * On some systems _exit() is preferred to exit() for forked children.
1038585484eSchristos  * For example, http://netbsd.gw.com/cgi-bin/man-cgi?fork++NetBSD-5.0
1048585484eSchristos  * recommends _exit() to avoid double-flushing C runtime stream buffers
1058585484eSchristos  * and also to avoid calling the parent's atexit() routines in the
1068585484eSchristos  * child.  On those systems WORKER_CHILD_EXIT is _exit.  Since _exit
1078585484eSchristos  * bypasses CRT cleanup, fflush() files we know might have output
1088585484eSchristos  * buffered.
1098585484eSchristos  */
1108585484eSchristos void
exit_worker(int exitcode)1118585484eSchristos exit_worker(
1128585484eSchristos 	int	exitcode
1138585484eSchristos 	)
1148585484eSchristos {
1158585484eSchristos 	if (syslog_file != NULL)
1168585484eSchristos 		fflush(syslog_file);
1178585484eSchristos 	fflush(stdout);
1188585484eSchristos 	fflush(stderr);
1198585484eSchristos 	WORKER_CHILD_EXIT (exitcode);	/* space before ( required */
1208585484eSchristos }
1218585484eSchristos 
1228585484eSchristos 
1238585484eSchristos static RETSIGTYPE
worker_sighup(int sig)1248585484eSchristos worker_sighup(
1258585484eSchristos 	int sig
1268585484eSchristos 	)
1278585484eSchristos {
1288585484eSchristos 	if (SIGHUP == sig)
1298585484eSchristos 		worker_sighup_received = 1;
1308585484eSchristos }
1318585484eSchristos 
1328585484eSchristos 
1338585484eSchristos int
worker_sleep(blocking_child * c,time_t seconds)1348585484eSchristos worker_sleep(
1358585484eSchristos 	blocking_child *	c,
1368585484eSchristos 	time_t			seconds
1378585484eSchristos 	)
1388585484eSchristos {
1398585484eSchristos 	u_int sleep_remain;
1408585484eSchristos 
1418585484eSchristos 	sleep_remain = (u_int)seconds;
1428585484eSchristos 	do {
1438585484eSchristos 		if (!worker_sighup_received)
1448585484eSchristos 			sleep_remain = sleep(sleep_remain);
1458585484eSchristos 		if (worker_sighup_received) {
1468585484eSchristos 			TRACE(1, ("worker SIGHUP with %us left to sleep",
1478585484eSchristos 				  sleep_remain));
1488585484eSchristos 			worker_sighup_received = 0;
1498585484eSchristos 			return -1;
1508585484eSchristos 		}
1518585484eSchristos 	} while (sleep_remain);
1528585484eSchristos 
1538585484eSchristos 	return 0;
1548585484eSchristos }
1558585484eSchristos 
1568585484eSchristos 
1578585484eSchristos void
interrupt_worker_sleep(void)1588585484eSchristos interrupt_worker_sleep(void)
1598585484eSchristos {
1608585484eSchristos 	u_int			idx;
1618585484eSchristos 	blocking_child *	c;
1628585484eSchristos 	int			rc;
1638585484eSchristos 
1648585484eSchristos 	for (idx = 0; idx < blocking_children_alloc; idx++) {
1658585484eSchristos 		c = blocking_children[idx];
16670e8e53eSkardel 
16770e8e53eSkardel 		if (NULL == c || c->reusable == TRUE)
1688585484eSchristos 			continue;
16970e8e53eSkardel 
1708585484eSchristos 		rc = kill(c->pid, SIGHUP);
1718585484eSchristos 		if (rc < 0)
1728585484eSchristos 			msyslog(LOG_ERR,
1738585484eSchristos 				"Unable to signal HUP to wake child pid %d: %m",
1748585484eSchristos 				c->pid);
1758585484eSchristos 	}
1768585484eSchristos }
1778585484eSchristos 
1788585484eSchristos 
1798585484eSchristos /*
1805d681e99Schristos  * harvest_child_status() runs in the parent.
18103cfe0ffSchristos  *
18203cfe0ffSchristos  * Note the error handling -- this is an interaction with SIGCHLD.
18303cfe0ffSchristos  * SIG_IGN on SIGCHLD on some OSes means do not wait but reap
18403cfe0ffSchristos  * automatically. Since we're not really interested in the result code,
18503cfe0ffSchristos  * we simply ignore the error.
1865d681e99Schristos  */
1875d681e99Schristos static void
harvest_child_status(blocking_child * c)1885d681e99Schristos harvest_child_status(
1895d681e99Schristos 	blocking_child *	c
1905d681e99Schristos 	)
1915d681e99Schristos {
19203cfe0ffSchristos 	if (c->pid) {
1935d681e99Schristos 		/* Wait on the child so it can finish terminating */
1945d681e99Schristos 		if (waitpid(c->pid, NULL, 0) == c->pid)
1955d681e99Schristos 			TRACE(4, ("harvested child %d\n", c->pid));
19603cfe0ffSchristos 		else if (errno != ECHILD)
197aa4b4f79Schristos 			msyslog(LOG_ERR, "error waiting on child %d: %m", c->pid);
198be80d735Schristos 		c->pid = 0;
1995d681e99Schristos 	}
2005d681e99Schristos }
2015d681e99Schristos 
2025d681e99Schristos /*
2038585484eSchristos  * req_child_exit() runs in the parent.
2048585484eSchristos  */
2058585484eSchristos int
req_child_exit(blocking_child * c)2068585484eSchristos req_child_exit(
2078585484eSchristos 	blocking_child *	c
2088585484eSchristos 	)
2098585484eSchristos {
2108585484eSchristos 	if (-1 != c->req_write_pipe) {
2118585484eSchristos 		close(c->req_write_pipe);
2128585484eSchristos 		c->req_write_pipe = -1;
2138585484eSchristos 		return 0;
2148585484eSchristos 	}
2155d681e99Schristos 	/* Closing the pipe forces the child to exit */
2165d681e99Schristos 	harvest_child_status(c);
2178585484eSchristos 	return -1;
2188585484eSchristos }
2198585484eSchristos 
2208585484eSchristos 
2218585484eSchristos /*
2228585484eSchristos  * cleanup_after_child() runs in parent.
2238585484eSchristos  */
2248585484eSchristos static void
cleanup_after_child(blocking_child * c)2258585484eSchristos cleanup_after_child(
2268585484eSchristos 	blocking_child *	c
2278585484eSchristos 	)
2288585484eSchristos {
2295d681e99Schristos 	harvest_child_status(c);
2308585484eSchristos 	if (-1 != c->resp_read_pipe) {
2318585484eSchristos 		(*addremove_io_fd)(c->resp_read_pipe, c->ispipe, TRUE);
2328585484eSchristos 		close(c->resp_read_pipe);
2338585484eSchristos 		c->resp_read_pipe = -1;
2348585484eSchristos 	}
2358585484eSchristos 	c->resp_read_ctx = NULL;
2368585484eSchristos 	DEBUG_INSIST(-1 == c->req_read_pipe);
2378585484eSchristos 	DEBUG_INSIST(-1 == c->resp_write_pipe);
2388585484eSchristos 	c->reusable = TRUE;
2398585484eSchristos }
2408585484eSchristos 
2418585484eSchristos 
2428585484eSchristos static void
send_worker_home_atexit(void)2438585484eSchristos send_worker_home_atexit(void)
2448585484eSchristos {
2458585484eSchristos 	u_int			idx;
2468585484eSchristos 	blocking_child *	c;
2478585484eSchristos 
2488585484eSchristos 	if (worker_process)
2498585484eSchristos 		return;
2508585484eSchristos 
2518585484eSchristos 	for (idx = 0; idx < blocking_children_alloc; idx++) {
2528585484eSchristos 		c = blocking_children[idx];
2538585484eSchristos 		if (NULL == c)
2548585484eSchristos 			continue;
2558585484eSchristos 		req_child_exit(c);
2568585484eSchristos 	}
2578585484eSchristos }
2588585484eSchristos 
2598585484eSchristos 
2608585484eSchristos int
send_blocking_req_internal(blocking_child * c,blocking_pipe_header * hdr,void * data)2618585484eSchristos send_blocking_req_internal(
2628585484eSchristos 	blocking_child *	c,
2638585484eSchristos 	blocking_pipe_header *	hdr,
2648585484eSchristos 	void *			data
2658585484eSchristos 	)
2668585484eSchristos {
267ccc794f0Schristos 	size_t	octets;
268ccc794f0Schristos 	size_t	rc;
2698585484eSchristos 
2708585484eSchristos 	DEBUG_REQUIRE(hdr != NULL);
2718585484eSchristos 	DEBUG_REQUIRE(data != NULL);
2728585484eSchristos 	DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == hdr->magic_sig);
2738585484eSchristos 
2748585484eSchristos 	if (-1 == c->req_write_pipe) {
2758585484eSchristos 		fork_blocking_child(c);
2768585484eSchristos 		DEBUG_INSIST(-1 != c->req_write_pipe);
2778585484eSchristos 	}
2788585484eSchristos 
2798585484eSchristos 	octets = sizeof(*hdr);
28003cfe0ffSchristos 	rc = netwrite(c->req_write_pipe, hdr, octets);
2818585484eSchristos 
2828585484eSchristos 	if (rc == octets) {
2838585484eSchristos 		octets = hdr->octets - sizeof(*hdr);
28403cfe0ffSchristos 		rc = netwrite(c->req_write_pipe, data, octets);
2858585484eSchristos 		if (rc == octets)
2868585484eSchristos 			return 0;
2878585484eSchristos 	}
2888585484eSchristos 
2898585484eSchristos 	msyslog(LOG_ERR,
290ccc794f0Schristos 		"send_blocking_req_internal: short write (%zu of %zu), %m",
2918585484eSchristos 		rc, octets);
2928585484eSchristos 
2935d681e99Schristos 	/* Fatal error.  Clean up the child process.  */
2945d681e99Schristos 	req_child_exit(c);
2958585484eSchristos 	exit(1);	/* otherwise would be return -1 */
2968585484eSchristos }
2978585484eSchristos 
2988585484eSchristos 
2998585484eSchristos blocking_pipe_header *
receive_blocking_req_internal(blocking_child * c)3008585484eSchristos receive_blocking_req_internal(
3018585484eSchristos 	blocking_child *	c
3028585484eSchristos 	)
3038585484eSchristos {
3048585484eSchristos 	blocking_pipe_header	hdr;
3058585484eSchristos 	blocking_pipe_header *	req;
306ccc794f0Schristos 	size_t			rc;
307ccc794f0Schristos 	size_t			octets;
3088585484eSchristos 
3098585484eSchristos 	DEBUG_REQUIRE(-1 != c->req_read_pipe);
3108585484eSchristos 
3118585484eSchristos 	req = NULL;
31203cfe0ffSchristos 	rc = netread(c->req_read_pipe, &hdr, sizeof(hdr));
3138585484eSchristos 
314ccc794f0Schristos 	if (0 == rc) {
3158585484eSchristos 		TRACE(4, ("parent closed request pipe, child %d terminating\n",
3168585484eSchristos 			  c->pid));
3178585484eSchristos 	} else if (rc != sizeof(hdr)) {
3188585484eSchristos 		msyslog(LOG_ERR,
319ccc794f0Schristos 			"receive_blocking_req_internal: short header read (%zu of %zu), %m",
320ccc794f0Schristos 			rc, sizeof(hdr));
3218585484eSchristos 	} else {
3228585484eSchristos 		INSIST(sizeof(hdr) < hdr.octets && hdr.octets < 4 * 1024);
3238585484eSchristos 		req = emalloc(hdr.octets);
3248585484eSchristos 		memcpy(req, &hdr, sizeof(*req));
3258585484eSchristos 		octets = hdr.octets - sizeof(hdr);
326ccc794f0Schristos 		rc = netread(c->req_read_pipe, (char *)(req + 1),
3278585484eSchristos 			     octets);
3288585484eSchristos 
329ccc794f0Schristos 		if (rc != octets)
3308585484eSchristos 			msyslog(LOG_ERR,
331ccc794f0Schristos 				"receive_blocking_req_internal: short read (%zu of %zu), %m",
3328585484eSchristos 				rc, octets);
3338585484eSchristos 		else if (BLOCKING_REQ_MAGIC != req->magic_sig)
3348585484eSchristos 			msyslog(LOG_ERR,
3358585484eSchristos 				"receive_blocking_req_internal: packet header mismatch (0x%x)",
3368585484eSchristos 				req->magic_sig);
3378585484eSchristos 		else
3388585484eSchristos 			return req;
3398585484eSchristos 	}
3408585484eSchristos 
3418585484eSchristos 	if (req != NULL)
3428585484eSchristos 		free(req);
3438585484eSchristos 
3448585484eSchristos 	return NULL;
3458585484eSchristos }
3468585484eSchristos 
3478585484eSchristos 
3488585484eSchristos int
send_blocking_resp_internal(blocking_child * c,blocking_pipe_header * resp)3498585484eSchristos send_blocking_resp_internal(
3508585484eSchristos 	blocking_child *	c,
3518585484eSchristos 	blocking_pipe_header *	resp
3528585484eSchristos 	)
3538585484eSchristos {
354ccc794f0Schristos 	size_t	octets;
355ccc794f0Schristos 	size_t	rc;
3568585484eSchristos 
3578585484eSchristos 	DEBUG_REQUIRE(-1 != c->resp_write_pipe);
3588585484eSchristos 
3598585484eSchristos 	octets = resp->octets;
36003cfe0ffSchristos 	rc = netwrite(c->resp_write_pipe, resp, octets);
3618585484eSchristos 	free(resp);
3628585484eSchristos 
3638585484eSchristos 	if (octets == rc)
3648585484eSchristos 		return 0;
3658585484eSchristos 
366ccc794f0Schristos 	TRACE(1, ("send_blocking_resp_internal: short write (%zu of %zu), %m\n",
3678585484eSchristos 		  rc, octets));
3688585484eSchristos 	return -1;
3698585484eSchristos }
3708585484eSchristos 
3718585484eSchristos 
3728585484eSchristos blocking_pipe_header *
receive_blocking_resp_internal(blocking_child * c)3738585484eSchristos receive_blocking_resp_internal(
3748585484eSchristos 	blocking_child *	c
3758585484eSchristos 	)
3768585484eSchristos {
3778585484eSchristos 	blocking_pipe_header	hdr;
3788585484eSchristos 	blocking_pipe_header *	resp;
379ccc794f0Schristos 	size_t			rc;
380ccc794f0Schristos 	size_t			octets;
3818585484eSchristos 
3828585484eSchristos 	DEBUG_REQUIRE(c->resp_read_pipe != -1);
3838585484eSchristos 
3848585484eSchristos 	resp = NULL;
38503cfe0ffSchristos 	rc = netread(c->resp_read_pipe, &hdr, sizeof(hdr));
3868585484eSchristos 
387ccc794f0Schristos 	if (0 == rc) {
3888585484eSchristos 		/* this is the normal child exited indication */
3898585484eSchristos 	} else if (rc != sizeof(hdr)) {
390ccc794f0Schristos 		TRACE(1, ("receive_blocking_resp_internal: short header read (%zu of %zu), %m\n",
391ccc794f0Schristos 			  rc, sizeof(hdr)));
3928585484eSchristos 	} else if (BLOCKING_RESP_MAGIC != hdr.magic_sig) {
3938585484eSchristos 		TRACE(1, ("receive_blocking_resp_internal: header mismatch (0x%x)\n",
3948585484eSchristos 			  hdr.magic_sig));
3958585484eSchristos 	} else {
3968585484eSchristos 		INSIST(sizeof(hdr) < hdr.octets &&
3978585484eSchristos 		       hdr.octets < 16 * 1024);
3988585484eSchristos 		resp = emalloc(hdr.octets);
3998585484eSchristos 		memcpy(resp, &hdr, sizeof(*resp));
4008585484eSchristos 		octets = hdr.octets - sizeof(hdr);
401ccc794f0Schristos 		rc = netread(c->resp_read_pipe, (char *)(resp + 1),
4028585484eSchristos 			     octets);
4038585484eSchristos 
404ccc794f0Schristos 		if (rc != octets)
405ccc794f0Schristos 			TRACE(1, ("receive_blocking_resp_internal: short read (%zu of %zu), %m\n",
4068585484eSchristos 				  rc, octets));
4078585484eSchristos 		else
4088585484eSchristos 			return resp;
4098585484eSchristos 	}
4108585484eSchristos 
4118585484eSchristos 	cleanup_after_child(c);
4128585484eSchristos 
4138585484eSchristos 	if (resp != NULL)
4148585484eSchristos 		free(resp);
4158585484eSchristos 
4168585484eSchristos 	return NULL;
4178585484eSchristos }
4188585484eSchristos 
4198585484eSchristos 
4208585484eSchristos #if defined(HAVE_DROPROOT) && defined(WORK_FORK)
4218585484eSchristos void
fork_deferred_worker(void)4228585484eSchristos fork_deferred_worker(void)
4238585484eSchristos {
4248585484eSchristos 	u_int			idx;
4258585484eSchristos 	blocking_child *	c;
4268585484eSchristos 
4278585484eSchristos 	REQUIRE(droproot && root_dropped);
4288585484eSchristos 
4298585484eSchristos 	for (idx = 0; idx < blocking_children_alloc; idx++) {
4308585484eSchristos 		c = blocking_children[idx];
4318585484eSchristos 		if (NULL == c)
4328585484eSchristos 			continue;
4338585484eSchristos 		if (-1 != c->req_write_pipe && 0 == c->pid)
4348585484eSchristos 			fork_blocking_child(c);
4358585484eSchristos 	}
4368585484eSchristos }
4378585484eSchristos #endif
4388585484eSchristos 
43994523889Schristos #if HAVE_SETPROCTITLE == 0
44094523889Schristos static void
setproctitle(const char * fmt,...)44194523889Schristos setproctitle(const char *fmt, ...)
44294523889Schristos {
44394523889Schristos 	va_list ap;
44494523889Schristos 	char b1[128];
44594523889Schristos 	int argcc, argvlen, l;
44694523889Schristos 
44794523889Schristos 	if (saved_argc == 0)
44894523889Schristos 		return;
44994523889Schristos 
45094523889Schristos 	va_start(ap, fmt);
45194523889Schristos 	vsnprintf(b1, sizeof(b1), fmt, ap);
45294523889Schristos 	va_end(ap);
45394523889Schristos 
45494523889Schristos 	/* Clear argv */
45594523889Schristos 	for (argvlen = 0, argcc = 0; argcc < saved_argc; argcc++) {
45694523889Schristos 		l = strlen(saved_argv[argcc]);
45794523889Schristos 		argvlen += l + 1;
45894523889Schristos 		memset(saved_argv[argcc], 0, l);
45994523889Schristos 	}
46094523889Schristos 	l = snprintf(saved_argv[0], argvlen, "ntpd: %s", b1);
46194523889Schristos 	for (argcc = 1; argcc < saved_argc; argcc++)
46294523889Schristos 		saved_argv[argcc] = &saved_argv[0][l];
46394523889Schristos }
46494523889Schristos #endif
4658585484eSchristos 
4668585484eSchristos static void
fork_blocking_child(blocking_child * c)4678585484eSchristos fork_blocking_child(
4688585484eSchristos 	blocking_child *	c
4698585484eSchristos 	)
4708585484eSchristos {
4718585484eSchristos 	static int	atexit_installed;
4728585484eSchristos 	static int	blocking_pipes[4] = { -1, -1, -1, -1 };
4738585484eSchristos 	int		rc;
4748585484eSchristos 	int		was_pipe;
4758585484eSchristos 	int		is_pipe;
4767476e6e4Schristos 	int		saved_errno = 0;
4778585484eSchristos 	int		childpid;
4788585484eSchristos 	int		keep_fd;
4798585484eSchristos 	int		fd;
4808585484eSchristos 
4818585484eSchristos 	/*
4828585484eSchristos 	 * parent and child communicate via a pair of pipes.
4838585484eSchristos 	 *
4848585484eSchristos 	 * 0 child read request
4858585484eSchristos 	 * 1 parent write request
4868585484eSchristos 	 * 2 parent read response
4878585484eSchristos 	 * 3 child write response
4888585484eSchristos 	 */
4898585484eSchristos 	if (-1 == c->req_write_pipe) {
4908585484eSchristos 		rc = pipe_socketpair(&blocking_pipes[0], &was_pipe);
4918585484eSchristos 		if (0 != rc) {
4928585484eSchristos 			saved_errno = errno;
4938585484eSchristos 		} else {
4948585484eSchristos 			rc = pipe_socketpair(&blocking_pipes[2], &is_pipe);
4958585484eSchristos 			if (0 != rc) {
4968585484eSchristos 				saved_errno = errno;
4978585484eSchristos 				close(blocking_pipes[0]);
4988585484eSchristos 				close(blocking_pipes[1]);
4998585484eSchristos 			} else {
5008585484eSchristos 				INSIST(was_pipe == is_pipe);
5018585484eSchristos 			}
5028585484eSchristos 		}
5038585484eSchristos 		if (0 != rc) {
5048585484eSchristos 			errno = saved_errno;
5058585484eSchristos 			msyslog(LOG_ERR, "unable to create worker pipes: %m");
5068585484eSchristos 			exit(1);
5078585484eSchristos 		}
5088585484eSchristos 
5098585484eSchristos 		/*
5108585484eSchristos 		 * Move the descriptors the parent will keep open out of the
5118585484eSchristos 		 * low descriptors preferred by C runtime buffered FILE *.
5128585484eSchristos 		 */
5138585484eSchristos 		c->req_write_pipe = move_fd(blocking_pipes[1]);
5148585484eSchristos 		c->resp_read_pipe = move_fd(blocking_pipes[2]);
5158585484eSchristos 		/*
5168585484eSchristos 		 * wake any worker child on orderly shutdown of the
5178585484eSchristos 		 * daemon so that it can notice the broken pipes and
5188585484eSchristos 		 * go away promptly.
5198585484eSchristos 		 */
5208585484eSchristos 		if (!atexit_installed) {
5218585484eSchristos 			atexit(&send_worker_home_atexit);
5228585484eSchristos 			atexit_installed = TRUE;
5238585484eSchristos 		}
5248585484eSchristos 	}
5258585484eSchristos 
52668dbbb44Schristos #if defined(HAVE_DROPROOT) && !defined(NEED_EARLY_FORK)
5278585484eSchristos 	/* defer the fork until after root is dropped */
5288585484eSchristos 	if (droproot && !root_dropped)
5298585484eSchristos 		return;
5308585484eSchristos #endif
5318585484eSchristos 	if (syslog_file != NULL)
5328585484eSchristos 		fflush(syslog_file);
5338585484eSchristos 	fflush(stdout);
5348585484eSchristos 	fflush(stderr);
5358585484eSchristos 
53603cfe0ffSchristos 	/* [BUG 3050] setting SIGCHLD to SIG_IGN likely causes unwanted
53703cfe0ffSchristos 	 * or undefined effects. We don't do it and leave SIGCHLD alone.
53803cfe0ffSchristos 	 */
53903cfe0ffSchristos 	/* signal_no_reset(SIGCHLD, SIG_IGN); */
5408585484eSchristos 
5418585484eSchristos 	childpid = fork();
5428585484eSchristos 	if (-1 == childpid) {
5438585484eSchristos 		msyslog(LOG_ERR, "unable to fork worker: %m");
5448585484eSchristos 		exit(1);
5458585484eSchristos 	}
5468585484eSchristos 
5478585484eSchristos 	if (childpid) {
5488585484eSchristos 		/* this is the parent */
5498585484eSchristos 		TRACE(1, ("forked worker child (pid %d)\n", childpid));
5508585484eSchristos 		c->pid = childpid;
5518585484eSchristos 		c->ispipe = is_pipe;
5528585484eSchristos 
5538585484eSchristos 		/* close the child's pipe descriptors. */
5548585484eSchristos 		close(blocking_pipes[0]);
5558585484eSchristos 		close(blocking_pipes[3]);
5568585484eSchristos 
5578585484eSchristos 		memset(blocking_pipes, -1, sizeof(blocking_pipes));
5588585484eSchristos 
5598585484eSchristos 		/* wire into I/O loop */
5608585484eSchristos 		(*addremove_io_fd)(c->resp_read_pipe, is_pipe, FALSE);
5618585484eSchristos 
562*b6509243Schristos 		/* wait until child is done */
563*b6509243Schristos 		rc = netread(c->resp_read_pipe, &rc, sizeof(rc));
564*b6509243Schristos 
5658585484eSchristos 		return;		/* parent returns */
5668585484eSchristos 	}
5678585484eSchristos 
5688585484eSchristos 	/*
5698585484eSchristos 	 * The parent gets the child pid as the return value of fork().
5708585484eSchristos 	 * The child must work for it.
5718585484eSchristos 	 */
5728585484eSchristos 	c->pid = getpid();
5738585484eSchristos 	worker_process = TRUE;
5748585484eSchristos 
5758585484eSchristos 	/*
576ccc794f0Schristos 	 * Change the process name of the child to avoid confusion
577ccc794f0Schristos 	 * about ntpd trunning twice.
578ccc794f0Schristos 	 */
57994523889Schristos 	setproctitle("asynchronous dns resolver");
580ccc794f0Schristos 
581ccc794f0Schristos 	/*
5828585484eSchristos 	 * In the child, close all files except stdin, stdout, stderr,
5838585484eSchristos 	 * and the two child ends of the pipes.
5848585484eSchristos 	 */
5858585484eSchristos 	DEBUG_INSIST(-1 == c->req_read_pipe);
5868585484eSchristos 	DEBUG_INSIST(-1 == c->resp_write_pipe);
5878585484eSchristos 	c->req_read_pipe = blocking_pipes[0];
5888585484eSchristos 	c->resp_write_pipe = blocking_pipes[3];
5898585484eSchristos 
5908585484eSchristos 	kill_asyncio(0);
591*b6509243Schristos 
592*b6509243Schristos 	/* Tell parent we are ready */
593*b6509243Schristos 	rc = netwrite(c->resp_write_pipe, &rc, sizeof(rc));
594*b6509243Schristos 
5958585484eSchristos 	closelog();
5968585484eSchristos 	if (syslog_file != NULL) {
5978585484eSchristos 		fclose(syslog_file);
5988585484eSchristos 		syslog_file = NULL;
5998585484eSchristos 		syslogit = TRUE;
6008585484eSchristos 	}
6018585484eSchristos 	keep_fd = max(c->req_read_pipe, c->resp_write_pipe);
6028585484eSchristos 	for (fd = 3; fd < keep_fd; fd++)
6038585484eSchristos 		if (fd != c->req_read_pipe &&
6048585484eSchristos 		    fd != c->resp_write_pipe)
6058585484eSchristos 			close(fd);
6068585484eSchristos 	close_all_beyond(keep_fd);
6078585484eSchristos 	/*
6088585484eSchristos 	 * We get signals from refclock serial I/O on NetBSD in the
6098585484eSchristos 	 * worker if we do not reset SIGIO's handler to the default.
6108585484eSchristos 	 * It is not conditionalized for NetBSD alone because on
6118585484eSchristos 	 * systems where it is not needed, it is harmless, and that
6128585484eSchristos 	 * allows us to handle unknown others with NetBSD behavior.
6138585484eSchristos 	 * [Bug 1386]
6148585484eSchristos 	 */
6158585484eSchristos #if defined(USE_SIGIO)
6168585484eSchristos 	signal_no_reset(SIGIO, SIG_DFL);
6178585484eSchristos #elif defined(USE_SIGPOLL)
6188585484eSchristos 	signal_no_reset(SIGPOLL, SIG_DFL);
6198585484eSchristos #endif
6208585484eSchristos 	signal_no_reset(SIGHUP, worker_sighup);
6218585484eSchristos 	init_logging("ntp_intres", 0, FALSE);
6228585484eSchristos 	setup_logfile(NULL);
6238585484eSchristos 
624cdfa2a7eSchristos #ifdef HAVE_DROPROOT
62579045f13Schristos 	(void) set_user_group_ids();
626cdfa2a7eSchristos #endif
62779045f13Schristos 
6288585484eSchristos 	/*
6298585484eSchristos 	 * And now back to the portable code
6308585484eSchristos 	 */
6318585484eSchristos 	exit_worker(blocking_child_common(c));
6328585484eSchristos }
6338585484eSchristos 
6348585484eSchristos 
worker_global_lock(int inOrOut)63568dbbb44Schristos void worker_global_lock(int inOrOut)
63668dbbb44Schristos {
63768dbbb44Schristos 	(void)inOrOut;
63868dbbb44Schristos }
63968dbbb44Schristos 
6408585484eSchristos #else	/* !WORK_FORK follows */
6418585484eSchristos char work_fork_nonempty_compilation_unit;
6428585484eSchristos #endif
643