work_fork.c revision 285612
1289694Sngie/* 2289694Sngie * work_fork.c - fork implementation for blocking worker child. 3289694Sngie */ 4289694Sngie#include <config.h> 5289694Sngie#include "ntp_workimpl.h" 6289694Sngie 7289694Sngie#ifdef WORK_FORK 8289694Sngie#include <stdio.h> 9289694Sngie#include <ctype.h> 10289694Sngie#include <signal.h> 11289694Sngie#include <sys/wait.h> 12289694Sngie 13289694Sngie#include "iosignal.h" 14289694Sngie#include "ntp_stdlib.h" 15289694Sngie#include "ntp_malloc.h" 16289694Sngie#include "ntp_syslog.h" 17289694Sngie#include "ntpd.h" 18289694Sngie#include "ntp_io.h" 19289694Sngie#include "ntp_assert.h" 20289694Sngie#include "ntp_unixtime.h" 21289694Sngie#include "ntp_worker.h" 22289694Sngie 23289694Sngie/* === variables === */ 24289694Sngie int worker_process; 25289694Sngie addremove_io_fd_func addremove_io_fd; 26289694Sngiestatic volatile int worker_sighup_received; 27289694Sngie 28289694Sngie/* === function prototypes === */ 29289694Sngiestatic void fork_blocking_child(blocking_child *); 30290586Sngiestatic RETSIGTYPE worker_sighup(int); 31290586Sngiestatic void send_worker_home_atexit(void); 32290594Sngiestatic void cleanup_after_child(blocking_child *); 33290594Sngie 34290594Sngie/* === functions === */ 35290594Sngie/* 36290594Sngie * exit_worker() 37290594Sngie * 38290594Sngie * On some systems _exit() is preferred to exit() for forked children. 39290586Sngie * For example, http://netbsd.gw.com/cgi-bin/man-cgi?fork++NetBSD-5.0 40290586Sngie * recommends _exit() to avoid double-flushing C runtime stream buffers 41290586Sngie * and also to avoid calling the parent's atexit() routines in the 42290586Sngie * child. On those systems WORKER_CHILD_EXIT is _exit. Since _exit 43290586Sngie * bypasses CRT cleanup, fflush() files we know might have output 44289694Sngie * buffered. 45290586Sngie */ 46289694Sngievoid 47290586Sngieexit_worker( 48290586Sngie int exitcode 49290594Sngie ) 50289694Sngie{ 51290586Sngie if (syslog_file != NULL) 52290586Sngie fflush(syslog_file); 53290586Sngie fflush(stdout); 54290586Sngie fflush(stderr); 55290586Sngie WORKER_CHILD_EXIT (exitcode); /* space before ( required */ 56290586Sngie} 57290586Sngie 58290586Sngie 59290586Sngiestatic RETSIGTYPE 60290586Sngieworker_sighup( 61290586Sngie int sig 62290586Sngie ) 63290586Sngie{ 64290586Sngie if (SIGHUP == sig) 65290586Sngie worker_sighup_received = 1; 66290586Sngie} 67290586Sngie 68290586Sngie 69290586Sngieint 70290586Sngieworker_sleep( 71289694Sngie blocking_child * c, 72290586Sngie time_t seconds 73290586Sngie ) 74290586Sngie{ 75290586Sngie u_int sleep_remain; 76289694Sngie 77290586Sngie sleep_remain = (u_int)seconds; 78290586Sngie do { 79290586Sngie if (!worker_sighup_received) 80290586Sngie sleep_remain = sleep(sleep_remain); 81290586Sngie if (worker_sighup_received) { 82290586Sngie TRACE(1, ("worker SIGHUP with %us left to sleep", 83289694Sngie sleep_remain)); 84290586Sngie worker_sighup_received = 0; 85290586Sngie return -1; 86290586Sngie } 87290586Sngie } while (sleep_remain); 88290586Sngie 89290586Sngie return 0; 90290586Sngie} 91290586Sngie 92290586Sngie 93290586Sngievoid 94290586Sngieinterrupt_worker_sleep(void) 95289694Sngie{ 96289694Sngie u_int idx; 97289694Sngie blocking_child * c; 98289694Sngie int rc; 99289694Sngie 100289694Sngie for (idx = 0; idx < blocking_children_alloc; idx++) { 101290586Sngie c = blocking_children[idx]; 102289694Sngie 103290586Sngie if (NULL == c || c->reusable == TRUE) 104290586Sngie continue; 105289694Sngie 106289694Sngie rc = kill(c->pid, SIGHUP); 107289694Sngie if (rc < 0) 108289694Sngie msyslog(LOG_ERR, 109289694Sngie "Unable to signal HUP to wake child pid %d: %m", 110289694Sngie c->pid); 111290586Sngie } 112290586Sngie} 113290586Sngie 114289694Sngie 115289694Sngie/* 116289694Sngie * harvest_child_status() runs in the parent. 117289694Sngie */ 118289694Sngiestatic void 119289694Sngieharvest_child_status( 120289694Sngie blocking_child * c 121289694Sngie ) 122289694Sngie{ 123289694Sngie if (c->pid) 124289694Sngie { 125289694Sngie /* Wait on the child so it can finish terminating */ 126289694Sngie if (waitpid(c->pid, NULL, 0) == c->pid) 127289694Sngie TRACE(4, ("harvested child %d\n", c->pid)); 128289694Sngie else msyslog(LOG_ERR, "error waiting on child %d: %m", c->pid); 129289694Sngie } 130289694Sngie} 131289694Sngie 132289694Sngie/* 133290586Sngie * req_child_exit() runs in the parent. 134290586Sngie */ 135290586Sngieint 136290586Sngiereq_child_exit( 137290586Sngie blocking_child * c 138290586Sngie ) 139290586Sngie{ 140290586Sngie if (-1 != c->req_write_pipe) { 141290586Sngie close(c->req_write_pipe); 142290586Sngie c->req_write_pipe = -1; 143289694Sngie return 0; 144290586Sngie } 145290586Sngie /* Closing the pipe forces the child to exit */ 146290586Sngie harvest_child_status(c); 147290586Sngie return -1; 148290586Sngie} 149290586Sngie 150290586Sngie 151290586Sngie/* 152290586Sngie * cleanup_after_child() runs in parent. 153 */ 154static void 155cleanup_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 173static void 174send_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 191int 192send_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 235blocking_pipe_header * 236receive_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 293int 294send_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 321blocking_pipe_header * 322receive_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) 375void 376fork_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 394static void 395fork_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 */ 544char work_fork_nonempty_compilation_unit; 545#endif 546