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