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