2 * work_fork.c - fork implementation for blocking worker child.
5 #include "ntp_workimpl.h"
13 #include "ntp_stdlib.h"
14 #include "ntp_malloc.h"
15 #include "ntp_syslog.h"
18 #include "ntp_assert.h"
19 #include "ntp_unixtime.h"
20 #include "ntp_worker.h"
22 /* === variables === */
24 addremove_io_fd_func addremove_io_fd;
25 static volatile int worker_sighup_received;
27 /* === function prototypes === */
28 static void fork_blocking_child(blocking_child *);
29 static RETSIGTYPE worker_sighup(int);
30 static void send_worker_home_atexit(void);
31 static void cleanup_after_child(blocking_child *);
33 /* === functions === */
37 * On some systems _exit() is preferred to exit() for forked children.
38 * For example, http://netbsd.gw.com/cgi-bin/man-cgi?fork++NetBSD-5.0
39 * recommends _exit() to avoid double-flushing C runtime stream buffers
40 * and also to avoid calling the parent's atexit() routines in the
41 * child. On those systems WORKER_CHILD_EXIT is _exit. Since _exit
42 * bypasses CRT cleanup, fflush() files we know might have output
50 if (syslog_file != NULL)
54 WORKER_CHILD_EXIT (exitcode); /* space before ( required */
64 worker_sighup_received = 1;
76 sleep_remain = (u_int)seconds;
78 if (!worker_sighup_received)
79 sleep_remain = sleep(sleep_remain);
80 if (worker_sighup_received) {
81 TRACE(1, ("worker SIGHUP with %us left to sleep",
83 worker_sighup_received = 0;
86 } while (sleep_remain);
93 interrupt_worker_sleep(void)
99 for (idx = 0; idx < blocking_children_alloc; idx++) {
100 c = blocking_children[idx];
102 if (NULL == c || c->reusable == TRUE)
105 rc = kill(c->pid, SIGHUP);
108 "Unable to signal HUP to wake child pid %d: %m",
115 * req_child_exit() runs in the parent.
122 if (-1 != c->req_write_pipe) {
123 close(c->req_write_pipe);
124 c->req_write_pipe = -1;
132 * cleanup_after_child() runs in parent.
139 if (-1 != c->req_write_pipe) {
140 close(c->req_write_pipe);
141 c->req_write_pipe = -1;
143 if (-1 != c->resp_read_pipe) {
144 (*addremove_io_fd)(c->resp_read_pipe, c->ispipe, TRUE);
145 close(c->resp_read_pipe);
146 c->resp_read_pipe = -1;
149 c->resp_read_ctx = NULL;
150 DEBUG_INSIST(-1 == c->req_read_pipe);
151 DEBUG_INSIST(-1 == c->resp_write_pipe);
157 send_worker_home_atexit(void)
165 for (idx = 0; idx < blocking_children_alloc; idx++) {
166 c = blocking_children[idx];
175 send_blocking_req_internal(
177 blocking_pipe_header * hdr,
184 DEBUG_REQUIRE(hdr != NULL);
185 DEBUG_REQUIRE(data != NULL);
186 DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == hdr->magic_sig);
188 if (-1 == c->req_write_pipe) {
189 fork_blocking_child(c);
190 DEBUG_INSIST(-1 != c->req_write_pipe);
193 octets = sizeof(*hdr);
194 rc = write(c->req_write_pipe, hdr, octets);
197 octets = hdr->octets - sizeof(*hdr);
198 rc = write(c->req_write_pipe, data, octets);
206 "send_blocking_req_internal: pipe write: %m");
209 "send_blocking_req_internal: short write %d of %d",
212 exit(1); /* otherwise would be return -1 */
216 blocking_pipe_header *
217 receive_blocking_req_internal(
221 blocking_pipe_header hdr;
222 blocking_pipe_header * req;
226 DEBUG_REQUIRE(-1 != c->req_read_pipe);
231 rc = read(c->req_read_pipe, &hdr, sizeof(hdr));
232 } while (rc < 0 && EINTR == errno);
236 "receive_blocking_req_internal: pipe read %m");
237 } else if (0 == rc) {
238 TRACE(4, ("parent closed request pipe, child %d terminating\n",
240 } else if (rc != sizeof(hdr)) {
242 "receive_blocking_req_internal: short header read %d of %lu",
243 rc, (u_long)sizeof(hdr));
245 INSIST(sizeof(hdr) < hdr.octets && hdr.octets < 4 * 1024);
246 req = emalloc(hdr.octets);
247 memcpy(req, &hdr, sizeof(*req));
248 octets = hdr.octets - sizeof(hdr);
249 rc = read(c->req_read_pipe, (char *)req + sizeof(*req),
254 "receive_blocking_req_internal: pipe data read %m");
255 else if (rc != octets)
257 "receive_blocking_req_internal: short read %d of %ld",
259 else if (BLOCKING_REQ_MAGIC != req->magic_sig)
261 "receive_blocking_req_internal: packet header mismatch (0x%x)",
275 send_blocking_resp_internal(
277 blocking_pipe_header * resp
283 DEBUG_REQUIRE(-1 != c->resp_write_pipe);
285 octets = resp->octets;
286 rc = write(c->resp_write_pipe, resp, octets);
293 TRACE(1, ("send_blocking_resp_internal: pipe write %m\n"));
295 TRACE(1, ("send_blocking_resp_internal: short write %d of %ld\n",
302 blocking_pipe_header *
303 receive_blocking_resp_internal(
307 blocking_pipe_header hdr;
308 blocking_pipe_header * resp;
312 DEBUG_REQUIRE(c->resp_read_pipe != -1);
315 rc = read(c->resp_read_pipe, &hdr, sizeof(hdr));
318 TRACE(1, ("receive_blocking_resp_internal: pipe read %m\n"));
319 } else if (0 == rc) {
320 /* this is the normal child exited indication */
321 } else if (rc != sizeof(hdr)) {
322 TRACE(1, ("receive_blocking_resp_internal: short header read %d of %lu\n",
323 rc, (u_long)sizeof(hdr)));
324 } else if (BLOCKING_RESP_MAGIC != hdr.magic_sig) {
325 TRACE(1, ("receive_blocking_resp_internal: header mismatch (0x%x)\n",
328 INSIST(sizeof(hdr) < hdr.octets &&
329 hdr.octets < 16 * 1024);
330 resp = emalloc(hdr.octets);
331 memcpy(resp, &hdr, sizeof(*resp));
332 octets = hdr.octets - sizeof(hdr);
333 rc = read(c->resp_read_pipe,
334 (char *)resp + sizeof(*resp),
338 TRACE(1, ("receive_blocking_resp_internal: pipe data read %m\n"));
339 else if (rc < octets)
340 TRACE(1, ("receive_blocking_resp_internal: short read %d of %ld\n",
346 cleanup_after_child(c);
355 #if defined(HAVE_DROPROOT) && defined(WORK_FORK)
357 fork_deferred_worker(void)
362 REQUIRE(droproot && root_dropped);
364 for (idx = 0; idx < blocking_children_alloc; idx++) {
365 c = blocking_children[idx];
368 if (-1 != c->req_write_pipe && 0 == c->pid)
369 fork_blocking_child(c);
380 static int atexit_installed;
381 static int blocking_pipes[4] = { -1, -1, -1, -1 };
391 * parent and child communicate via a pair of pipes.
393 * 0 child read request
394 * 1 parent write request
395 * 2 parent read response
396 * 3 child write response
398 if (-1 == c->req_write_pipe) {
399 rc = pipe_socketpair(&blocking_pipes[0], &was_pipe);
403 rc = pipe_socketpair(&blocking_pipes[2], &is_pipe);
406 close(blocking_pipes[0]);
407 close(blocking_pipes[1]);
409 INSIST(was_pipe == is_pipe);
414 msyslog(LOG_ERR, "unable to create worker pipes: %m");
419 * Move the descriptors the parent will keep open out of the
420 * low descriptors preferred by C runtime buffered FILE *.
422 c->req_write_pipe = move_fd(blocking_pipes[1]);
423 c->resp_read_pipe = move_fd(blocking_pipes[2]);
425 * wake any worker child on orderly shutdown of the
426 * daemon so that it can notice the broken pipes and
429 if (!atexit_installed) {
430 atexit(&send_worker_home_atexit);
431 atexit_installed = TRUE;
436 /* defer the fork until after root is dropped */
437 if (droproot && !root_dropped)
440 if (syslog_file != NULL)
445 signal_no_reset(SIGCHLD, SIG_IGN);
448 if (-1 == childpid) {
449 msyslog(LOG_ERR, "unable to fork worker: %m");
454 /* this is the parent */
455 TRACE(1, ("forked worker child (pid %d)\n", childpid));
459 /* close the child's pipe descriptors. */
460 close(blocking_pipes[0]);
461 close(blocking_pipes[3]);
463 memset(blocking_pipes, -1, sizeof(blocking_pipes));
465 /* wire into I/O loop */
466 (*addremove_io_fd)(c->resp_read_pipe, is_pipe, FALSE);
468 return; /* parent returns */
472 * The parent gets the child pid as the return value of fork().
473 * The child must work for it.
476 worker_process = TRUE;
479 * In the child, close all files except stdin, stdout, stderr,
480 * and the two child ends of the pipes.
482 DEBUG_INSIST(-1 == c->req_read_pipe);
483 DEBUG_INSIST(-1 == c->resp_write_pipe);
484 c->req_read_pipe = blocking_pipes[0];
485 c->resp_write_pipe = blocking_pipes[3];
489 if (syslog_file != NULL) {
494 keep_fd = max(c->req_read_pipe, c->resp_write_pipe);
495 for (fd = 3; fd < keep_fd; fd++)
496 if (fd != c->req_read_pipe &&
497 fd != c->resp_write_pipe)
499 close_all_beyond(keep_fd);
501 * We get signals from refclock serial I/O on NetBSD in the
502 * worker if we do not reset SIGIO's handler to the default.
503 * It is not conditionalized for NetBSD alone because on
504 * systems where it is not needed, it is harmless, and that
505 * allows us to handle unknown others with NetBSD behavior.
508 #if defined(USE_SIGIO)
509 signal_no_reset(SIGIO, SIG_DFL);
510 #elif defined(USE_SIGPOLL)
511 signal_no_reset(SIGPOLL, SIG_DFL);
513 signal_no_reset(SIGHUP, worker_sighup);
514 init_logging("ntp_intres", 0, FALSE);
518 * And now back to the portable code
520 exit_worker(blocking_child_common(c));
524 #else /* !WORK_FORK follows */
525 char work_fork_nonempty_compilation_unit;