1275970Scy/* 2275970Scy * work_fork.c - fork implementation for blocking worker child. 3275970Scy */ 4275970Scy#include <config.h> 5275970Scy#include "ntp_workimpl.h" 6275970Scy 7275970Scy#ifdef WORK_FORK 8275970Scy#include <stdio.h> 9275970Scy#include <ctype.h> 10275970Scy#include <signal.h> 11285612Sdelphij#include <sys/wait.h> 12275970Scy 13275970Scy#include "iosignal.h" 14275970Scy#include "ntp_stdlib.h" 15275970Scy#include "ntp_malloc.h" 16275970Scy#include "ntp_syslog.h" 17275970Scy#include "ntpd.h" 18275970Scy#include "ntp_io.h" 19275970Scy#include "ntp_assert.h" 20275970Scy#include "ntp_unixtime.h" 21275970Scy#include "ntp_worker.h" 22275970Scy 23275970Scy/* === variables === */ 24275970Scy int worker_process; 25275970Scy addremove_io_fd_func addremove_io_fd; 26275970Scystatic volatile int worker_sighup_received; 27275970Scy 28275970Scy/* === function prototypes === */ 29275970Scystatic void fork_blocking_child(blocking_child *); 30275970Scystatic RETSIGTYPE worker_sighup(int); 31275970Scystatic void send_worker_home_atexit(void); 32275970Scystatic void cleanup_after_child(blocking_child *); 33275970Scy 34275970Scy/* === functions === */ 35275970Scy/* 36275970Scy * exit_worker() 37275970Scy * 38275970Scy * On some systems _exit() is preferred to exit() for forked children. 39275970Scy * For example, http://netbsd.gw.com/cgi-bin/man-cgi?fork++NetBSD-5.0 40275970Scy * recommends _exit() to avoid double-flushing C runtime stream buffers 41275970Scy * and also to avoid calling the parent's atexit() routines in the 42275970Scy * child. On those systems WORKER_CHILD_EXIT is _exit. Since _exit 43275970Scy * bypasses CRT cleanup, fflush() files we know might have output 44275970Scy * buffered. 45275970Scy */ 46275970Scyvoid 47275970Scyexit_worker( 48275970Scy int exitcode 49275970Scy ) 50275970Scy{ 51275970Scy if (syslog_file != NULL) 52275970Scy fflush(syslog_file); 53275970Scy fflush(stdout); 54275970Scy fflush(stderr); 55275970Scy WORKER_CHILD_EXIT (exitcode); /* space before ( required */ 56275970Scy} 57275970Scy 58275970Scy 59275970Scystatic RETSIGTYPE 60275970Scyworker_sighup( 61275970Scy int sig 62275970Scy ) 63275970Scy{ 64275970Scy if (SIGHUP == sig) 65275970Scy worker_sighup_received = 1; 66275970Scy} 67275970Scy 68275970Scy 69275970Scyint 70275970Scyworker_sleep( 71275970Scy blocking_child * c, 72275970Scy time_t seconds 73275970Scy ) 74275970Scy{ 75275970Scy u_int sleep_remain; 76275970Scy 77275970Scy sleep_remain = (u_int)seconds; 78275970Scy do { 79275970Scy if (!worker_sighup_received) 80275970Scy sleep_remain = sleep(sleep_remain); 81275970Scy if (worker_sighup_received) { 82275970Scy TRACE(1, ("worker SIGHUP with %us left to sleep", 83275970Scy sleep_remain)); 84275970Scy worker_sighup_received = 0; 85275970Scy return -1; 86275970Scy } 87275970Scy } while (sleep_remain); 88275970Scy 89275970Scy return 0; 90275970Scy} 91275970Scy 92275970Scy 93275970Scyvoid 94275970Scyinterrupt_worker_sleep(void) 95275970Scy{ 96275970Scy u_int idx; 97275970Scy blocking_child * c; 98275970Scy int rc; 99275970Scy 100275970Scy for (idx = 0; idx < blocking_children_alloc; idx++) { 101275970Scy c = blocking_children[idx]; 102275970Scy 103275970Scy if (NULL == c || c->reusable == TRUE) 104275970Scy continue; 105275970Scy 106275970Scy rc = kill(c->pid, SIGHUP); 107275970Scy if (rc < 0) 108275970Scy msyslog(LOG_ERR, 109275970Scy "Unable to signal HUP to wake child pid %d: %m", 110275970Scy c->pid); 111275970Scy } 112275970Scy} 113275970Scy 114275970Scy 115275970Scy/* 116285612Sdelphij * harvest_child_status() runs in the parent. 117310419Sdelphij * 118310419Sdelphij * Note the error handling -- this is an interaction with SIGCHLD. 119310419Sdelphij * SIG_IGN on SIGCHLD on some OSes means do not wait but reap 120310419Sdelphij * automatically. Since we're not really interested in the result code, 121310419Sdelphij * we simply ignore the error. 122285612Sdelphij */ 123285612Sdelphijstatic void 124285612Sdelphijharvest_child_status( 125285612Sdelphij blocking_child * c 126285612Sdelphij ) 127285612Sdelphij{ 128310419Sdelphij if (c->pid) { 129285612Sdelphij /* Wait on the child so it can finish terminating */ 130285612Sdelphij if (waitpid(c->pid, NULL, 0) == c->pid) 131285612Sdelphij TRACE(4, ("harvested child %d\n", c->pid)); 132310419Sdelphij else if (errno != ECHILD) 133310419Sdelphij msyslog(LOG_ERR, "error waiting on child %d: %m", c->pid); 134310419Sdelphij c->pid = 0; 135285612Sdelphij } 136285612Sdelphij} 137285612Sdelphij 138285612Sdelphij/* 139275970Scy * req_child_exit() runs in the parent. 140275970Scy */ 141275970Scyint 142275970Scyreq_child_exit( 143275970Scy blocking_child * c 144275970Scy ) 145275970Scy{ 146275970Scy if (-1 != c->req_write_pipe) { 147275970Scy close(c->req_write_pipe); 148275970Scy c->req_write_pipe = -1; 149275970Scy return 0; 150275970Scy } 151285612Sdelphij /* Closing the pipe forces the child to exit */ 152285612Sdelphij harvest_child_status(c); 153275970Scy return -1; 154275970Scy} 155275970Scy 156275970Scy 157275970Scy/* 158275970Scy * cleanup_after_child() runs in parent. 159275970Scy */ 160275970Scystatic void 161275970Scycleanup_after_child( 162275970Scy blocking_child * c 163275970Scy ) 164275970Scy{ 165285612Sdelphij harvest_child_status(c); 166275970Scy if (-1 != c->resp_read_pipe) { 167275970Scy (*addremove_io_fd)(c->resp_read_pipe, c->ispipe, TRUE); 168275970Scy close(c->resp_read_pipe); 169275970Scy c->resp_read_pipe = -1; 170275970Scy } 171275970Scy c->resp_read_ctx = NULL; 172275970Scy DEBUG_INSIST(-1 == c->req_read_pipe); 173275970Scy DEBUG_INSIST(-1 == c->resp_write_pipe); 174275970Scy c->reusable = TRUE; 175275970Scy} 176275970Scy 177275970Scy 178275970Scystatic void 179275970Scysend_worker_home_atexit(void) 180275970Scy{ 181275970Scy u_int idx; 182275970Scy blocking_child * c; 183275970Scy 184275970Scy if (worker_process) 185275970Scy return; 186275970Scy 187275970Scy for (idx = 0; idx < blocking_children_alloc; idx++) { 188275970Scy c = blocking_children[idx]; 189275970Scy if (NULL == c) 190275970Scy continue; 191275970Scy req_child_exit(c); 192275970Scy } 193275970Scy} 194275970Scy 195275970Scy 196275970Scyint 197275970Scysend_blocking_req_internal( 198275970Scy blocking_child * c, 199275970Scy blocking_pipe_header * hdr, 200275970Scy void * data 201275970Scy ) 202275970Scy{ 203275970Scy int octets; 204275970Scy int rc; 205275970Scy 206275970Scy DEBUG_REQUIRE(hdr != NULL); 207275970Scy DEBUG_REQUIRE(data != NULL); 208275970Scy DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == hdr->magic_sig); 209275970Scy 210275970Scy if (-1 == c->req_write_pipe) { 211275970Scy fork_blocking_child(c); 212275970Scy DEBUG_INSIST(-1 != c->req_write_pipe); 213275970Scy } 214275970Scy 215275970Scy octets = sizeof(*hdr); 216275970Scy rc = write(c->req_write_pipe, hdr, octets); 217275970Scy 218275970Scy if (rc == octets) { 219275970Scy octets = hdr->octets - sizeof(*hdr); 220275970Scy rc = write(c->req_write_pipe, data, octets); 221275970Scy 222275970Scy if (rc == octets) 223275970Scy return 0; 224275970Scy } 225275970Scy 226275970Scy if (rc < 0) 227275970Scy msyslog(LOG_ERR, 228275970Scy "send_blocking_req_internal: pipe write: %m"); 229275970Scy else 230275970Scy msyslog(LOG_ERR, 231275970Scy "send_blocking_req_internal: short write %d of %d", 232275970Scy rc, octets); 233275970Scy 234285612Sdelphij /* Fatal error. Clean up the child process. */ 235285612Sdelphij req_child_exit(c); 236275970Scy exit(1); /* otherwise would be return -1 */ 237275970Scy} 238275970Scy 239275970Scy 240275970Scyblocking_pipe_header * 241275970Scyreceive_blocking_req_internal( 242275970Scy blocking_child * c 243275970Scy ) 244275970Scy{ 245275970Scy blocking_pipe_header hdr; 246275970Scy blocking_pipe_header * req; 247275970Scy int rc; 248275970Scy long octets; 249275970Scy 250275970Scy DEBUG_REQUIRE(-1 != c->req_read_pipe); 251275970Scy 252275970Scy req = NULL; 253275970Scy 254275970Scy do { 255275970Scy rc = read(c->req_read_pipe, &hdr, sizeof(hdr)); 256275970Scy } while (rc < 0 && EINTR == errno); 257275970Scy 258275970Scy if (rc < 0) { 259275970Scy msyslog(LOG_ERR, 260275970Scy "receive_blocking_req_internal: pipe read %m"); 261275970Scy } else if (0 == rc) { 262275970Scy TRACE(4, ("parent closed request pipe, child %d terminating\n", 263275970Scy c->pid)); 264275970Scy } else if (rc != sizeof(hdr)) { 265275970Scy msyslog(LOG_ERR, 266275970Scy "receive_blocking_req_internal: short header read %d of %lu", 267275970Scy rc, (u_long)sizeof(hdr)); 268275970Scy } else { 269275970Scy INSIST(sizeof(hdr) < hdr.octets && hdr.octets < 4 * 1024); 270275970Scy req = emalloc(hdr.octets); 271275970Scy memcpy(req, &hdr, sizeof(*req)); 272275970Scy octets = hdr.octets - sizeof(hdr); 273275970Scy rc = read(c->req_read_pipe, (char *)req + sizeof(*req), 274275970Scy octets); 275275970Scy 276275970Scy if (rc < 0) 277275970Scy msyslog(LOG_ERR, 278275970Scy "receive_blocking_req_internal: pipe data read %m"); 279275970Scy else if (rc != octets) 280275970Scy msyslog(LOG_ERR, 281275970Scy "receive_blocking_req_internal: short read %d of %ld", 282275970Scy rc, octets); 283275970Scy else if (BLOCKING_REQ_MAGIC != req->magic_sig) 284275970Scy msyslog(LOG_ERR, 285275970Scy "receive_blocking_req_internal: packet header mismatch (0x%x)", 286275970Scy req->magic_sig); 287275970Scy else 288275970Scy return req; 289275970Scy } 290275970Scy 291275970Scy if (req != NULL) 292275970Scy free(req); 293275970Scy 294275970Scy return NULL; 295275970Scy} 296275970Scy 297275970Scy 298275970Scyint 299275970Scysend_blocking_resp_internal( 300275970Scy blocking_child * c, 301275970Scy blocking_pipe_header * resp 302275970Scy ) 303275970Scy{ 304275970Scy long octets; 305275970Scy int rc; 306275970Scy 307275970Scy DEBUG_REQUIRE(-1 != c->resp_write_pipe); 308275970Scy 309275970Scy octets = resp->octets; 310275970Scy rc = write(c->resp_write_pipe, resp, octets); 311275970Scy free(resp); 312275970Scy 313275970Scy if (octets == rc) 314275970Scy return 0; 315275970Scy 316275970Scy if (rc < 0) 317275970Scy TRACE(1, ("send_blocking_resp_internal: pipe write %m\n")); 318275970Scy else 319275970Scy TRACE(1, ("send_blocking_resp_internal: short write %d of %ld\n", 320275970Scy rc, octets)); 321275970Scy 322275970Scy return -1; 323275970Scy} 324275970Scy 325275970Scy 326275970Scyblocking_pipe_header * 327275970Scyreceive_blocking_resp_internal( 328275970Scy blocking_child * c 329275970Scy ) 330275970Scy{ 331275970Scy blocking_pipe_header hdr; 332275970Scy blocking_pipe_header * resp; 333275970Scy int rc; 334275970Scy long octets; 335275970Scy 336275970Scy DEBUG_REQUIRE(c->resp_read_pipe != -1); 337275970Scy 338275970Scy resp = NULL; 339275970Scy rc = read(c->resp_read_pipe, &hdr, sizeof(hdr)); 340275970Scy 341275970Scy if (rc < 0) { 342275970Scy TRACE(1, ("receive_blocking_resp_internal: pipe read %m\n")); 343275970Scy } else if (0 == rc) { 344275970Scy /* this is the normal child exited indication */ 345275970Scy } else if (rc != sizeof(hdr)) { 346275970Scy TRACE(1, ("receive_blocking_resp_internal: short header read %d of %lu\n", 347275970Scy rc, (u_long)sizeof(hdr))); 348275970Scy } else if (BLOCKING_RESP_MAGIC != hdr.magic_sig) { 349275970Scy TRACE(1, ("receive_blocking_resp_internal: header mismatch (0x%x)\n", 350275970Scy hdr.magic_sig)); 351275970Scy } else { 352275970Scy INSIST(sizeof(hdr) < hdr.octets && 353275970Scy hdr.octets < 16 * 1024); 354275970Scy resp = emalloc(hdr.octets); 355275970Scy memcpy(resp, &hdr, sizeof(*resp)); 356275970Scy octets = hdr.octets - sizeof(hdr); 357275970Scy rc = read(c->resp_read_pipe, 358275970Scy (char *)resp + sizeof(*resp), 359275970Scy octets); 360275970Scy 361275970Scy if (rc < 0) 362275970Scy TRACE(1, ("receive_blocking_resp_internal: pipe data read %m\n")); 363275970Scy else if (rc < octets) 364275970Scy TRACE(1, ("receive_blocking_resp_internal: short read %d of %ld\n", 365275970Scy rc, octets)); 366275970Scy else 367275970Scy return resp; 368275970Scy } 369275970Scy 370275970Scy cleanup_after_child(c); 371275970Scy 372275970Scy if (resp != NULL) 373275970Scy free(resp); 374275970Scy 375275970Scy return NULL; 376275970Scy} 377275970Scy 378275970Scy 379275970Scy#if defined(HAVE_DROPROOT) && defined(WORK_FORK) 380275970Scyvoid 381275970Scyfork_deferred_worker(void) 382275970Scy{ 383275970Scy u_int idx; 384275970Scy blocking_child * c; 385275970Scy 386275970Scy REQUIRE(droproot && root_dropped); 387275970Scy 388275970Scy for (idx = 0; idx < blocking_children_alloc; idx++) { 389275970Scy c = blocking_children[idx]; 390275970Scy if (NULL == c) 391275970Scy continue; 392275970Scy if (-1 != c->req_write_pipe && 0 == c->pid) 393275970Scy fork_blocking_child(c); 394275970Scy } 395275970Scy} 396275970Scy#endif 397275970Scy 398275970Scy 399275970Scystatic void 400275970Scyfork_blocking_child( 401275970Scy blocking_child * c 402275970Scy ) 403275970Scy{ 404275970Scy static int atexit_installed; 405275970Scy static int blocking_pipes[4] = { -1, -1, -1, -1 }; 406275970Scy int rc; 407275970Scy int was_pipe; 408275970Scy int is_pipe; 409285612Sdelphij int saved_errno = 0; 410275970Scy int childpid; 411275970Scy int keep_fd; 412275970Scy int fd; 413275970Scy 414275970Scy /* 415275970Scy * parent and child communicate via a pair of pipes. 416275970Scy * 417275970Scy * 0 child read request 418275970Scy * 1 parent write request 419275970Scy * 2 parent read response 420275970Scy * 3 child write response 421275970Scy */ 422275970Scy if (-1 == c->req_write_pipe) { 423275970Scy rc = pipe_socketpair(&blocking_pipes[0], &was_pipe); 424275970Scy if (0 != rc) { 425275970Scy saved_errno = errno; 426275970Scy } else { 427275970Scy rc = pipe_socketpair(&blocking_pipes[2], &is_pipe); 428275970Scy if (0 != rc) { 429275970Scy saved_errno = errno; 430275970Scy close(blocking_pipes[0]); 431275970Scy close(blocking_pipes[1]); 432275970Scy } else { 433275970Scy INSIST(was_pipe == is_pipe); 434275970Scy } 435275970Scy } 436275970Scy if (0 != rc) { 437275970Scy errno = saved_errno; 438275970Scy msyslog(LOG_ERR, "unable to create worker pipes: %m"); 439275970Scy exit(1); 440275970Scy } 441275970Scy 442275970Scy /* 443275970Scy * Move the descriptors the parent will keep open out of the 444275970Scy * low descriptors preferred by C runtime buffered FILE *. 445275970Scy */ 446275970Scy c->req_write_pipe = move_fd(blocking_pipes[1]); 447275970Scy c->resp_read_pipe = move_fd(blocking_pipes[2]); 448275970Scy /* 449275970Scy * wake any worker child on orderly shutdown of the 450275970Scy * daemon so that it can notice the broken pipes and 451275970Scy * go away promptly. 452275970Scy */ 453275970Scy if (!atexit_installed) { 454275970Scy atexit(&send_worker_home_atexit); 455275970Scy atexit_installed = TRUE; 456275970Scy } 457275970Scy } 458275970Scy 459298770Sdelphij#if defined(HAVE_DROPROOT) && !defined(NEED_EARLY_FORK) 460275970Scy /* defer the fork until after root is dropped */ 461275970Scy if (droproot && !root_dropped) 462275970Scy return; 463275970Scy#endif 464275970Scy if (syslog_file != NULL) 465275970Scy fflush(syslog_file); 466275970Scy fflush(stdout); 467275970Scy fflush(stderr); 468275970Scy 469310419Sdelphij /* [BUG 3050] setting SIGCHLD to SIG_IGN likely causes unwanted 470310419Sdelphij * or undefined effects. We don't do it and leave SIGCHLD alone. 471310419Sdelphij */ 472310419Sdelphij /* signal_no_reset(SIGCHLD, SIG_IGN); */ 473275970Scy 474275970Scy childpid = fork(); 475275970Scy if (-1 == childpid) { 476275970Scy msyslog(LOG_ERR, "unable to fork worker: %m"); 477275970Scy exit(1); 478275970Scy } 479275970Scy 480275970Scy if (childpid) { 481275970Scy /* this is the parent */ 482275970Scy TRACE(1, ("forked worker child (pid %d)\n", childpid)); 483275970Scy c->pid = childpid; 484275970Scy c->ispipe = is_pipe; 485275970Scy 486275970Scy /* close the child's pipe descriptors. */ 487275970Scy close(blocking_pipes[0]); 488275970Scy close(blocking_pipes[3]); 489275970Scy 490275970Scy memset(blocking_pipes, -1, sizeof(blocking_pipes)); 491275970Scy 492275970Scy /* wire into I/O loop */ 493275970Scy (*addremove_io_fd)(c->resp_read_pipe, is_pipe, FALSE); 494275970Scy 495275970Scy return; /* parent returns */ 496275970Scy } 497275970Scy 498275970Scy /* 499275970Scy * The parent gets the child pid as the return value of fork(). 500275970Scy * The child must work for it. 501275970Scy */ 502275970Scy c->pid = getpid(); 503275970Scy worker_process = TRUE; 504275970Scy 505275970Scy /* 506275970Scy * In the child, close all files except stdin, stdout, stderr, 507275970Scy * and the two child ends of the pipes. 508275970Scy */ 509275970Scy DEBUG_INSIST(-1 == c->req_read_pipe); 510275970Scy DEBUG_INSIST(-1 == c->resp_write_pipe); 511275970Scy c->req_read_pipe = blocking_pipes[0]; 512275970Scy c->resp_write_pipe = blocking_pipes[3]; 513275970Scy 514275970Scy kill_asyncio(0); 515275970Scy closelog(); 516275970Scy if (syslog_file != NULL) { 517275970Scy fclose(syslog_file); 518275970Scy syslog_file = NULL; 519275970Scy syslogit = TRUE; 520275970Scy } 521275970Scy keep_fd = max(c->req_read_pipe, c->resp_write_pipe); 522275970Scy for (fd = 3; fd < keep_fd; fd++) 523275970Scy if (fd != c->req_read_pipe && 524275970Scy fd != c->resp_write_pipe) 525275970Scy close(fd); 526275970Scy close_all_beyond(keep_fd); 527275970Scy /* 528275970Scy * We get signals from refclock serial I/O on NetBSD in the 529275970Scy * worker if we do not reset SIGIO's handler to the default. 530275970Scy * It is not conditionalized for NetBSD alone because on 531275970Scy * systems where it is not needed, it is harmless, and that 532275970Scy * allows us to handle unknown others with NetBSD behavior. 533275970Scy * [Bug 1386] 534275970Scy */ 535275970Scy#if defined(USE_SIGIO) 536275970Scy signal_no_reset(SIGIO, SIG_DFL); 537275970Scy#elif defined(USE_SIGPOLL) 538275970Scy signal_no_reset(SIGPOLL, SIG_DFL); 539275970Scy#endif 540275970Scy signal_no_reset(SIGHUP, worker_sighup); 541275970Scy init_logging("ntp_intres", 0, FALSE); 542275970Scy setup_logfile(NULL); 543275970Scy 544275970Scy /* 545275970Scy * And now back to the portable code 546275970Scy */ 547275970Scy exit_worker(blocking_child_common(c)); 548275970Scy} 549275970Scy 550275970Scy 551298770Sdelphijvoid worker_global_lock(int inOrOut) 552298770Sdelphij{ 553298770Sdelphij (void)inOrOut; 554298770Sdelphij} 555298770Sdelphij 556275970Scy#else /* !WORK_FORK follows */ 557275970Scychar work_fork_nonempty_compilation_unit; 558275970Scy#endif 559