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