s6-supervise.c (19496B)
1 /* ISC license. */ 2 3 /* For SIGWINCH */ 4 #include <skalibs/nonposix.h> 5 6 #include <stdint.h> 7 #include <unistd.h> 8 #include <string.h> 9 #include <strings.h> 10 #include <errno.h> 11 #include <limits.h> 12 #include <signal.h> 13 #include <fcntl.h> 14 #include <sys/resource.h> 15 #include <sys/stat.h> 16 #include <sys/wait.h> 17 18 #include <skalibs/posixplz.h> 19 #include <skalibs/allreadwrite.h> 20 #include <skalibs/bytestr.h> 21 #include <skalibs/types.h> 22 #include <skalibs/strerr.h> 23 #include <skalibs/tai.h> 24 #include <skalibs/iopause.h> 25 #include <skalibs/cspawn.h> 26 #include <skalibs/djbunix.h> 27 #include <skalibs/sig.h> 28 #include <skalibs/selfpipe.h> 29 #include <skalibs/skamisc.h> 30 31 #include <s6/config.h> 32 #include <s6/ftrigw.h> 33 #include <s6/supervise.h> 34 35 #define USAGE "s6-supervise dir" 36 #define CTL S6_SUPERVISE_CTLDIR "/control" 37 #define LCK S6_SUPERVISE_CTLDIR "/lock" 38 #define SLCK S6_SUPERVISE_CTLDIR "/service-lock" 39 40 #define S6_PATH_MAX 512 41 42 typedef enum trans_e trans_t, *trans_t_ref ; 43 enum trans_e 44 { 45 V_TIMEOUT, V_CHLD, V_TERM, V_HUP, V_QUIT, V_INT, 46 V_a, V_b, V_q, V_h, V_k, V_t, V_i, V_1, V_2, V_p, V_c, V_y, V_r, 47 V_o, V_d, V_u, V_D, V_U, V_x, V_O, V_Q 48 } ; 49 50 typedef enum state_e state_t, *state_t_ref ; 51 enum state_e 52 { 53 DOWN, 54 UP, 55 FINISH, 56 LASTUP, 57 LASTFINISH 58 } ; 59 60 struct gflags_s 61 { 62 uint8_t cont : 1 ; 63 uint8_t dying : 1 ; 64 } gflags = 65 { 66 .cont = 1, 67 .dying = 0 68 } ; 69 70 typedef void action_t (void) ; 71 typedef action_t *action_t_ref ; 72 73 static tain deadline ; 74 static tain nextstart = TAIN_ZERO ; 75 static s6_svstatus_t status = S6_SVSTATUS_ZERO ; 76 static state_t state = DOWN ; 77 static int notifyfd = -1 ; 78 static char const *servicename = 0 ; 79 static rlim_t maxfd ; 80 81 static inline void settimeout (int secs) 82 { 83 tain_addsec_g(&deadline, secs) ; 84 } 85 86 static inline void settimeout_infinite (void) 87 { 88 tain_add_g(&deadline, &tain_infinite_relative) ; 89 } 90 91 static inline void announce (void) 92 { 93 if (!s6_svstatus_write(".", &status)) 94 strerr_warnwu1sys("write status file") ; 95 } 96 97 static int read_file (char const *file, char *buf, size_t n) 98 { 99 ssize_t r = openreadnclose_nb(file, buf, n) ; 100 if (r == -1) 101 { 102 if (errno != ENOENT) strerr_warnwu2sys("open ", file) ; 103 return 0 ; 104 } 105 buf[byte_chr(buf, r, '\n')] = 0 ; 106 return 1 ; 107 } 108 109 static int read_uint (char const *file, unsigned int *fd) 110 { 111 char buf[UINT_FMT + 1] ; 112 if (!read_file(file, buf, UINT_FMT)) return 0 ; 113 if (!uint0_scan(buf, fd)) 114 { 115 strerr_warnw2x("invalid ", file) ; 116 return 0 ; 117 } 118 return 1 ; 119 } 120 121 static inline int read_downsig (void) 122 { 123 int sig = SIGTERM ; 124 char buf[16] ; 125 if (read_file("down-signal", buf, 15) && !sig0_scan(buf, &sig)) 126 strerr_warnw1x("invalid down-signal") ; 127 return sig ; 128 } 129 130 static void set_down_and_ready (char const *s, unsigned int n) 131 { 132 status.pid = 0 ; 133 status.flagfinishing = 0 ; 134 status.flagready = 1 ; 135 state = DOWN ; 136 if (tai_sec(tain_secp(&nextstart))) deadline = nextstart ; 137 else tain_addsec_g(&deadline, 1) ; 138 tain_wallclock_read(&status.readystamp) ; 139 announce() ; 140 ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, s, n) ; 141 } 142 143 144 /* The action array. */ 145 146 static void nop (void) 147 { 148 } 149 150 static void bail (void) 151 { 152 gflags.cont = 0 ; 153 } 154 155 static void sigint (void) 156 { 157 pid_t pgid = getpgid(status.pid) ; 158 if (pgid == -1) strerr_warnwu1sys("getpgid") ; 159 else killpg(pgid, SIGINT) ; 160 bail() ; 161 } 162 163 static void closethem (void) 164 { 165 fd_close(0) ; 166 fd_close(1) ; 167 if (open_readb("/dev/null")) 168 strerr_warnwu2sys("open /dev/null for ", "reading") ; 169 else if (open_write("/dev/null") != 1 || ndelay_off(1) < 0) 170 strerr_warnwu2sys("open /dev/null for ", "writing") ; 171 } 172 173 static void adddown (void) 174 { 175 if (!openwritenclose_unsafe("down", "", 0)) 176 strerr_warnwu2sys("create ", "./down file") ; 177 } 178 179 static void deldown (void) 180 { 181 int e = errno ; 182 if (unlink("down") == -1 && errno != ENOENT) 183 strerr_warnwu2sys("unlink ", "./down file") ; 184 errno = e ; 185 } 186 187 static void killa (void) 188 { 189 kill(status.pid, SIGALRM) ; 190 } 191 192 static void killb (void) 193 { 194 kill(status.pid, SIGABRT) ; 195 } 196 197 static void killh (void) 198 { 199 kill(status.pid, SIGHUP) ; 200 } 201 202 static void killq (void) 203 { 204 kill(status.pid, SIGQUIT) ; 205 } 206 207 static void killk (void) 208 { 209 kill(status.pid, SIGKILL) ; 210 } 211 212 static void killt (void) 213 { 214 kill(status.pid, SIGTERM) ; 215 } 216 217 static void killi (void) 218 { 219 kill(status.pid, SIGINT) ; 220 } 221 222 static void kill1 (void) 223 { 224 kill(status.pid, SIGUSR1) ; 225 } 226 227 static void kill2 (void) 228 { 229 kill(status.pid, SIGUSR2) ; 230 } 231 232 static void killp (void) 233 { 234 kill(status.pid, SIGSTOP) ; 235 status.flagpaused = 1 ; 236 announce() ; 237 } 238 239 static void killc (void) 240 { 241 kill(status.pid, SIGCONT) ; 242 status.flagpaused = 0 ; 243 announce() ; 244 } 245 246 static void killy (void) 247 { 248 kill(status.pid, SIGWINCH) ; 249 } 250 251 static void killr (void) 252 { 253 kill(status.pid, read_downsig()) ; 254 } 255 256 static void trystart (void) 257 { 258 cspawn_fileaction fa[2] = 259 { 260 [0] = { .type = CSPAWN_FA_CLOSE }, 261 [1] = { .type = CSPAWN_FA_MOVE }, 262 } ; 263 char lkfmt[UINT_FMT] ; 264 char const *cargv[7] = { S6_BINPREFIX "s6-setlock", "-d", lkfmt, "--", "./run", servicename, 0 } ; 265 size_t orig = 4 ; 266 int notifyp[2] = { -1, -1 } ; 267 unsigned int lk = 0, notif = 0 ; 268 269 if (read_uint("lock-fd", &lk)) 270 { 271 if (lk > maxfd) strerr_warnw2x("invalid ", "lock-fd") ; 272 else 273 { 274 struct stat st ; 275 int islocked ; 276 int lfd = open_write(SLCK) ; 277 if (lfd == -1) 278 { 279 settimeout(60) ; 280 strerr_warnwu4sys("open ", SLCK, " for writing", " (waiting 60 seconds)") ; 281 goto errn ; 282 } 283 if (fstat(lfd, &st) == -1) 284 { 285 settimeout(60) ; 286 strerr_warnwu3sys("stat ", SLCK, " (waiting 60 seconds)") ; 287 fd_close(lfd) ; 288 return ; 289 } 290 if (st.st_size) 291 { 292 ftruncate(lfd, 0) ; 293 strerr_warnw1x("a previous instance of the service wrote to the lock file!") ; 294 } 295 islocked = fd_islocked(lfd) ; 296 if (islocked == -1) 297 { 298 settimeout(60) ; 299 strerr_warnwu3sys("read lock state on ", SLCK, " (waiting 60 seconds)") ; 300 fd_close(lfd) ; 301 return ; 302 } 303 if (islocked) 304 strerr_warnw1x("another instance of the service is already running, child will block") ; 305 fd_close(lfd) ; 306 lkfmt[uint_fmt(lkfmt, lk)] = 0 ; 307 orig = 0 ; 308 } 309 } 310 311 if (read_uint("notification-fd", ¬if)) 312 { 313 if (notif > maxfd) strerr_warnw2x("invalid ", "notification-fd") ; 314 if (!orig && notif == lk) 315 { 316 settimeout_infinite() ; 317 strerr_warnwu1x("start service: notification-fd and lock-fd are the same") ; 318 return ; 319 } 320 if (pipe(notifyp) == -1) 321 { 322 settimeout(60) ; 323 strerr_warnwu2sys("create notification pipe", " (waiting 60 seconds)") ; 324 return ; 325 } 326 fa[0].x.fd = notifyp[0] ; 327 fa[1].x.fd2[0] = notif ; 328 fa[1].x.fd2[1] = notifyp[1] ; 329 } 330 331 uint16_t cspawn_flags = CSPAWN_FLAGS_SELFPIPE_FINISH | CSPAWN_FLAGS_SETSID; 332 if (access("clone-newpid", F_OK) == 0) 333 { 334 cspawn_flags |= CSPAWN_FLAGS_LINUX_NEWPID; 335 } 336 else if(errno != ENOENT) { 337 strerr_warnwu1sys("check for clone-newpid file") ; 338 return ; 339 /* TODO: Close stuff from above? Exit instead? */ 340 } 341 342 status.pid = cspawn(cargv[orig], cargv + orig, (char const *const *)environ, cspawn_flags, fa, notifyp[1] >= 0 ? 2 : 0) ; 343 if (!status.pid) 344 { 345 settimeout(60) ; 346 strerr_warnwu3sys("spawn ", cargv[orig], " (waiting 60 seconds)") ; 347 goto errn ; 348 } 349 350 if (notifyp[1] >= 0) 351 { 352 fd_close(notifyp[1]) ; 353 notifyfd = notifyp[0] ; 354 } 355 settimeout_infinite() ; 356 nextstart = tain_zero ; 357 state = UP ; 358 status.flagready = 0 ; 359 tain_wallclock_read(&status.stamp) ; 360 announce() ; 361 ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "u", 1) ; 362 return ; 363 364 errn: 365 if (notifyp[1] >= 0) 366 { 367 fd_close(notifyp[1]) ; 368 fd_close(notifyp[0]) ; 369 } 370 } 371 372 static void wantdown (void) 373 { 374 status.flagwantup = 0 ; 375 announce() ; 376 } 377 378 static void wantup (void) 379 { 380 status.flagwantup = 1 ; 381 announce() ; 382 } 383 384 static void wantDOWN (void) 385 { 386 adddown() ; 387 wantdown() ; 388 } 389 390 static void wantUP (void) 391 { 392 deldown() ; 393 wantup() ; 394 } 395 396 static void downtimeout (void) 397 { 398 if (status.flagwantup) trystart() ; 399 else settimeout_infinite() ; 400 } 401 402 static void down_o (void) 403 { 404 wantdown() ; 405 trystart() ; 406 } 407 408 static void down_u (void) 409 { 410 wantup() ; 411 trystart() ; 412 } 413 414 static void down_U (void) 415 { 416 wantUP() ; 417 trystart() ; 418 } 419 420 static int uplastup_z (void) 421 { 422 unsigned int n ; 423 char fmt0[UINT_FMT] ; 424 char fmt1[UINT_FMT] ; 425 char const *cargv[5] = { "finish", fmt0, fmt1, servicename, 0 } ; 426 427 status.wstat = (int)status.pid ; 428 status.flagpaused = 0 ; 429 status.flagready = 0 ; 430 gflags.dying = 0 ; 431 tain_wallclock_read(&status.stamp) ; 432 if (notifyfd >= 0) 433 { 434 fd_close(notifyfd) ; 435 notifyfd = -1 ; 436 } 437 fmt0[uint_fmt(fmt0, WIFSIGNALED(status.wstat) ? 256 : WEXITSTATUS(status.wstat))] = 0 ; 438 fmt1[uint_fmt(fmt1, WTERMSIG(status.wstat))] = 0 ; 439 440 if (!read_uint("max-death-tally", &n)) n = 100 ; 441 if (n > S6_MAX_DEATH_TALLY) n = S6_MAX_DEATH_TALLY ; 442 if (n) 443 { 444 s6_dtally_t tab[n+1] ; 445 ssize_t m = s6_dtally_read(".", tab, n) ; 446 if (m < 0) strerr_warnwu2sys("read ", S6_DTALLY_FILENAME) ; 447 else 448 { 449 tab[m].stamp = status.stamp ; 450 tab[m].sig = WIFSIGNALED(status.wstat) ? WTERMSIG(status.wstat) : 0 ; 451 tab[m].exitcode = WIFSIGNALED(status.wstat) ? 128 + WTERMSIG(status.wstat) : WEXITSTATUS(status.wstat) ; 452 if (!(m >= n ? s6_dtally_write(".", tab+1, n) : s6_dtally_write(".", tab, m+1))) 453 strerr_warnwu2sys("write ", S6_DTALLY_FILENAME) ; 454 } 455 } 456 457 status.pid = cspawn("./finish", cargv, (char const *const *)environ, CSPAWN_FLAGS_SELFPIPE_FINISH | CSPAWN_FLAGS_SETSID, 0, 0) ; 458 if (!status.pid) 459 { 460 if (errno != ENOENT) strerr_warnwu2sys("spawn ", "./finish") ; 461 set_down_and_ready("dD", 2) ; 462 return 0 ; 463 } 464 { 465 tain tto ; 466 unsigned int timeout ; 467 if (!read_uint("timeout-finish", &timeout)) timeout = 5000 ; 468 if (timeout && tain_from_millisecs(&tto, timeout)) 469 tain_add_g(&deadline, &tto) ; 470 else settimeout_infinite() ; 471 } 472 status.flagfinishing = 1 ; 473 announce() ; 474 ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "d", 1) ; 475 return 1 ; 476 } 477 478 static void up_z (void) 479 { 480 if (uplastup_z()) state = FINISH ; 481 } 482 483 static void lastup_z (void) 484 { 485 if (uplastup_z()) state = LASTFINISH ; 486 else bail() ; 487 } 488 489 static void uptimeout (void) 490 { 491 if (gflags.dying) 492 { 493 killk() ; 494 settimeout(5) ; 495 } 496 else 497 { 498 settimeout_infinite() ; 499 strerr_warnw1x("can't happen: timeout while the service is up!") ; 500 } 501 } 502 503 static void up_d (void) 504 { 505 tain tto ; 506 unsigned int timeout ; 507 status.flagwantup = 0 ; 508 killr() ; 509 killc() ; 510 if (!read_uint("timeout-kill", &timeout)) timeout = 0 ; 511 if (timeout && tain_from_millisecs(&tto, timeout)) 512 { 513 tain_add_g(&deadline, &tto) ; 514 gflags.dying = 1 ; 515 } 516 else settimeout_infinite() ; 517 } 518 519 static void up_D (void) 520 { 521 adddown() ; 522 up_d() ; 523 } 524 525 static void up_x (void) 526 { 527 state = LASTUP ; 528 closethem() ; 529 } 530 531 static void up_term (void) 532 { 533 state = LASTUP ; 534 up_d() ; 535 } 536 537 static void finishtimeout (void) 538 { 539 strerr_warnw1x("finish script lifetime reached maximum value - sending it a SIGKILL") ; 540 killc() ; killk() ; 541 settimeout(5) ; 542 } 543 544 static void finish_z (void) 545 { 546 int wstat = (int)status.pid ; 547 if (WIFEXITED(wstat) && WEXITSTATUS(wstat) == 125) 548 { 549 status.flagwantup = 0 ; 550 set_down_and_ready("OD", 2) ; 551 } 552 else set_down_and_ready("D", 1) ; 553 } 554 555 static void finish_x (void) 556 { 557 state = LASTFINISH ; 558 closethem() ; 559 } 560 561 static void lastfinish_z (void) 562 { 563 finish_z() ; 564 bail() ; 565 } 566 567 static action_t_ref const actions[5][27] = 568 { 569 { &downtimeout, &nop, &bail, &bail, &bail, &bail, 570 &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, 571 &down_o, &wantdown, &down_u, &wantDOWN, &down_U, &bail, &wantdown, &wantDOWN }, 572 { &uptimeout, &up_z, &up_term, &up_x, &bail, &sigint, 573 &killa, &killb, &killq, &killh, &killk, &killt, &killi, &kill1, &kill2, &killp, &killc, &killy, &killr, 574 &wantdown, &up_d, &wantup, &up_D, &wantUP, &up_x, &wantdown, &wantDOWN }, 575 { &finishtimeout, &finish_z, &finish_x, &finish_x, &bail, &sigint, 576 &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, 577 &wantdown, &wantdown, &wantup, &wantDOWN, &wantUP, &finish_x, &wantdown, &wantDOWN }, 578 { &uptimeout, &lastup_z, &up_d, &closethem, &bail, &sigint, 579 &killa, &killb, &killq, &killh, &killk, &killt, &killi, &kill1, &kill2, &killp, &killc, &killy, &killr, 580 &wantdown, &up_d, &wantup, &up_D, &wantUP, &closethem, &wantdown, &wantDOWN }, 581 { &finishtimeout, &lastfinish_z, &nop, &closethem, &bail, &sigint, 582 &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, 583 &wantdown, &wantdown, &wantup, &wantDOWN, &wantUP, &closethem, &wantdown, &wantDOWN } 584 } ; 585 586 587 588 /* The main loop. 589 It just loops around the iopause(), calling snippets of code in "actions" when needed. */ 590 591 592 static inline void handle_notifyfd (void) 593 { 594 char buf[512] ; 595 ssize_t r = 1 ; 596 while (r > 0) 597 { 598 r = sanitize_read(fd_read(notifyfd, buf, 512)) ; 599 if (r > 0 && memchr(buf, '\n', r)) 600 { 601 tain_addsec_g(&nextstart, 1) ; 602 tain_wallclock_read(&status.readystamp) ; 603 status.flagready = 1 ; 604 announce() ; 605 ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "U", 1) ; 606 r = -1 ; 607 } 608 if (r < 0) 609 { 610 fd_close(notifyfd) ; 611 notifyfd = -1 ; 612 } 613 } 614 } 615 616 static inline void handle_signals (void) 617 { 618 for (;;) 619 { 620 switch (selfpipe_read()) 621 { 622 case -1 : strerr_diefu1sys(111, "selfpipe_read") ; 623 case 0 : return ; 624 case SIGCHLD : 625 if (!status.pid) wait_reap() ; 626 else 627 { 628 int wstat ; 629 int r = wait_pid_nohang(status.pid, &wstat) ; 630 if (r < 0) 631 if (errno != ECHILD) strerr_diefu1sys(111, "wait_pid_nohang") ; 632 else break ; 633 else if (!r) break ; 634 status.pid = (pid_t)wstat ; /* don't overwrite status.wstat if it's ./finish */ 635 (*actions[state][V_CHLD])() ; 636 } 637 break ; 638 case SIGTERM : 639 (*actions[state][V_TERM])() ; 640 break ; 641 case SIGHUP : 642 (*actions[state][V_HUP])() ; 643 break ; 644 case SIGQUIT : 645 (*actions[state][V_QUIT])() ; 646 break ; 647 case SIGINT : 648 (*actions[state][V_INT])() ; 649 break ; 650 default : 651 strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ; 652 } 653 } 654 } 655 656 static inline void handle_control (int fd) 657 { 658 for (;;) 659 { 660 char c ; 661 ssize_t r = sanitize_read(fd_read(fd, &c, 1)) ; 662 if (r < 0) strerr_diefu1sys(111, "read " S6_SUPERVISE_CTLDIR "/control") ; 663 else if (!r) break ; 664 else 665 { 666 size_t pos = byte_chr("abqhkti12pcyroduDUxOQ", 21, c) ; 667 if (pos < 21) (*actions[state][V_a + pos])() ; 668 } 669 } 670 } 671 672 static int trymkdir (char const *s) 673 { 674 char buf[S6_PATH_MAX] ; 675 ssize_t r ; 676 if (mkdir(s, 0700) >= 0) return 1 ; 677 if (errno != EEXIST) strerr_diefu2sys(111, "mkdir ", s) ; 678 r = readlink(s, buf, S6_PATH_MAX) ; 679 if (r < 0) 680 { 681 struct stat st ; 682 if (errno != EINVAL) 683 { 684 errno = EEXIST ; 685 strerr_diefu2sys(111, "mkdir ", s) ; 686 } 687 if (stat(s, &st) < 0) 688 strerr_diefu2sys(111, "stat ", s) ; 689 if (!S_ISDIR(st.st_mode)) 690 strerr_dief2x(100, s, " exists and is not a directory") ; 691 return 0 ; 692 } 693 else if (r == S6_PATH_MAX) 694 { 695 errno = ENAMETOOLONG ; 696 strerr_diefu2sys(111, "readlink ", s) ; 697 } 698 else 699 { 700 buf[r] = 0 ; 701 if (mkdir(buf, 0700) < 0) 702 strerr_diefu2sys(111, "mkdir ", buf) ; 703 return 1 ; 704 } 705 } 706 707 static inline int control_init (void) 708 { 709 mode_t m = umask(0) ; 710 int fdctl, fdlck, r ; 711 if (trymkdir(S6_SUPERVISE_EVENTDIR)) 712 { 713 if (chown(S6_SUPERVISE_EVENTDIR, -1, getegid()) < 0) 714 strerr_diefu1sys(111, "chown " S6_SUPERVISE_EVENTDIR) ; 715 if (chmod(S6_SUPERVISE_EVENTDIR, 03730) < 0) 716 strerr_diefu1sys(111, "chmod " S6_SUPERVISE_EVENTDIR) ; 717 } 718 719 trymkdir(S6_SUPERVISE_CTLDIR) ; 720 fdlck = open3(LCK, O_WRONLY | O_NONBLOCK | O_CREAT | O_CLOEXEC, 0644) ; 721 if (fdlck < 0) strerr_diefu1sys(111, "open " LCK) ; 722 r = fd_lock(fdlck, 1, 1) ; 723 if (r < 0) strerr_diefu1sys(111, "lock " LCK) ; 724 if (!r) strerr_dief1x(100, "another instance of s6-supervise is already running") ; 725 /* fdlck leaks but it's coe */ 726 727 if (mkfifo(CTL, 0600) < 0) 728 { 729 struct stat st ; 730 if (errno != EEXIST) 731 strerr_diefu1sys(111, "mkfifo " CTL) ; 732 if (stat(CTL, &st) < 0) 733 strerr_diefu1sys(111, "stat " CTL) ; 734 if (!S_ISFIFO(st.st_mode)) 735 strerr_dief1x(100, CTL " is not a FIFO") ; 736 } 737 fdctl = openc_read(CTL) ; 738 if (fdctl < 0) 739 strerr_diefu1sys(111, "open " CTL " for reading") ; 740 r = openc_write(CTL) ; 741 if (r < 0) 742 strerr_diefu1sys(111, "open " CTL " for writing") ; 743 /* r leaks but it's coe */ 744 745 umask(m) ; 746 return fdctl ; 747 } 748 749 int main (int argc, char const *const *argv) 750 { 751 iopause_fd x[3] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ; 752 PROG = "s6-supervise" ; 753 if (argc < 2) strerr_dieusage(100, USAGE) ; 754 if (chdir(argv[1]) < 0) strerr_diefu2sys(111, "chdir to ", argv[1]) ; 755 servicename = argv[1] ; 756 { 757 size_t proglen = strlen(PROG) ; 758 size_t namelen = strlen(argv[1]) ; 759 char progname[proglen + namelen + 10] ; 760 memcpy(progname, PROG, proglen) ; 761 progname[proglen] = ' ' ; 762 memcpy(progname + proglen + 1, argv[1], namelen + 1) ; 763 memcpy(progname + proglen + 2 + namelen, "(child)", 8) ; 764 PROG = progname ; 765 if (!fd_sanitize()) strerr_diefu1sys(111, "sanitize stdin and stdout") ; 766 { 767 struct rlimit rl ; 768 if (getrlimit(RLIMIT_NOFILE, &rl) == -1) 769 strerr_diefu1sys(111, "getrlimit") ; 770 maxfd = rl.rlim_cur ; 771 } 772 x[1].fd = control_init() ; 773 x[0].fd = selfpipe_init() ; 774 if (x[0].fd == -1) strerr_diefu1sys(111, "init selfpipe") ; 775 if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; 776 { 777 sigset_t set ; 778 sigemptyset(&set) ; 779 sigaddset(&set, SIGCHLD) ; 780 sigaddset(&set, SIGTERM) ; 781 sigaddset(&set, SIGHUP) ; 782 sigaddset(&set, SIGQUIT) ; 783 sigaddset(&set, SIGINT) ; 784 if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ; 785 } 786 787 if (!ftrigw_clean(S6_SUPERVISE_EVENTDIR)) 788 strerr_warnwu2sys("ftrigw_clean ", S6_SUPERVISE_EVENTDIR) ; 789 { 790 int fd = open_trunc(S6_DTALLY_FILENAME) ; 791 if (fd < 0) strerr_diefu2sys(111, "truncate ", S6_DTALLY_FILENAME) ; 792 fd_close(fd) ; 793 } 794 795 if (access("down", F_OK) == 0) status.flagwantup = 0 ; 796 else if (errno != ENOENT) 797 strerr_diefu1sys(111, "access ./down") ; 798 799 tain_now_set_stopwatch_g() ; 800 settimeout(0) ; 801 tain_copynow(&status.stamp) ; 802 status.readystamp = status.stamp ; 803 announce() ; 804 ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "s", 1) ; 805 806 while (gflags.cont) 807 { 808 int r ; 809 x[2].fd = notifyfd ; 810 r = iopause_g(x, 2 + (notifyfd >= 0), &deadline) ; 811 if (r < 0) strerr_diefu1sys(111, "iopause") ; 812 else if (!r) (*actions[state][V_TIMEOUT])() ; 813 else 814 { 815 if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) 816 strerr_diefu1x(111, "iopause: trouble with pipes") ; 817 if (notifyfd >= 0 && x[2].revents & IOPAUSE_READ) handle_notifyfd() ; 818 if (x[0].revents & IOPAUSE_READ) handle_signals() ; 819 else if (x[1].revents & IOPAUSE_READ) handle_control(x[1].fd) ; 820 } 821 } 822 823 ftrigw_notifyb_nosig(S6_SUPERVISE_EVENTDIR, "x", 1) ; 824 } 825 return 0 ; 826 }