12b15cb3dSCy Schubert /*
22b15cb3dSCy Schubert * work_fork.c - fork implementation for blocking worker child.
32b15cb3dSCy Schubert */
42b15cb3dSCy Schubert #include <config.h>
52b15cb3dSCy Schubert #include "ntp_workimpl.h"
62b15cb3dSCy Schubert
72b15cb3dSCy Schubert #ifdef WORK_FORK
82b15cb3dSCy Schubert #include <stdio.h>
92b15cb3dSCy Schubert #include <ctype.h>
102b15cb3dSCy Schubert #include <signal.h>
11276da39aSCy Schubert #include <sys/wait.h>
122b15cb3dSCy Schubert
132b15cb3dSCy Schubert #include "iosignal.h"
142b15cb3dSCy Schubert #include "ntp_stdlib.h"
152b15cb3dSCy Schubert #include "ntp_malloc.h"
162b15cb3dSCy Schubert #include "ntp_syslog.h"
172b15cb3dSCy Schubert #include "ntpd.h"
182b15cb3dSCy Schubert #include "ntp_io.h"
192b15cb3dSCy Schubert #include "ntp_assert.h"
202b15cb3dSCy Schubert #include "ntp_unixtime.h"
212b15cb3dSCy Schubert #include "ntp_worker.h"
222b15cb3dSCy Schubert
232b15cb3dSCy Schubert /* === variables === */
242b15cb3dSCy Schubert int worker_process;
252b15cb3dSCy Schubert addremove_io_fd_func addremove_io_fd;
262b15cb3dSCy Schubert static volatile int worker_sighup_received;
27f0574f5cSXin LI int saved_argc = 0;
28f0574f5cSXin LI char **saved_argv;
292b15cb3dSCy Schubert
302b15cb3dSCy Schubert /* === function prototypes === */
312b15cb3dSCy Schubert static void fork_blocking_child(blocking_child *);
322b15cb3dSCy Schubert static RETSIGTYPE worker_sighup(int);
332b15cb3dSCy Schubert static void send_worker_home_atexit(void);
342b15cb3dSCy Schubert static void cleanup_after_child(blocking_child *);
352b15cb3dSCy Schubert
36f0574f5cSXin LI /* === I/O helpers === */
37f0574f5cSXin LI /* Since we have signals enabled, there's a good chance that blocking IO
38f0574f5cSXin LI * via pipe suffers from EINTR -- and this goes for both directions.
39f0574f5cSXin LI * The next two wrappers will loop until either all the data is written
40f0574f5cSXin LI * or read, plus handling the EOF condition on read. They may return
41f0574f5cSXin LI * zero if no data was transferred at all, and effectively every return
42f0574f5cSXin LI * value that differs from the given transfer length signifies an error
43f0574f5cSXin LI * condition.
44f0574f5cSXin LI */
45f0574f5cSXin LI
46f0574f5cSXin LI static size_t
netread(int fd,void * vb,size_t l)47f0574f5cSXin LI netread(
48f0574f5cSXin LI int fd,
49f0574f5cSXin LI void * vb,
50f0574f5cSXin LI size_t l
51f0574f5cSXin LI )
52f0574f5cSXin LI {
53f0574f5cSXin LI char * b = vb;
54f0574f5cSXin LI ssize_t r;
55f0574f5cSXin LI
56f0574f5cSXin LI while (l) {
57f0574f5cSXin LI r = read(fd, b, l);
58f0574f5cSXin LI if (r > 0) {
59f0574f5cSXin LI l -= r;
60f0574f5cSXin LI b += r;
61f0574f5cSXin LI } else if (r == 0 || errno != EINTR) {
62f0574f5cSXin LI l = 0;
63f0574f5cSXin LI }
64f0574f5cSXin LI }
65f0574f5cSXin LI return (size_t)(b - (char *)vb);
66f0574f5cSXin LI }
67f0574f5cSXin LI
68f0574f5cSXin LI
69f0574f5cSXin LI static size_t
netwrite(int fd,const void * vb,size_t l)70f0574f5cSXin LI netwrite(
71f0574f5cSXin LI int fd,
72f0574f5cSXin LI const void * vb,
73f0574f5cSXin LI size_t l
74f0574f5cSXin LI )
75f0574f5cSXin LI {
76f0574f5cSXin LI const char * b = vb;
77f0574f5cSXin LI ssize_t w;
78f0574f5cSXin LI
79f0574f5cSXin LI while (l) {
80f0574f5cSXin LI w = write(fd, b, l);
81f0574f5cSXin LI if (w > 0) {
82f0574f5cSXin LI l -= w;
83f0574f5cSXin LI b += w;
84f0574f5cSXin LI } else if (errno != EINTR) {
85f0574f5cSXin LI l = 0;
86f0574f5cSXin LI }
87f0574f5cSXin LI }
88f0574f5cSXin LI return (size_t)(b - (const char *)vb);
89f0574f5cSXin LI }
90f0574f5cSXin LI
91f0574f5cSXin LI
92*052d159aSCy Schubert #if defined(HAVE_DROPROOT)
93*052d159aSCy Schubert extern int set_user_group_ids(void);
94*052d159aSCy Schubert #endif
954e1ef62aSXin LI
962b15cb3dSCy Schubert /* === functions === */
972b15cb3dSCy Schubert /*
982b15cb3dSCy Schubert * exit_worker()
992b15cb3dSCy Schubert *
1002b15cb3dSCy Schubert * On some systems _exit() is preferred to exit() for forked children.
1012b15cb3dSCy Schubert * For example, http://netbsd.gw.com/cgi-bin/man-cgi?fork++NetBSD-5.0
1022b15cb3dSCy Schubert * recommends _exit() to avoid double-flushing C runtime stream buffers
1032b15cb3dSCy Schubert * and also to avoid calling the parent's atexit() routines in the
1042b15cb3dSCy Schubert * child. On those systems WORKER_CHILD_EXIT is _exit. Since _exit
1052b15cb3dSCy Schubert * bypasses CRT cleanup, fflush() files we know might have output
1062b15cb3dSCy Schubert * buffered.
1072b15cb3dSCy Schubert */
1082b15cb3dSCy Schubert void
exit_worker(int exitcode)1092b15cb3dSCy Schubert exit_worker(
1102b15cb3dSCy Schubert int exitcode
1112b15cb3dSCy Schubert )
1122b15cb3dSCy Schubert {
1132b15cb3dSCy Schubert if (syslog_file != NULL)
1142b15cb3dSCy Schubert fflush(syslog_file);
1152b15cb3dSCy Schubert fflush(stdout);
1162b15cb3dSCy Schubert fflush(stderr);
1172b15cb3dSCy Schubert WORKER_CHILD_EXIT (exitcode); /* space before ( required */
1182b15cb3dSCy Schubert }
1192b15cb3dSCy Schubert
1202b15cb3dSCy Schubert
1212b15cb3dSCy Schubert static RETSIGTYPE
worker_sighup(int sig)1222b15cb3dSCy Schubert worker_sighup(
1232b15cb3dSCy Schubert int sig
1242b15cb3dSCy Schubert )
1252b15cb3dSCy Schubert {
1262b15cb3dSCy Schubert if (SIGHUP == sig)
1272b15cb3dSCy Schubert worker_sighup_received = 1;
1282b15cb3dSCy Schubert }
1292b15cb3dSCy Schubert
1302b15cb3dSCy Schubert
1312b15cb3dSCy Schubert int
worker_sleep(blocking_child * c,time_t seconds)1322b15cb3dSCy Schubert worker_sleep(
1332b15cb3dSCy Schubert blocking_child * c,
1342b15cb3dSCy Schubert time_t seconds
1352b15cb3dSCy Schubert )
1362b15cb3dSCy Schubert {
1372b15cb3dSCy Schubert u_int sleep_remain;
1382b15cb3dSCy Schubert
1392b15cb3dSCy Schubert sleep_remain = (u_int)seconds;
1402b15cb3dSCy Schubert do {
1412b15cb3dSCy Schubert if (!worker_sighup_received)
1422b15cb3dSCy Schubert sleep_remain = sleep(sleep_remain);
1432b15cb3dSCy Schubert if (worker_sighup_received) {
1442b15cb3dSCy Schubert TRACE(1, ("worker SIGHUP with %us left to sleep",
1452b15cb3dSCy Schubert sleep_remain));
1462b15cb3dSCy Schubert worker_sighup_received = 0;
1472b15cb3dSCy Schubert return -1;
1482b15cb3dSCy Schubert }
1492b15cb3dSCy Schubert } while (sleep_remain);
1502b15cb3dSCy Schubert
1512b15cb3dSCy Schubert return 0;
1522b15cb3dSCy Schubert }
1532b15cb3dSCy Schubert
1542b15cb3dSCy Schubert
1552b15cb3dSCy Schubert void
interrupt_worker_sleep(void)1562b15cb3dSCy Schubert interrupt_worker_sleep(void)
1572b15cb3dSCy Schubert {
1582b15cb3dSCy Schubert u_int idx;
1592b15cb3dSCy Schubert blocking_child * c;
1602b15cb3dSCy Schubert int rc;
1612b15cb3dSCy Schubert
1622b15cb3dSCy Schubert for (idx = 0; idx < blocking_children_alloc; idx++) {
1632b15cb3dSCy Schubert c = blocking_children[idx];
1642b15cb3dSCy Schubert
1652b15cb3dSCy Schubert if (NULL == c || c->reusable == TRUE)
1662b15cb3dSCy Schubert continue;
1672b15cb3dSCy Schubert
1682b15cb3dSCy Schubert rc = kill(c->pid, SIGHUP);
1692b15cb3dSCy Schubert if (rc < 0)
1702b15cb3dSCy Schubert msyslog(LOG_ERR,
1712b15cb3dSCy Schubert "Unable to signal HUP to wake child pid %d: %m",
1722b15cb3dSCy Schubert c->pid);
1732b15cb3dSCy Schubert }
1742b15cb3dSCy Schubert }
1752b15cb3dSCy Schubert
1762b15cb3dSCy Schubert
1772b15cb3dSCy Schubert /*
178276da39aSCy Schubert * harvest_child_status() runs in the parent.
179f391d6bcSXin LI *
180f391d6bcSXin LI * Note the error handling -- this is an interaction with SIGCHLD.
181f391d6bcSXin LI * SIG_IGN on SIGCHLD on some OSes means do not wait but reap
182f391d6bcSXin LI * automatically. Since we're not really interested in the result code,
183f391d6bcSXin LI * we simply ignore the error.
184276da39aSCy Schubert */
185276da39aSCy Schubert static void
harvest_child_status(blocking_child * c)186276da39aSCy Schubert harvest_child_status(
187276da39aSCy Schubert blocking_child * c
188276da39aSCy Schubert )
189276da39aSCy Schubert {
190f391d6bcSXin LI if (c->pid) {
191276da39aSCy Schubert /* Wait on the child so it can finish terminating */
192276da39aSCy Schubert if (waitpid(c->pid, NULL, 0) == c->pid)
193276da39aSCy Schubert TRACE(4, ("harvested child %d\n", c->pid));
194f391d6bcSXin LI else if (errno != ECHILD)
195f391d6bcSXin LI msyslog(LOG_ERR, "error waiting on child %d: %m", c->pid);
196f391d6bcSXin LI c->pid = 0;
197276da39aSCy Schubert }
198276da39aSCy Schubert }
199276da39aSCy Schubert
200276da39aSCy Schubert /*
2012b15cb3dSCy Schubert * req_child_exit() runs in the parent.
2022b15cb3dSCy Schubert */
2032b15cb3dSCy Schubert int
req_child_exit(blocking_child * c)2042b15cb3dSCy Schubert req_child_exit(
2052b15cb3dSCy Schubert blocking_child * c
2062b15cb3dSCy Schubert )
2072b15cb3dSCy Schubert {
2082b15cb3dSCy Schubert if (-1 != c->req_write_pipe) {
2092b15cb3dSCy Schubert close(c->req_write_pipe);
2102b15cb3dSCy Schubert c->req_write_pipe = -1;
2112b15cb3dSCy Schubert return 0;
2122b15cb3dSCy Schubert }
213276da39aSCy Schubert /* Closing the pipe forces the child to exit */
214276da39aSCy Schubert harvest_child_status(c);
2152b15cb3dSCy Schubert return -1;
2162b15cb3dSCy Schubert }
2172b15cb3dSCy Schubert
2182b15cb3dSCy Schubert
2192b15cb3dSCy Schubert /*
2202b15cb3dSCy Schubert * cleanup_after_child() runs in parent.
2212b15cb3dSCy Schubert */
2222b15cb3dSCy Schubert static void
cleanup_after_child(blocking_child * c)2232b15cb3dSCy Schubert cleanup_after_child(
2242b15cb3dSCy Schubert blocking_child * c
2252b15cb3dSCy Schubert )
2262b15cb3dSCy Schubert {
227276da39aSCy Schubert harvest_child_status(c);
2282b15cb3dSCy Schubert if (-1 != c->resp_read_pipe) {
2292b15cb3dSCy Schubert (*addremove_io_fd)(c->resp_read_pipe, c->ispipe, TRUE);
2302b15cb3dSCy Schubert close(c->resp_read_pipe);
2312b15cb3dSCy Schubert c->resp_read_pipe = -1;
2322b15cb3dSCy Schubert }
2332b15cb3dSCy Schubert c->resp_read_ctx = NULL;
2342b15cb3dSCy Schubert DEBUG_INSIST(-1 == c->req_read_pipe);
2352b15cb3dSCy Schubert DEBUG_INSIST(-1 == c->resp_write_pipe);
2362b15cb3dSCy Schubert c->reusable = TRUE;
2372b15cb3dSCy Schubert }
2382b15cb3dSCy Schubert
2392b15cb3dSCy Schubert
2402b15cb3dSCy Schubert static void
send_worker_home_atexit(void)2412b15cb3dSCy Schubert send_worker_home_atexit(void)
2422b15cb3dSCy Schubert {
2432b15cb3dSCy Schubert u_int idx;
2442b15cb3dSCy Schubert blocking_child * c;
2452b15cb3dSCy Schubert
2462b15cb3dSCy Schubert if (worker_process)
2472b15cb3dSCy Schubert return;
2482b15cb3dSCy Schubert
2492b15cb3dSCy Schubert for (idx = 0; idx < blocking_children_alloc; idx++) {
2502b15cb3dSCy Schubert c = blocking_children[idx];
2512b15cb3dSCy Schubert if (NULL == c)
2522b15cb3dSCy Schubert continue;
2532b15cb3dSCy Schubert req_child_exit(c);
2542b15cb3dSCy Schubert }
2552b15cb3dSCy Schubert }
2562b15cb3dSCy Schubert
2572b15cb3dSCy Schubert
2582b15cb3dSCy Schubert int
send_blocking_req_internal(blocking_child * c,blocking_pipe_header * hdr,void * data)2592b15cb3dSCy Schubert send_blocking_req_internal(
2602b15cb3dSCy Schubert blocking_child * c,
2612b15cb3dSCy Schubert blocking_pipe_header * hdr,
2622b15cb3dSCy Schubert void * data
2632b15cb3dSCy Schubert )
2642b15cb3dSCy Schubert {
265f0574f5cSXin LI size_t octets;
266f0574f5cSXin LI size_t rc;
2672b15cb3dSCy Schubert
2682b15cb3dSCy Schubert DEBUG_REQUIRE(hdr != NULL);
2692b15cb3dSCy Schubert DEBUG_REQUIRE(data != NULL);
2702b15cb3dSCy Schubert DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == hdr->magic_sig);
2712b15cb3dSCy Schubert
2722b15cb3dSCy Schubert if (-1 == c->req_write_pipe) {
2732b15cb3dSCy Schubert fork_blocking_child(c);
2742b15cb3dSCy Schubert DEBUG_INSIST(-1 != c->req_write_pipe);
2752b15cb3dSCy Schubert }
2762b15cb3dSCy Schubert
2772b15cb3dSCy Schubert octets = sizeof(*hdr);
278f0574f5cSXin LI rc = netwrite(c->req_write_pipe, hdr, octets);
2792b15cb3dSCy Schubert
2802b15cb3dSCy Schubert if (rc == octets) {
2812b15cb3dSCy Schubert octets = hdr->octets - sizeof(*hdr);
282f0574f5cSXin LI rc = netwrite(c->req_write_pipe, data, octets);
2832b15cb3dSCy Schubert if (rc == octets)
2842b15cb3dSCy Schubert return 0;
2852b15cb3dSCy Schubert }
2862b15cb3dSCy Schubert
2872b15cb3dSCy Schubert msyslog(LOG_ERR,
288f0574f5cSXin LI "send_blocking_req_internal: short write (%zu of %zu), %m",
2892b15cb3dSCy Schubert rc, octets);
2902b15cb3dSCy Schubert
291276da39aSCy Schubert /* Fatal error. Clean up the child process. */
292276da39aSCy Schubert req_child_exit(c);
2932b15cb3dSCy Schubert exit(1); /* otherwise would be return -1 */
2942b15cb3dSCy Schubert }
2952b15cb3dSCy Schubert
2962b15cb3dSCy Schubert
2972b15cb3dSCy Schubert blocking_pipe_header *
receive_blocking_req_internal(blocking_child * c)2982b15cb3dSCy Schubert receive_blocking_req_internal(
2992b15cb3dSCy Schubert blocking_child * c
3002b15cb3dSCy Schubert )
3012b15cb3dSCy Schubert {
3022b15cb3dSCy Schubert blocking_pipe_header hdr;
3032b15cb3dSCy Schubert blocking_pipe_header * req;
304f0574f5cSXin LI size_t rc;
305f0574f5cSXin LI size_t octets;
3062b15cb3dSCy Schubert
3072b15cb3dSCy Schubert DEBUG_REQUIRE(-1 != c->req_read_pipe);
3082b15cb3dSCy Schubert
3092b15cb3dSCy Schubert req = NULL;
310f0574f5cSXin LI rc = netread(c->req_read_pipe, &hdr, sizeof(hdr));
3112b15cb3dSCy Schubert
312f0574f5cSXin LI if (0 == rc) {
3132b15cb3dSCy Schubert TRACE(4, ("parent closed request pipe, child %d terminating\n",
3142b15cb3dSCy Schubert c->pid));
3152b15cb3dSCy Schubert } else if (rc != sizeof(hdr)) {
3162b15cb3dSCy Schubert msyslog(LOG_ERR,
317f0574f5cSXin LI "receive_blocking_req_internal: short header read (%zu of %zu), %m",
318f0574f5cSXin LI rc, sizeof(hdr));
3192b15cb3dSCy Schubert } else {
3202b15cb3dSCy Schubert INSIST(sizeof(hdr) < hdr.octets && hdr.octets < 4 * 1024);
3212b15cb3dSCy Schubert req = emalloc(hdr.octets);
3222b15cb3dSCy Schubert memcpy(req, &hdr, sizeof(*req));
3232b15cb3dSCy Schubert octets = hdr.octets - sizeof(hdr);
324f0574f5cSXin LI rc = netread(c->req_read_pipe, (char *)(req + 1),
3252b15cb3dSCy Schubert octets);
3262b15cb3dSCy Schubert
327f0574f5cSXin LI if (rc != octets)
3282b15cb3dSCy Schubert msyslog(LOG_ERR,
329f0574f5cSXin LI "receive_blocking_req_internal: short read (%zu of %zu), %m",
3302b15cb3dSCy Schubert rc, octets);
3312b15cb3dSCy Schubert else if (BLOCKING_REQ_MAGIC != req->magic_sig)
3322b15cb3dSCy Schubert msyslog(LOG_ERR,
3332b15cb3dSCy Schubert "receive_blocking_req_internal: packet header mismatch (0x%x)",
3342b15cb3dSCy Schubert req->magic_sig);
3352b15cb3dSCy Schubert else
3362b15cb3dSCy Schubert return req;
3372b15cb3dSCy Schubert }
3382b15cb3dSCy Schubert
3392b15cb3dSCy Schubert if (req != NULL)
3402b15cb3dSCy Schubert free(req);
3412b15cb3dSCy Schubert
3422b15cb3dSCy Schubert return NULL;
3432b15cb3dSCy Schubert }
3442b15cb3dSCy Schubert
3452b15cb3dSCy Schubert
3462b15cb3dSCy Schubert int
send_blocking_resp_internal(blocking_child * c,blocking_pipe_header * resp)3472b15cb3dSCy Schubert send_blocking_resp_internal(
3482b15cb3dSCy Schubert blocking_child * c,
3492b15cb3dSCy Schubert blocking_pipe_header * resp
3502b15cb3dSCy Schubert )
3512b15cb3dSCy Schubert {
352f0574f5cSXin LI size_t octets;
353f0574f5cSXin LI size_t rc;
3542b15cb3dSCy Schubert
3552b15cb3dSCy Schubert DEBUG_REQUIRE(-1 != c->resp_write_pipe);
3562b15cb3dSCy Schubert
3572b15cb3dSCy Schubert octets = resp->octets;
358f0574f5cSXin LI rc = netwrite(c->resp_write_pipe, resp, octets);
3592b15cb3dSCy Schubert free(resp);
3602b15cb3dSCy Schubert
3612b15cb3dSCy Schubert if (octets == rc)
3622b15cb3dSCy Schubert return 0;
3632b15cb3dSCy Schubert
364f0574f5cSXin LI TRACE(1, ("send_blocking_resp_internal: short write (%zu of %zu), %m\n",
3652b15cb3dSCy Schubert rc, octets));
3662b15cb3dSCy Schubert return -1;
3672b15cb3dSCy Schubert }
3682b15cb3dSCy Schubert
3692b15cb3dSCy Schubert
3702b15cb3dSCy Schubert blocking_pipe_header *
receive_blocking_resp_internal(blocking_child * c)3712b15cb3dSCy Schubert receive_blocking_resp_internal(
3722b15cb3dSCy Schubert blocking_child * c
3732b15cb3dSCy Schubert )
3742b15cb3dSCy Schubert {
3752b15cb3dSCy Schubert blocking_pipe_header hdr;
3762b15cb3dSCy Schubert blocking_pipe_header * resp;
377f0574f5cSXin LI size_t rc;
378f0574f5cSXin LI size_t octets;
3792b15cb3dSCy Schubert
3802b15cb3dSCy Schubert DEBUG_REQUIRE(c->resp_read_pipe != -1);
3812b15cb3dSCy Schubert
3822b15cb3dSCy Schubert resp = NULL;
383f0574f5cSXin LI rc = netread(c->resp_read_pipe, &hdr, sizeof(hdr));
3842b15cb3dSCy Schubert
385f0574f5cSXin LI if (0 == rc) {
3862b15cb3dSCy Schubert /* this is the normal child exited indication */
3872b15cb3dSCy Schubert } else if (rc != sizeof(hdr)) {
388f0574f5cSXin LI TRACE(1, ("receive_blocking_resp_internal: short header read (%zu of %zu), %m\n",
389f0574f5cSXin LI rc, sizeof(hdr)));
3902b15cb3dSCy Schubert } else if (BLOCKING_RESP_MAGIC != hdr.magic_sig) {
3912b15cb3dSCy Schubert TRACE(1, ("receive_blocking_resp_internal: header mismatch (0x%x)\n",
3922b15cb3dSCy Schubert hdr.magic_sig));
3932b15cb3dSCy Schubert } else {
3942b15cb3dSCy Schubert INSIST(sizeof(hdr) < hdr.octets &&
3952b15cb3dSCy Schubert hdr.octets < 16 * 1024);
3962b15cb3dSCy Schubert resp = emalloc(hdr.octets);
3972b15cb3dSCy Schubert memcpy(resp, &hdr, sizeof(*resp));
3982b15cb3dSCy Schubert octets = hdr.octets - sizeof(hdr);
399f0574f5cSXin LI rc = netread(c->resp_read_pipe, (char *)(resp + 1),
4002b15cb3dSCy Schubert octets);
4012b15cb3dSCy Schubert
402f0574f5cSXin LI if (rc != octets)
403f0574f5cSXin LI TRACE(1, ("receive_blocking_resp_internal: short read (%zu of %zu), %m\n",
4042b15cb3dSCy Schubert rc, octets));
4052b15cb3dSCy Schubert else
4062b15cb3dSCy Schubert return resp;
4072b15cb3dSCy Schubert }
4082b15cb3dSCy Schubert
4092b15cb3dSCy Schubert cleanup_after_child(c);
4102b15cb3dSCy Schubert
4112b15cb3dSCy Schubert if (resp != NULL)
4122b15cb3dSCy Schubert free(resp);
4132b15cb3dSCy Schubert
4142b15cb3dSCy Schubert return NULL;
4152b15cb3dSCy Schubert }
4162b15cb3dSCy Schubert
4172b15cb3dSCy Schubert
4182b15cb3dSCy Schubert #if defined(HAVE_DROPROOT) && defined(WORK_FORK)
4192b15cb3dSCy Schubert void
fork_deferred_worker(void)4202b15cb3dSCy Schubert fork_deferred_worker(void)
4212b15cb3dSCy Schubert {
4222b15cb3dSCy Schubert u_int idx;
4232b15cb3dSCy Schubert blocking_child * c;
4242b15cb3dSCy Schubert
4252b15cb3dSCy Schubert REQUIRE(droproot && root_dropped);
4262b15cb3dSCy Schubert
4272b15cb3dSCy Schubert for (idx = 0; idx < blocking_children_alloc; idx++) {
4282b15cb3dSCy Schubert c = blocking_children[idx];
4292b15cb3dSCy Schubert if (NULL == c)
4302b15cb3dSCy Schubert continue;
4312b15cb3dSCy Schubert if (-1 != c->req_write_pipe && 0 == c->pid)
4322b15cb3dSCy Schubert fork_blocking_child(c);
4332b15cb3dSCy Schubert }
4342b15cb3dSCy Schubert }
4352b15cb3dSCy Schubert #endif
4362b15cb3dSCy Schubert
4372b15cb3dSCy Schubert
4382b15cb3dSCy Schubert static void
fork_blocking_child(blocking_child * c)4392b15cb3dSCy Schubert fork_blocking_child(
4402b15cb3dSCy Schubert blocking_child * c
4412b15cb3dSCy Schubert )
4422b15cb3dSCy Schubert {
4432b15cb3dSCy Schubert static int atexit_installed;
4442b15cb3dSCy Schubert static int blocking_pipes[4] = { -1, -1, -1, -1 };
4452b15cb3dSCy Schubert int rc;
4462b15cb3dSCy Schubert int was_pipe;
4472b15cb3dSCy Schubert int is_pipe;
448a25439b6SCy Schubert int saved_errno = 0;
4492b15cb3dSCy Schubert int childpid;
4502b15cb3dSCy Schubert int keep_fd;
4512b15cb3dSCy Schubert int fd;
4522b15cb3dSCy Schubert
4532b15cb3dSCy Schubert /*
4542b15cb3dSCy Schubert * parent and child communicate via a pair of pipes.
4552b15cb3dSCy Schubert *
4562b15cb3dSCy Schubert * 0 child read request
4572b15cb3dSCy Schubert * 1 parent write request
4582b15cb3dSCy Schubert * 2 parent read response
4592b15cb3dSCy Schubert * 3 child write response
4602b15cb3dSCy Schubert */
4612b15cb3dSCy Schubert if (-1 == c->req_write_pipe) {
4622b15cb3dSCy Schubert rc = pipe_socketpair(&blocking_pipes[0], &was_pipe);
4632b15cb3dSCy Schubert if (0 != rc) {
4642b15cb3dSCy Schubert saved_errno = errno;
4652b15cb3dSCy Schubert } else {
4662b15cb3dSCy Schubert rc = pipe_socketpair(&blocking_pipes[2], &is_pipe);
4672b15cb3dSCy Schubert if (0 != rc) {
4682b15cb3dSCy Schubert saved_errno = errno;
4692b15cb3dSCy Schubert close(blocking_pipes[0]);
4702b15cb3dSCy Schubert close(blocking_pipes[1]);
4712b15cb3dSCy Schubert } else {
4722b15cb3dSCy Schubert INSIST(was_pipe == is_pipe);
4732b15cb3dSCy Schubert }
4742b15cb3dSCy Schubert }
4752b15cb3dSCy Schubert if (0 != rc) {
4762b15cb3dSCy Schubert errno = saved_errno;
4772b15cb3dSCy Schubert msyslog(LOG_ERR, "unable to create worker pipes: %m");
4782b15cb3dSCy Schubert exit(1);
4792b15cb3dSCy Schubert }
4802b15cb3dSCy Schubert
4812b15cb3dSCy Schubert /*
4822b15cb3dSCy Schubert * Move the descriptors the parent will keep open out of the
4832b15cb3dSCy Schubert * low descriptors preferred by C runtime buffered FILE *.
4842b15cb3dSCy Schubert */
4852b15cb3dSCy Schubert c->req_write_pipe = move_fd(blocking_pipes[1]);
4862b15cb3dSCy Schubert c->resp_read_pipe = move_fd(blocking_pipes[2]);
4872b15cb3dSCy Schubert /*
4882b15cb3dSCy Schubert * wake any worker child on orderly shutdown of the
4892b15cb3dSCy Schubert * daemon so that it can notice the broken pipes and
4902b15cb3dSCy Schubert * go away promptly.
4912b15cb3dSCy Schubert */
4922b15cb3dSCy Schubert if (!atexit_installed) {
4932b15cb3dSCy Schubert atexit(&send_worker_home_atexit);
4942b15cb3dSCy Schubert atexit_installed = TRUE;
4952b15cb3dSCy Schubert }
4962b15cb3dSCy Schubert }
4972b15cb3dSCy Schubert
4984990d495SXin LI #if defined(HAVE_DROPROOT) && !defined(NEED_EARLY_FORK)
4992b15cb3dSCy Schubert /* defer the fork until after root is dropped */
5002b15cb3dSCy Schubert if (droproot && !root_dropped)
5012b15cb3dSCy Schubert return;
5022b15cb3dSCy Schubert #endif
5032b15cb3dSCy Schubert if (syslog_file != NULL)
5042b15cb3dSCy Schubert fflush(syslog_file);
5052b15cb3dSCy Schubert fflush(stdout);
5062b15cb3dSCy Schubert fflush(stderr);
5072b15cb3dSCy Schubert
508f391d6bcSXin LI /* [BUG 3050] setting SIGCHLD to SIG_IGN likely causes unwanted
509f391d6bcSXin LI * or undefined effects. We don't do it and leave SIGCHLD alone.
510f391d6bcSXin LI */
511f391d6bcSXin LI /* signal_no_reset(SIGCHLD, SIG_IGN); */
5122b15cb3dSCy Schubert
5132b15cb3dSCy Schubert childpid = fork();
5142b15cb3dSCy Schubert if (-1 == childpid) {
5152b15cb3dSCy Schubert msyslog(LOG_ERR, "unable to fork worker: %m");
5162b15cb3dSCy Schubert exit(1);
5172b15cb3dSCy Schubert }
5182b15cb3dSCy Schubert
5192b15cb3dSCy Schubert if (childpid) {
5202b15cb3dSCy Schubert /* this is the parent */
5212b15cb3dSCy Schubert TRACE(1, ("forked worker child (pid %d)\n", childpid));
5222b15cb3dSCy Schubert c->pid = childpid;
5232b15cb3dSCy Schubert c->ispipe = is_pipe;
5242b15cb3dSCy Schubert
5252b15cb3dSCy Schubert /* close the child's pipe descriptors. */
5262b15cb3dSCy Schubert close(blocking_pipes[0]);
5272b15cb3dSCy Schubert close(blocking_pipes[3]);
5282b15cb3dSCy Schubert
5292b15cb3dSCy Schubert memset(blocking_pipes, -1, sizeof(blocking_pipes));
5302b15cb3dSCy Schubert
5312b15cb3dSCy Schubert /* wire into I/O loop */
5322b15cb3dSCy Schubert (*addremove_io_fd)(c->resp_read_pipe, is_pipe, FALSE);
5332b15cb3dSCy Schubert
5342b15cb3dSCy Schubert return; /* parent returns */
5352b15cb3dSCy Schubert }
5362b15cb3dSCy Schubert
5372b15cb3dSCy Schubert /*
5382b15cb3dSCy Schubert * The parent gets the child pid as the return value of fork().
5392b15cb3dSCy Schubert * The child must work for it.
5402b15cb3dSCy Schubert */
5412b15cb3dSCy Schubert c->pid = getpid();
5422b15cb3dSCy Schubert worker_process = TRUE;
5432b15cb3dSCy Schubert
5442b15cb3dSCy Schubert /*
545f0574f5cSXin LI * Change the process name of the child to avoid confusion
546f0574f5cSXin LI * about ntpd trunning twice.
547f0574f5cSXin LI */
548f0574f5cSXin LI if (saved_argc != 0) {
549f0574f5cSXin LI int argcc;
550f0574f5cSXin LI int argvlen = 0;
551f0574f5cSXin LI /* Clear argv */
552f0574f5cSXin LI for (argcc = 0; argcc < saved_argc; argcc++) {
553f0574f5cSXin LI int l = strlen(saved_argv[argcc]);
554f0574f5cSXin LI argvlen += l + 1;
555f0574f5cSXin LI memset(saved_argv[argcc], 0, l);
556f0574f5cSXin LI }
557f0574f5cSXin LI strlcpy(saved_argv[0], "ntpd: asynchronous dns resolver", argvlen);
558f0574f5cSXin LI }
559f0574f5cSXin LI
560f0574f5cSXin LI /*
5612b15cb3dSCy Schubert * In the child, close all files except stdin, stdout, stderr,
5622b15cb3dSCy Schubert * and the two child ends of the pipes.
5632b15cb3dSCy Schubert */
5642b15cb3dSCy Schubert DEBUG_INSIST(-1 == c->req_read_pipe);
5652b15cb3dSCy Schubert DEBUG_INSIST(-1 == c->resp_write_pipe);
5662b15cb3dSCy Schubert c->req_read_pipe = blocking_pipes[0];
5672b15cb3dSCy Schubert c->resp_write_pipe = blocking_pipes[3];
5682b15cb3dSCy Schubert
5692b15cb3dSCy Schubert kill_asyncio(0);
5702b15cb3dSCy Schubert closelog();
5712b15cb3dSCy Schubert if (syslog_file != NULL) {
5722b15cb3dSCy Schubert fclose(syslog_file);
5732b15cb3dSCy Schubert syslog_file = NULL;
5742b15cb3dSCy Schubert syslogit = TRUE;
5752b15cb3dSCy Schubert }
5762b15cb3dSCy Schubert keep_fd = max(c->req_read_pipe, c->resp_write_pipe);
5772b15cb3dSCy Schubert for (fd = 3; fd < keep_fd; fd++)
5782b15cb3dSCy Schubert if (fd != c->req_read_pipe &&
5792b15cb3dSCy Schubert fd != c->resp_write_pipe)
5802b15cb3dSCy Schubert close(fd);
5812b15cb3dSCy Schubert close_all_beyond(keep_fd);
5822b15cb3dSCy Schubert /*
5832b15cb3dSCy Schubert * We get signals from refclock serial I/O on NetBSD in the
5842b15cb3dSCy Schubert * worker if we do not reset SIGIO's handler to the default.
5852b15cb3dSCy Schubert * It is not conditionalized for NetBSD alone because on
5862b15cb3dSCy Schubert * systems where it is not needed, it is harmless, and that
5872b15cb3dSCy Schubert * allows us to handle unknown others with NetBSD behavior.
5882b15cb3dSCy Schubert * [Bug 1386]
5892b15cb3dSCy Schubert */
5902b15cb3dSCy Schubert #if defined(USE_SIGIO)
5912b15cb3dSCy Schubert signal_no_reset(SIGIO, SIG_DFL);
5922b15cb3dSCy Schubert #elif defined(USE_SIGPOLL)
5932b15cb3dSCy Schubert signal_no_reset(SIGPOLL, SIG_DFL);
5942b15cb3dSCy Schubert #endif
5952b15cb3dSCy Schubert signal_no_reset(SIGHUP, worker_sighup);
5962b15cb3dSCy Schubert init_logging("ntp_intres", 0, FALSE);
5972b15cb3dSCy Schubert setup_logfile(NULL);
5982b15cb3dSCy Schubert
599*052d159aSCy Schubert #ifdef HAVE_DROPROOT
6004e1ef62aSXin LI (void) set_user_group_ids();
601*052d159aSCy Schubert #endif
6024e1ef62aSXin LI
6032b15cb3dSCy Schubert /*
6042b15cb3dSCy Schubert * And now back to the portable code
6052b15cb3dSCy Schubert */
6062b15cb3dSCy Schubert exit_worker(blocking_child_common(c));
6072b15cb3dSCy Schubert }
6082b15cb3dSCy Schubert
6092b15cb3dSCy Schubert
worker_global_lock(int inOrOut)6104990d495SXin LI void worker_global_lock(int inOrOut)
6114990d495SXin LI {
6124990d495SXin LI (void)inOrOut;
6134990d495SXin LI }
6144990d495SXin LI
6152b15cb3dSCy Schubert #else /* !WORK_FORK follows */
6162b15cb3dSCy Schubert char work_fork_nonempty_compilation_unit;
6172b15cb3dSCy Schubert #endif
618