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