1275970Scy/*
2275970Scy * work_fork.c - fork implementation for blocking worker child.
3275970Scy */
4275970Scy#include <config.h>
5275970Scy#include "ntp_workimpl.h"
6275970Scy
7275970Scy#ifdef WORK_FORK
8275970Scy#include <stdio.h>
9275970Scy#include <ctype.h>
10275970Scy#include <signal.h>
11285612Sdelphij#include <sys/wait.h>
12275970Scy
13275970Scy#include "iosignal.h"
14275970Scy#include "ntp_stdlib.h"
15275970Scy#include "ntp_malloc.h"
16275970Scy#include "ntp_syslog.h"
17275970Scy#include "ntpd.h"
18275970Scy#include "ntp_io.h"
19275970Scy#include "ntp_assert.h"
20275970Scy#include "ntp_unixtime.h"
21275970Scy#include "ntp_worker.h"
22275970Scy
23275970Scy/* === variables === */
24275970Scy	int			worker_process;
25275970Scy	addremove_io_fd_func	addremove_io_fd;
26275970Scystatic	volatile int		worker_sighup_received;
27275970Scy
28275970Scy/* === function prototypes === */
29275970Scystatic	void		fork_blocking_child(blocking_child *);
30275970Scystatic	RETSIGTYPE	worker_sighup(int);
31275970Scystatic	void		send_worker_home_atexit(void);
32275970Scystatic	void		cleanup_after_child(blocking_child *);
33275970Scy
34275970Scy/* === functions === */
35275970Scy/*
36275970Scy * exit_worker()
37275970Scy *
38275970Scy * On some systems _exit() is preferred to exit() for forked children.
39275970Scy * For example, http://netbsd.gw.com/cgi-bin/man-cgi?fork++NetBSD-5.0
40275970Scy * recommends _exit() to avoid double-flushing C runtime stream buffers
41275970Scy * and also to avoid calling the parent's atexit() routines in the
42275970Scy * child.  On those systems WORKER_CHILD_EXIT is _exit.  Since _exit
43275970Scy * bypasses CRT cleanup, fflush() files we know might have output
44275970Scy * buffered.
45275970Scy */
46275970Scyvoid
47275970Scyexit_worker(
48275970Scy	int	exitcode
49275970Scy	)
50275970Scy{
51275970Scy	if (syslog_file != NULL)
52275970Scy		fflush(syslog_file);
53275970Scy	fflush(stdout);
54275970Scy	fflush(stderr);
55275970Scy	WORKER_CHILD_EXIT (exitcode);	/* space before ( required */
56275970Scy}
57275970Scy
58275970Scy
59275970Scystatic RETSIGTYPE
60275970Scyworker_sighup(
61275970Scy	int sig
62275970Scy	)
63275970Scy{
64275970Scy	if (SIGHUP == sig)
65275970Scy		worker_sighup_received = 1;
66275970Scy}
67275970Scy
68275970Scy
69275970Scyint
70275970Scyworker_sleep(
71275970Scy	blocking_child *	c,
72275970Scy	time_t			seconds
73275970Scy	)
74275970Scy{
75275970Scy	u_int sleep_remain;
76275970Scy
77275970Scy	sleep_remain = (u_int)seconds;
78275970Scy	do {
79275970Scy		if (!worker_sighup_received)
80275970Scy			sleep_remain = sleep(sleep_remain);
81275970Scy		if (worker_sighup_received) {
82275970Scy			TRACE(1, ("worker SIGHUP with %us left to sleep",
83275970Scy				  sleep_remain));
84275970Scy			worker_sighup_received = 0;
85275970Scy			return -1;
86275970Scy		}
87275970Scy	} while (sleep_remain);
88275970Scy
89275970Scy	return 0;
90275970Scy}
91275970Scy
92275970Scy
93275970Scyvoid
94275970Scyinterrupt_worker_sleep(void)
95275970Scy{
96275970Scy	u_int			idx;
97275970Scy	blocking_child *	c;
98275970Scy	int			rc;
99275970Scy
100275970Scy	for (idx = 0; idx < blocking_children_alloc; idx++) {
101275970Scy		c = blocking_children[idx];
102275970Scy
103275970Scy		if (NULL == c || c->reusable == TRUE)
104275970Scy			continue;
105275970Scy
106275970Scy		rc = kill(c->pid, SIGHUP);
107275970Scy		if (rc < 0)
108275970Scy			msyslog(LOG_ERR,
109275970Scy				"Unable to signal HUP to wake child pid %d: %m",
110275970Scy				c->pid);
111275970Scy	}
112275970Scy}
113275970Scy
114275970Scy
115275970Scy/*
116285612Sdelphij * harvest_child_status() runs in the parent.
117310419Sdelphij *
118310419Sdelphij * Note the error handling -- this is an interaction with SIGCHLD.
119310419Sdelphij * SIG_IGN on SIGCHLD on some OSes means do not wait but reap
120310419Sdelphij * automatically. Since we're not really interested in the result code,
121310419Sdelphij * we simply ignore the error.
122285612Sdelphij */
123285612Sdelphijstatic void
124285612Sdelphijharvest_child_status(
125285612Sdelphij	blocking_child *	c
126285612Sdelphij	)
127285612Sdelphij{
128310419Sdelphij	if (c->pid) {
129285612Sdelphij		/* Wait on the child so it can finish terminating */
130285612Sdelphij		if (waitpid(c->pid, NULL, 0) == c->pid)
131285612Sdelphij			TRACE(4, ("harvested child %d\n", c->pid));
132310419Sdelphij		else if (errno != ECHILD)
133310419Sdelphij			msyslog(LOG_ERR, "error waiting on child %d: %m", c->pid);
134310419Sdelphij		c->pid = 0;
135285612Sdelphij	}
136285612Sdelphij}
137285612Sdelphij
138285612Sdelphij/*
139275970Scy * req_child_exit() runs in the parent.
140275970Scy */
141275970Scyint
142275970Scyreq_child_exit(
143275970Scy	blocking_child *	c
144275970Scy	)
145275970Scy{
146275970Scy	if (-1 != c->req_write_pipe) {
147275970Scy		close(c->req_write_pipe);
148275970Scy		c->req_write_pipe = -1;
149275970Scy		return 0;
150275970Scy	}
151285612Sdelphij	/* Closing the pipe forces the child to exit */
152285612Sdelphij	harvest_child_status(c);
153275970Scy	return -1;
154275970Scy}
155275970Scy
156275970Scy
157275970Scy/*
158275970Scy * cleanup_after_child() runs in parent.
159275970Scy */
160275970Scystatic void
161275970Scycleanup_after_child(
162275970Scy	blocking_child *	c
163275970Scy	)
164275970Scy{
165285612Sdelphij	harvest_child_status(c);
166275970Scy	if (-1 != c->resp_read_pipe) {
167275970Scy		(*addremove_io_fd)(c->resp_read_pipe, c->ispipe, TRUE);
168275970Scy		close(c->resp_read_pipe);
169275970Scy		c->resp_read_pipe = -1;
170275970Scy	}
171275970Scy	c->resp_read_ctx = NULL;
172275970Scy	DEBUG_INSIST(-1 == c->req_read_pipe);
173275970Scy	DEBUG_INSIST(-1 == c->resp_write_pipe);
174275970Scy	c->reusable = TRUE;
175275970Scy}
176275970Scy
177275970Scy
178275970Scystatic void
179275970Scysend_worker_home_atexit(void)
180275970Scy{
181275970Scy	u_int			idx;
182275970Scy	blocking_child *	c;
183275970Scy
184275970Scy	if (worker_process)
185275970Scy		return;
186275970Scy
187275970Scy	for (idx = 0; idx < blocking_children_alloc; idx++) {
188275970Scy		c = blocking_children[idx];
189275970Scy		if (NULL == c)
190275970Scy			continue;
191275970Scy		req_child_exit(c);
192275970Scy	}
193275970Scy}
194275970Scy
195275970Scy
196275970Scyint
197275970Scysend_blocking_req_internal(
198275970Scy	blocking_child *	c,
199275970Scy	blocking_pipe_header *	hdr,
200275970Scy	void *			data
201275970Scy	)
202275970Scy{
203275970Scy	int octets;
204275970Scy	int rc;
205275970Scy
206275970Scy	DEBUG_REQUIRE(hdr != NULL);
207275970Scy	DEBUG_REQUIRE(data != NULL);
208275970Scy	DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == hdr->magic_sig);
209275970Scy
210275970Scy	if (-1 == c->req_write_pipe) {
211275970Scy		fork_blocking_child(c);
212275970Scy		DEBUG_INSIST(-1 != c->req_write_pipe);
213275970Scy	}
214275970Scy
215275970Scy	octets = sizeof(*hdr);
216275970Scy	rc = write(c->req_write_pipe, hdr, octets);
217275970Scy
218275970Scy	if (rc == octets) {
219275970Scy		octets = hdr->octets - sizeof(*hdr);
220275970Scy		rc = write(c->req_write_pipe, data, octets);
221275970Scy
222275970Scy		if (rc == octets)
223275970Scy			return 0;
224275970Scy	}
225275970Scy
226275970Scy	if (rc < 0)
227275970Scy		msyslog(LOG_ERR,
228275970Scy			"send_blocking_req_internal: pipe write: %m");
229275970Scy	else
230275970Scy		msyslog(LOG_ERR,
231275970Scy			"send_blocking_req_internal: short write %d of %d",
232275970Scy			rc, octets);
233275970Scy
234285612Sdelphij	/* Fatal error.  Clean up the child process.  */
235285612Sdelphij	req_child_exit(c);
236275970Scy	exit(1);	/* otherwise would be return -1 */
237275970Scy}
238275970Scy
239275970Scy
240275970Scyblocking_pipe_header *
241275970Scyreceive_blocking_req_internal(
242275970Scy	blocking_child *	c
243275970Scy	)
244275970Scy{
245275970Scy	blocking_pipe_header	hdr;
246275970Scy	blocking_pipe_header *	req;
247275970Scy	int			rc;
248275970Scy	long			octets;
249275970Scy
250275970Scy	DEBUG_REQUIRE(-1 != c->req_read_pipe);
251275970Scy
252275970Scy	req = NULL;
253275970Scy
254275970Scy	do {
255275970Scy		rc = read(c->req_read_pipe, &hdr, sizeof(hdr));
256275970Scy	} while (rc < 0 && EINTR == errno);
257275970Scy
258275970Scy	if (rc < 0) {
259275970Scy		msyslog(LOG_ERR,
260275970Scy			"receive_blocking_req_internal: pipe read %m");
261275970Scy	} else if (0 == rc) {
262275970Scy		TRACE(4, ("parent closed request pipe, child %d terminating\n",
263275970Scy			  c->pid));
264275970Scy	} else if (rc != sizeof(hdr)) {
265275970Scy		msyslog(LOG_ERR,
266275970Scy			"receive_blocking_req_internal: short header read %d of %lu",
267275970Scy			rc, (u_long)sizeof(hdr));
268275970Scy	} else {
269275970Scy		INSIST(sizeof(hdr) < hdr.octets && hdr.octets < 4 * 1024);
270275970Scy		req = emalloc(hdr.octets);
271275970Scy		memcpy(req, &hdr, sizeof(*req));
272275970Scy		octets = hdr.octets - sizeof(hdr);
273275970Scy		rc = read(c->req_read_pipe, (char *)req + sizeof(*req),
274275970Scy			  octets);
275275970Scy
276275970Scy		if (rc < 0)
277275970Scy			msyslog(LOG_ERR,
278275970Scy				"receive_blocking_req_internal: pipe data read %m");
279275970Scy		else if (rc != octets)
280275970Scy			msyslog(LOG_ERR,
281275970Scy				"receive_blocking_req_internal: short read %d of %ld",
282275970Scy				rc, octets);
283275970Scy		else if (BLOCKING_REQ_MAGIC != req->magic_sig)
284275970Scy			msyslog(LOG_ERR,
285275970Scy				"receive_blocking_req_internal: packet header mismatch (0x%x)",
286275970Scy				req->magic_sig);
287275970Scy		else
288275970Scy			return req;
289275970Scy	}
290275970Scy
291275970Scy	if (req != NULL)
292275970Scy		free(req);
293275970Scy
294275970Scy	return NULL;
295275970Scy}
296275970Scy
297275970Scy
298275970Scyint
299275970Scysend_blocking_resp_internal(
300275970Scy	blocking_child *	c,
301275970Scy	blocking_pipe_header *	resp
302275970Scy	)
303275970Scy{
304275970Scy	long	octets;
305275970Scy	int	rc;
306275970Scy
307275970Scy	DEBUG_REQUIRE(-1 != c->resp_write_pipe);
308275970Scy
309275970Scy	octets = resp->octets;
310275970Scy	rc = write(c->resp_write_pipe, resp, octets);
311275970Scy	free(resp);
312275970Scy
313275970Scy	if (octets == rc)
314275970Scy		return 0;
315275970Scy
316275970Scy	if (rc < 0)
317275970Scy		TRACE(1, ("send_blocking_resp_internal: pipe write %m\n"));
318275970Scy	else
319275970Scy		TRACE(1, ("send_blocking_resp_internal: short write %d of %ld\n",
320275970Scy			  rc, octets));
321275970Scy
322275970Scy	return -1;
323275970Scy}
324275970Scy
325275970Scy
326275970Scyblocking_pipe_header *
327275970Scyreceive_blocking_resp_internal(
328275970Scy	blocking_child *	c
329275970Scy	)
330275970Scy{
331275970Scy	blocking_pipe_header	hdr;
332275970Scy	blocking_pipe_header *	resp;
333275970Scy	int			rc;
334275970Scy	long			octets;
335275970Scy
336275970Scy	DEBUG_REQUIRE(c->resp_read_pipe != -1);
337275970Scy
338275970Scy	resp = NULL;
339275970Scy	rc = read(c->resp_read_pipe, &hdr, sizeof(hdr));
340275970Scy
341275970Scy	if (rc < 0) {
342275970Scy		TRACE(1, ("receive_blocking_resp_internal: pipe read %m\n"));
343275970Scy	} else if (0 == rc) {
344275970Scy		/* this is the normal child exited indication */
345275970Scy	} else if (rc != sizeof(hdr)) {
346275970Scy		TRACE(1, ("receive_blocking_resp_internal: short header read %d of %lu\n",
347275970Scy			  rc, (u_long)sizeof(hdr)));
348275970Scy	} else if (BLOCKING_RESP_MAGIC != hdr.magic_sig) {
349275970Scy		TRACE(1, ("receive_blocking_resp_internal: header mismatch (0x%x)\n",
350275970Scy			  hdr.magic_sig));
351275970Scy	} else {
352275970Scy		INSIST(sizeof(hdr) < hdr.octets &&
353275970Scy		       hdr.octets < 16 * 1024);
354275970Scy		resp = emalloc(hdr.octets);
355275970Scy		memcpy(resp, &hdr, sizeof(*resp));
356275970Scy		octets = hdr.octets - sizeof(hdr);
357275970Scy		rc = read(c->resp_read_pipe,
358275970Scy			  (char *)resp + sizeof(*resp),
359275970Scy			  octets);
360275970Scy
361275970Scy		if (rc < 0)
362275970Scy			TRACE(1, ("receive_blocking_resp_internal: pipe data read %m\n"));
363275970Scy		else if (rc < octets)
364275970Scy			TRACE(1, ("receive_blocking_resp_internal: short read %d of %ld\n",
365275970Scy				  rc, octets));
366275970Scy		else
367275970Scy			return resp;
368275970Scy	}
369275970Scy
370275970Scy	cleanup_after_child(c);
371275970Scy
372275970Scy	if (resp != NULL)
373275970Scy		free(resp);
374275970Scy
375275970Scy	return NULL;
376275970Scy}
377275970Scy
378275970Scy
379275970Scy#if defined(HAVE_DROPROOT) && defined(WORK_FORK)
380275970Scyvoid
381275970Scyfork_deferred_worker(void)
382275970Scy{
383275970Scy	u_int			idx;
384275970Scy	blocking_child *	c;
385275970Scy
386275970Scy	REQUIRE(droproot && root_dropped);
387275970Scy
388275970Scy	for (idx = 0; idx < blocking_children_alloc; idx++) {
389275970Scy		c = blocking_children[idx];
390275970Scy		if (NULL == c)
391275970Scy			continue;
392275970Scy		if (-1 != c->req_write_pipe && 0 == c->pid)
393275970Scy			fork_blocking_child(c);
394275970Scy	}
395275970Scy}
396275970Scy#endif
397275970Scy
398275970Scy
399275970Scystatic void
400275970Scyfork_blocking_child(
401275970Scy	blocking_child *	c
402275970Scy	)
403275970Scy{
404275970Scy	static int	atexit_installed;
405275970Scy	static int	blocking_pipes[4] = { -1, -1, -1, -1 };
406275970Scy	int		rc;
407275970Scy	int		was_pipe;
408275970Scy	int		is_pipe;
409285612Sdelphij	int		saved_errno = 0;
410275970Scy	int		childpid;
411275970Scy	int		keep_fd;
412275970Scy	int		fd;
413275970Scy
414275970Scy	/*
415275970Scy	 * parent and child communicate via a pair of pipes.
416275970Scy	 *
417275970Scy	 * 0 child read request
418275970Scy	 * 1 parent write request
419275970Scy	 * 2 parent read response
420275970Scy	 * 3 child write response
421275970Scy	 */
422275970Scy	if (-1 == c->req_write_pipe) {
423275970Scy		rc = pipe_socketpair(&blocking_pipes[0], &was_pipe);
424275970Scy		if (0 != rc) {
425275970Scy			saved_errno = errno;
426275970Scy		} else {
427275970Scy			rc = pipe_socketpair(&blocking_pipes[2], &is_pipe);
428275970Scy			if (0 != rc) {
429275970Scy				saved_errno = errno;
430275970Scy				close(blocking_pipes[0]);
431275970Scy				close(blocking_pipes[1]);
432275970Scy			} else {
433275970Scy				INSIST(was_pipe == is_pipe);
434275970Scy			}
435275970Scy		}
436275970Scy		if (0 != rc) {
437275970Scy			errno = saved_errno;
438275970Scy			msyslog(LOG_ERR, "unable to create worker pipes: %m");
439275970Scy			exit(1);
440275970Scy		}
441275970Scy
442275970Scy		/*
443275970Scy		 * Move the descriptors the parent will keep open out of the
444275970Scy		 * low descriptors preferred by C runtime buffered FILE *.
445275970Scy		 */
446275970Scy		c->req_write_pipe = move_fd(blocking_pipes[1]);
447275970Scy		c->resp_read_pipe = move_fd(blocking_pipes[2]);
448275970Scy		/*
449275970Scy		 * wake any worker child on orderly shutdown of the
450275970Scy		 * daemon so that it can notice the broken pipes and
451275970Scy		 * go away promptly.
452275970Scy		 */
453275970Scy		if (!atexit_installed) {
454275970Scy			atexit(&send_worker_home_atexit);
455275970Scy			atexit_installed = TRUE;
456275970Scy		}
457275970Scy	}
458275970Scy
459298770Sdelphij#if defined(HAVE_DROPROOT) && !defined(NEED_EARLY_FORK)
460275970Scy	/* defer the fork until after root is dropped */
461275970Scy	if (droproot && !root_dropped)
462275970Scy		return;
463275970Scy#endif
464275970Scy	if (syslog_file != NULL)
465275970Scy		fflush(syslog_file);
466275970Scy	fflush(stdout);
467275970Scy	fflush(stderr);
468275970Scy
469310419Sdelphij	/* [BUG 3050] setting SIGCHLD to SIG_IGN likely causes unwanted
470310419Sdelphij	 * or undefined effects. We don't do it and leave SIGCHLD alone.
471310419Sdelphij	 */
472310419Sdelphij	/* signal_no_reset(SIGCHLD, SIG_IGN); */
473275970Scy
474275970Scy	childpid = fork();
475275970Scy	if (-1 == childpid) {
476275970Scy		msyslog(LOG_ERR, "unable to fork worker: %m");
477275970Scy		exit(1);
478275970Scy	}
479275970Scy
480275970Scy	if (childpid) {
481275970Scy		/* this is the parent */
482275970Scy		TRACE(1, ("forked worker child (pid %d)\n", childpid));
483275970Scy		c->pid = childpid;
484275970Scy		c->ispipe = is_pipe;
485275970Scy
486275970Scy		/* close the child's pipe descriptors. */
487275970Scy		close(blocking_pipes[0]);
488275970Scy		close(blocking_pipes[3]);
489275970Scy
490275970Scy		memset(blocking_pipes, -1, sizeof(blocking_pipes));
491275970Scy
492275970Scy		/* wire into I/O loop */
493275970Scy		(*addremove_io_fd)(c->resp_read_pipe, is_pipe, FALSE);
494275970Scy
495275970Scy		return;		/* parent returns */
496275970Scy	}
497275970Scy
498275970Scy	/*
499275970Scy	 * The parent gets the child pid as the return value of fork().
500275970Scy	 * The child must work for it.
501275970Scy	 */
502275970Scy	c->pid = getpid();
503275970Scy	worker_process = TRUE;
504275970Scy
505275970Scy	/*
506275970Scy	 * In the child, close all files except stdin, stdout, stderr,
507275970Scy	 * and the two child ends of the pipes.
508275970Scy	 */
509275970Scy	DEBUG_INSIST(-1 == c->req_read_pipe);
510275970Scy	DEBUG_INSIST(-1 == c->resp_write_pipe);
511275970Scy	c->req_read_pipe = blocking_pipes[0];
512275970Scy	c->resp_write_pipe = blocking_pipes[3];
513275970Scy
514275970Scy	kill_asyncio(0);
515275970Scy	closelog();
516275970Scy	if (syslog_file != NULL) {
517275970Scy		fclose(syslog_file);
518275970Scy		syslog_file = NULL;
519275970Scy		syslogit = TRUE;
520275970Scy	}
521275970Scy	keep_fd = max(c->req_read_pipe, c->resp_write_pipe);
522275970Scy	for (fd = 3; fd < keep_fd; fd++)
523275970Scy		if (fd != c->req_read_pipe &&
524275970Scy		    fd != c->resp_write_pipe)
525275970Scy			close(fd);
526275970Scy	close_all_beyond(keep_fd);
527275970Scy	/*
528275970Scy	 * We get signals from refclock serial I/O on NetBSD in the
529275970Scy	 * worker if we do not reset SIGIO's handler to the default.
530275970Scy	 * It is not conditionalized for NetBSD alone because on
531275970Scy	 * systems where it is not needed, it is harmless, and that
532275970Scy	 * allows us to handle unknown others with NetBSD behavior.
533275970Scy	 * [Bug 1386]
534275970Scy	 */
535275970Scy#if defined(USE_SIGIO)
536275970Scy	signal_no_reset(SIGIO, SIG_DFL);
537275970Scy#elif defined(USE_SIGPOLL)
538275970Scy	signal_no_reset(SIGPOLL, SIG_DFL);
539275970Scy#endif
540275970Scy	signal_no_reset(SIGHUP, worker_sighup);
541275970Scy	init_logging("ntp_intres", 0, FALSE);
542275970Scy	setup_logfile(NULL);
543275970Scy
544275970Scy	/*
545275970Scy	 * And now back to the portable code
546275970Scy	 */
547275970Scy	exit_worker(blocking_child_common(c));
548275970Scy}
549275970Scy
550275970Scy
551298770Sdelphijvoid worker_global_lock(int inOrOut)
552298770Sdelphij{
553298770Sdelphij	(void)inOrOut;
554298770Sdelphij}
555298770Sdelphij
556275970Scy#else	/* !WORK_FORK follows */
557275970Scychar work_fork_nonempty_compilation_unit;
558275970Scy#endif
559