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