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