/* accept4 */ #define _GNU_SOURCE #include #include #include #include #include #ifdef __linux__ #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_SV 512 // ## not truly portable :/ #define LOG(fmt, ...) dprintf(selflogfd[1], fmt "\n", ## __VA_ARGS__); enum { UNDEF, UP, DOWN, }; struct entry { char level; char name[62]; char logged; pid_t pid; pid_t display_pid; // not reset to 0, needed for oneshots int logfd[2]; time_t start; int status; char state; char seen; }; struct entry services[MAX_SV]; pthread_mutex_t services_lock; int level; pthread_t main_thread; pthread_t socket_thread; pthread_t logger_thread; int selfpipe[2]; int selflogfd[2]; int newlogfd[2]; int globallogfd[2]; int nullfd; int pid1; int real_pid1; int use_global_log; volatile sig_atomic_t want_shutdown; int want_rescan; int want_rescandir; void on_signal(int sig) { switch (sig) { case SIGINT: if (real_pid1) want_shutdown = 2; /* Linux Ctrl-Alt-Delete */ else want_shutdown = 1; break; case SIGTERM: want_shutdown = 1; break; case SIGCONT: /* do nothing, but interrupt the system call */ break; case SIGCHLD: write(selfpipe[1], "", 1); break; } } pid_t timedwait(int *wstatus, int secs) { struct pollfd fds[1]; fds[0].fd = selfpipe[0]; fds[0].events = POLLIN; pid_t pid = waitpid(-1, wstatus, WNOHANG); if (pid > 0) return pid; if (pid < 0 && errno == ECHILD) return -1; while (1) { int n = poll(fds, 1, secs < 0 ? -1 : secs * 1000); if (n < 0 && errno == EINTR) return -1; if (n == 0) // timeout return 0; if (n == 1) { char trash; read(selfpipe[0], &trash, 1); pid = waitpid(-1, wstatus, WNOHANG); if (pid < 0) { if (errno == ECHILD) return -1; if (errno == EINTR) return -1; } if (pid > 0) return pid; } } } void restart(int i) { if (services[i].pid > 0) return; if (services[i].state != UP) return; int delay = 0; time_t now = time(0); if (services[i].start == now) delay = 1; int loggerpipe[2] = { -1, -1 }; LOG("start %s", services[i].name); if (services[i].logfd[0] == -1) { pipe(services[i].logfd); fcntl(services[i].logfd[0], F_SETFL, O_NONBLOCK); fcntl(services[i].logfd[0], F_SETFD, FD_CLOEXEC); fcntl(services[i].logfd[1], F_SETFD, FD_CLOEXEC); write(newlogfd[1], "!", 1); } if (services[i].name[2] == 'L') { for (int j = 0; j < MAX_SV; j++) { if (strcmp(services[i].name + 3, services[j].name + 3) == 0 && services[j].name[2] == 'D') { if (services[j].logfd[0] == -1) { // hook up logger pipe(loggerpipe); fcntl(loggerpipe[0], F_SETFD, FD_CLOEXEC); fcntl(loggerpipe[1], F_SETFD, FD_CLOEXEC); services[j].logfd[0] = loggerpipe[0]; services[j].logfd[1] = loggerpipe[1]; services[j].logged = 1; } else { // recover loggerpipe loggerpipe[0] = services[j].logfd[0]; loggerpipe[1] = services[j].logfd[1]; } break; } } } if (services[i].name[2] == 'G') { // enable global logger use_global_log = 1; LOG("enabling global logger %s", services[i].name); } pid_t child = fork(); if (child == 0) { // note that all dup2 below will remove CLOEXEC if (services[i].name[2] == 'L') { // loggers get read end of loggerpipe dup2(loggerpipe[0], 0); } if (services[i].name[2] == 'D') { // daemons get /dev/null as stdin dup2(nullfd, 0); } if (services[i].name[2] == 'G') { // global loggers get read end of global log pipe dup2(globallogfd[0], 0); // global loggers write to stderr, to avoid loops dup2(2, 1); } else { // pass write end of logger pipe to capture stdout dup2(services[i].logfd[1], 1); } sleep(delay); setsid(); if (services[i].name[2] == 'S' && pid1) ioctl(0, TIOCSCTTY, 0); execl(services[i].name, services[i].name, (char *)0); exit(-1); } else if (child > 0) { services[i].pid = services[i].display_pid = child; services[i].start = now; } else { abort(); } } void cleanup(int i) { /* for oneshots, we don't need the pipe after exit */ if (services[i].logfd[1] > 0) close(services[i].logfd[1]); } int charsig(char c) { switch(c) { case 'p': return SIGSTOP; case 'c': return SIGCONT; case 'h': return SIGHUP; case 'a': return SIGALRM; case 'i': return SIGINT; case 'q': return SIGQUIT; case '1': return SIGUSR1; case '2': return SIGUSR2; case 't': return SIGTERM; case 'k': return SIGKILL; } return 0; } void * socket_loop(void* ignored) { (void)ignored; static const char default_sock[] = "/run/rvnit/rvnit.sock"; const char *path = getenv("RVNIT_SOCK"); if (!path || !*path) path = default_sock; char *last_slash = strrchr(path, '/'); if (last_slash) { char dir[PATH_MAX]; memcpy(dir, path, last_slash - path); dir[last_slash - path] = 0; mkdir(dir, 0700); // ignore errors } struct sockaddr_un addr = { 0 }; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path, sizeof addr.sun_path - 1); int listenfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (listenfd < 0) { perror("rvnit: socket"); exit(111); } unlink(path); mode_t mask = umask(0077); int r = bind(listenfd, (struct sockaddr *)&addr, sizeof addr); umask(mask); if (r < 0) { perror("rvnit: bind"); exit(111); } r = listen(listenfd, SOMAXCONN); if (r < 0) { perror("rvnit: listen"); exit(111); } while (1) { int connfd = accept4(listenfd, 0, 0, SOCK_CLOEXEC); if (connfd < 0) continue; struct pollfd fds[1]; fds[0].fd = connfd; fds[0].events = POLLIN; int n = poll(fds, 1, 7000); if (n != 1) { close(connfd); continue; } char buf[63]; int rd = read(connfd, buf, sizeof buf - 1); if (rd <= 0) { close(connfd); continue; } char cmd = buf[0]; if (cmd == 'l') { dprintf(connfd, "level=%d\n", level); } if (cmd == 's') { time_t now = time(0); pthread_mutex_lock(&services_lock); for (int i = 0; i < MAX_SV; i++) { if (services[i].name[2] == 'D' || services[i].name[2] == 'G' || services[i].name[2] == 'L') dprintf(connfd, "%-25s pid=%d state=%s uptime=%ld status=%d\n", services[i].name, services[i].pid, services[i].state == UP ? "UP" : "DOWN", services[i].pid > 0 ? (long)(now - services[i].start) : 0, services[i].status); } pthread_mutex_unlock(&services_lock); } if ((cmd == 'd' || cmd == 'u' || charsig(cmd)) && rd > 1) { buf[rd] = 0; pthread_mutex_lock(&services_lock); for (int i = 0; i < MAX_SV; i++) { if (strcmp(services[i].name, buf+1) == 0) { write(connfd, "ok\n", 3); if (cmd == 'd') { LOG("setting %s down", services[i].name); services[i].state = DOWN; want_rescan = 1; pthread_kill(main_thread, SIGCONT); } else if (cmd == 'u') { LOG("setting %s up", services[i].name); services[i].state = UP; want_rescan = 1; pthread_kill(main_thread, SIGCONT); } else if (charsig(cmd) && services[i].pid > 0) { LOG("sending signal %d to %s", charsig(cmd), services[i].name); kill(services[i].pid, charsig(cmd)); } } } pthread_mutex_unlock(&services_lock); } if (cmd == 'r') { write(connfd, "ok\n", 3); pthread_mutex_lock(&services_lock); want_rescandir = 1; want_rescan = 1; pthread_mutex_unlock(&services_lock); pthread_kill(main_thread, SIGCONT); } if (cmd == 'S') { write(connfd, "ok\n", 3); pthread_mutex_lock(&services_lock); want_shutdown = 1; pthread_mutex_unlock(&services_lock); pthread_kill(main_thread, SIGCONT); } if (cmd == 'R') { write(connfd, "ok\n", 3); pthread_mutex_lock(&services_lock); want_shutdown = 2; pthread_mutex_unlock(&services_lock); pthread_kill(main_thread, SIGCONT); } close(connfd); } return 0; } void * logger_loop(void* ignored) { (void)ignored; while (1) { struct pollfd fds[MAX_SV]; nfds_t nfds = 0; fds[nfds].fd = newlogfd[0]; fds[nfds].events = POLLIN; nfds++; fds[nfds].fd = selflogfd[0]; fds[nfds].events = POLLIN; nfds++; pthread_mutex_lock(&services_lock); for (int i = 0; i < MAX_SV; i++) { if (!services[i].logged && services[i].logfd[0] > 0) { fds[nfds].fd = services[i].logfd[0]; fds[nfds].events = POLLIN; nfds++; } } pthread_mutex_unlock(&services_lock); int n = poll(fds, nfds, -1); if (n < 0) { perror("rvnit: poll"); sleep(1); continue; } if (fds[0].revents & POLLIN) { /* data on newlogfd, restart */ char c; read(fds[0].fd, &c, 1); continue; } if (n == 1 && (fds[1].revents & POLLHUP) && !(fds[1].revents & POLLIN)) { /* selflog was closed, end thread */ return 0; } for (nfds_t j = 0; j < nfds; j++) { if (fds[j].revents & POLLNVAL) { dprintf(2, "invalid fd %d\n", fds[j].revents); continue; } if ((fds[j].revents & (POLLHUP | POLLERR)) && !(fds[j].revents & POLLIN)) { closed_pipe: /* write end was closed, close whole pipe */ pthread_mutex_lock(&services_lock); for (int i = 0; i < MAX_SV; i++) { if (services[i].logfd[0] == fds[j].fd) { close(services[i].logfd[0]); services[i].logfd[0] = -1; services[i].logfd[1] = -1; services[i].logged = 0; break; } } pthread_mutex_unlock(&services_lock); continue; } if (!(fds[j].revents & POLLIN)) continue; struct timespec now; clock_gettime(CLOCK_REALTIME, &now); char timestamp[32]; strftime(timestamp, sizeof timestamp, "%Y-%m-%dT%H:%M:%S", gmtime(&now.tv_sec)); char buf[4096]; ssize_t rd = read(fds[j].fd, buf, sizeof buf); if (rd < 0) { perror("rvnit: read"); continue; } if (rd == 0) { // some OS set POLLIN|POLLHUP on closed pipes // https://www.greenend.org.uk/rjk/tech/poll.html if (j == 1 && n == 1) { /* selflog was closed, end thread */ return 0; } goto closed_pipe; } if (rd < (int)sizeof buf && buf[rd - 1] != '\n') { // sometimes processes use multiple write calls // for one line, give them 25msec to do try to // get the whole line. nanosleep(&(struct timespec){0, 25000000}, 0); ssize_t rd2 = read(fds[j].fd, buf + rd, sizeof buf - rd); if (rd2 > 0) rd += rd2; } const char *sv = ""; long pid = -1; int pass_thru = 0; if (fds[j].fd == selflogfd[0]) { sv = "rvnit"; pid = getpid(); pass_thru = (level != 100); } else { pthread_mutex_lock(&services_lock); for (int i = 0; i < MAX_SV; i++) { if (services[i].logfd[0] == fds[j].fd) { sv = services[i].name; pid = services[i].display_pid; break; } } pthread_mutex_unlock(&services_lock); } char *s = buf; char *e = buf + rd; while (s < e) { char *eol = memchr(s, '\n', e - s); if (!eol) eol = e; int r = dprintf(globallogfd[1], "%s.%05ld %s[%ld]: %.*s\n", timestamp, now.tv_nsec / 10000, sv, pid, (int)(eol - s), s); if (!use_global_log || pass_thru || r < 0) { /* print own messages, and messages during boot/shutdown, or when writing to logger failed (potentially because the pipe is full, we don't want to stall here) */ dprintf(2, "%s %s[%ld]: %.*s\n", timestamp, sv, pid, (int)(eol - s), s); } s = eol + 1; } } } return 0; } int reap(pid_t pid, int status) { for (int i = 0; i < MAX_SV; i++) { if (services[i].pid != pid) continue; services[i].pid = 0; services[i].status = status; LOG("reaped %s[%d] with status %d during level=%d", services[i].name, pid, status, level); return i; } return -1; } /* -1: no error handler, 0: error handler successful, 1: error handler failed */ int on_error() { int e = -1; for (int l = level; l >= 0; l--) { for (int i = 0; i < MAX_SV; i++) { if (services[i].level != l) continue; if (services[i].name[2] == 'E') { e = i; goto found; } } } // no error handler, keep going return -1; found: restart(e); int status = 0; pid_t pid = waitpid(services[e].pid, &status, 0); reap(pid, status); if (WEXITSTATUS(status) == 111) return -1; // keep going return status != 0; } int svcmp(void const *a, void const *b) { const struct entry *sv_a = a; const struct entry *sv_b = b; return strcmp(sv_a->name, sv_b->name); } void rescandir() { DIR *dir = opendir("."); if (!dir) abort(); int i = 0; for (int j = 0; j < MAX_SV; j++) services[j].seen = 0; struct dirent *ent; while ((ent = readdir(dir))) { if (!isdigit(ent->d_name[0]) || !isdigit(ent->d_name[1])) continue; if (ent->d_name[strlen(ent->d_name) - 1] == '~') continue; while (services[i].state != UNDEF) i++; for (int j = 0; j < MAX_SV; j++) if (strcmp(services[j].name, ent->d_name) == 0) { services[j].seen = 1; goto next; /* already know this one */ } services[i].seen = 1; services[i].level = 10 * (ent->d_name[0] - '0') + (ent->d_name[1] - '0'); snprintf(services[i].name, sizeof services[i].name, "%s", ent->d_name); // follow symlinks services[i].state = access(ent->d_name, X_OK) == 0 ? UP : DOWN; services[i].logfd[0] = -1; services[i].logfd[1] = -1; services[i].logged = 0; i++; next: ; } for (int j = 0; j < MAX_SV; j++) if (services[j].state != UNDEF && services[j].seen == 0) services[j].state = DOWN; qsort(services, MAX_SV, sizeof services[0], svcmp); closedir(dir); } void own_console() { int ttyfd = open("/dev/console", O_RDWR); if (ttyfd < 0) return; dup2(ttyfd, 0); dup2(ttyfd, 1); dup2(ttyfd, 2); if (ttyfd > 2) close(ttyfd); ioctl(0, TIOCSCTTY, 1); } /* only detects toplevel mount points */ static int mounted(const char *dir) { struct stat rootst; struct stat dirst; stat("/", &rootst); if (stat(dir, &dirst) < 0) return 1; /* can't mount if mountpoint doesn't exist */ return rootst.st_dev != dirst.st_dev; } void init_mount() { #ifdef __linux__ if (!access("/dev/null", F_OK) && !mounted("/dev")) { LOG("mounting /dev"); mount("dev", "/dev", "devtmpfs", MS_NOSUID, "mode=0755"); } if (!mounted("/run")) { LOG("mounting /run"); mount("run", "/run", "tmpfs", MS_NOSUID|MS_NODEV, "mode=0755"); } #endif } int main(int argc, char *argv[]) { if (argc < 1) return 111; const char *dir = "/etc/rvnit"; if (argc == 2) dir = argv[1]; if (chdir(dir) < 0) { perror("rvnit: chdir"); return 111; } pipe(selfpipe); fcntl(selfpipe[0], F_SETFL, O_NONBLOCK); fcntl(selfpipe[1], F_SETFL, O_NONBLOCK); fcntl(selfpipe[0], F_SETFD, FD_CLOEXEC); fcntl(selfpipe[1], F_SETFD, FD_CLOEXEC); pipe(selflogfd); fcntl(selflogfd[0], F_SETFL, O_NONBLOCK); fcntl(selflogfd[1], F_SETFL, O_NONBLOCK); fcntl(selflogfd[0], F_SETFD, FD_CLOEXEC); fcntl(selflogfd[1], F_SETFD, FD_CLOEXEC); pipe(newlogfd); fcntl(newlogfd[0], F_SETFL, O_NONBLOCK); fcntl(newlogfd[1], F_SETFL, O_NONBLOCK); fcntl(newlogfd[0], F_SETFD, FD_CLOEXEC); fcntl(newlogfd[1], F_SETFD, FD_CLOEXEC); pipe(globallogfd); // don't care about read end fcntl(globallogfd[1], F_SETFL, O_NONBLOCK); fcntl(globallogfd[0], F_SETFD, FD_CLOEXEC); fcntl(globallogfd[1], F_SETFD, FD_CLOEXEC); LOG("booting"); pid1 = real_pid1 = (getpid() == 1); if (pid1) { umask(0022); setenv("PATH", "/usr/bin:/usr/sbin", 0); init_mount(); own_console(); #ifdef __linux__ if (reboot(RB_DISABLE_CAD) < 0) real_pid1 = 0; /* we are in a container */ #endif } nullfd = open("/dev/null", O_RDONLY | O_CLOEXEC); if (nullfd < 0) { perror("rvnit: open /dev/null"); // use a closed pipe instead int fd[2]; pipe(fd); nullfd = fd[0]; close(fd[1]); } if (pthread_mutex_init(&services_lock, 0) != 0) { perror("rvnit: pthread_mutex_init"); return 111; } main_thread = pthread_self(); /* block all signals in the threads */ sigset_t allset; sigfillset(&allset); sigprocmask(SIG_BLOCK, &allset, 0); pthread_create(&socket_thread, 0, socket_loop, 0); pthread_create(&logger_thread, 0, logger_loop, 0); sigprocmask(SIG_UNBLOCK, &allset, 0); pthread_mutex_lock(&services_lock); rescandir(); pthread_mutex_unlock(&services_lock); if (pid1) ioctl(0, TIOCNOTTY); sigaction(SIGCHLD, &(struct sigaction){ .sa_handler=on_signal, .sa_mask=allset, .sa_flags=SA_NOCLDSTOP|SA_RESTART }, 0); sigaction(SIGINT, &(struct sigaction){ .sa_handler=on_signal, .sa_mask=allset }, 0); if (!real_pid1) { /* keep SIGTERM blocked on Linux for pid 1 by default. */ sigaction(SIGTERM, &(struct sigaction){ .sa_handler=on_signal, .sa_mask=allset }, 0); } int i; for (level = 0; level < 100; level++) { /* spawn all of level */ int oneshot = 0; pthread_mutex_lock(&services_lock); if (want_shutdown) { pthread_mutex_unlock(&services_lock); break; } // spawn loggers first for (i = 0; i < MAX_SV; i++) { if (services[i].level != level) continue; if (services[i].state != UP) continue; if (services[i].name[2] == 'L') restart(i); } // spawn oneshots, daemons, global logger for (i = 0; i < MAX_SV; i++) { if (services[i].level != level) continue; if (services[i].state != UP) continue; if (services[i].name[2] == 'S') { restart(i); oneshot++; } else if (services[i].name[2] == 'D') { restart(i); } else if (services[i].name[2] == 'G') { restart(i); } } pthread_mutex_unlock(&services_lock); while (oneshot) { int status = 0; int pid = timedwait(&status, -1); if (pid < 0 && errno == ECHILD) break; pthread_mutex_lock(&services_lock); if (want_shutdown) { pthread_mutex_unlock(&services_lock); break; } int i = reap(pid, status); if (i < 0) goto cont1; if (services[i].level != level) goto cont1; if (services[i].name[2] == 'S') { if (services[i].status != 0) { LOG("oneshot %s failed, running error services for level=%d", services[i].name, level); int r = on_error(); if (r == 0) { restart(i); goto cont1; } if (r < 0) { LOG("no error handler, going on"); } if (r > 0) { LOG("fatal error, shutting down at level=%d", level); pthread_mutex_unlock(&services_lock); goto fatal; } } cleanup(i); oneshot--; } else if (services[i].name[2] == 'D' || services[i].name[2] == 'G' || services[i].name[2] == 'L') { restart(i); } cont1: pthread_mutex_unlock(&services_lock); } } sigaction(SIGCONT, &(struct sigaction){ .sa_handler=on_signal, .sa_mask=allset }, 0); LOG("system up"); while (1) { pthread_mutex_lock(&services_lock); if (want_shutdown) { pthread_mutex_unlock(&services_lock); break; } if (want_rescandir) { rescandir(); want_rescandir = 0; } if (want_rescan) { for (i = 0; i < MAX_SV; i++) { if (services[i].name[2] == 'L') { if (services[i].state == UP && services[i].pid == 0) restart(i); if (services[i].state == DOWN && services[i].pid > 0) kill(services[i].pid, SIGTERM); } } for (i = 0; i < MAX_SV; i++) { if (services[i].name[2] == 'D') { if (services[i].state == UP && services[i].pid == 0) restart(i); if (services[i].state == DOWN && services[i].pid > 0) kill(services[i].pid, SIGTERM); } } for (i = 0; i < MAX_SV; i++) { if (services[i].name[2] == 'G') { if (services[i].state == UP && services[i].pid == 0) restart(i); if (services[i].state == DOWN && services[i].pid > 0) { kill(services[i].pid, SIGTERM); use_global_log = 0; LOG("disabling global logger %s", services[i].name); } } } want_rescan = 0; } pthread_mutex_unlock(&services_lock); int status = 0; errno = 0; int pid = timedwait(&status, -1); if (pid < 0) { if (errno == ECHILD) break; if (errno == EINTR) continue; } pthread_mutex_lock(&services_lock); int i = reap(pid, status); if (i < 0) goto cont2; if (services[i].name[2] == 'D' || services[i].name[2] == 'G' || services[i].name[2] == 'L') { LOG("%s terminated with status %d", services[i].name, services[i].status); if (services[i].state == UP) { LOG("restarting %s", services[i].name); restart(i); } } cont2: pthread_mutex_unlock(&services_lock); } fatal: if (pid1) own_console(); sigaction(SIGINT, &(struct sigaction){ .sa_handler=SIG_IGN, .sa_mask=allset }, 0); sigaction(SIGTERM, &(struct sigaction){ .sa_handler=SIG_IGN, .sa_mask=allset }, 0); sigaction(SIGCONT, &(struct sigaction){ .sa_handler=SIG_IGN, .sa_mask=allset }, 0); #ifdef __linux__ reboot(RB_ENABLE_CAD); #endif LOG("shutting down"); if (want_shutdown == 2) { static char rvnit_want_reboot[] = "RVNIT_WANT_REBOOT=1"; putenv(rvnit_want_reboot); } for (; level >= 0; level--) { /* kill all of level */ int oneshot = 0; int daemons = 0; int loggers = 0; pthread_mutex_lock(&services_lock); for (i = 0; i < MAX_SV; i++) { if (services[i].level != level) continue; if (services[i].name[2] == 'K' && services[i].state == UP) { LOG("starting shutdown oneshot %s", services[i].name); restart(i); oneshot++; } if (services[i].name[2] == 'D' && services[i].pid > 0) { LOG("sending sigterm to %s", services[i].name); kill(services[i].pid, SIGTERM); kill(services[i].pid, SIGCONT); services[i].state = DOWN; daemons++; } if (services[i].name[2] == 'L' && services[i].pid > 0) { loggers++; services[i].state = DOWN; } } pthread_mutex_unlock(&services_lock); while (oneshot) { int status = 0; int pid = timedwait(&status, -1); if (pid < 0 && errno == ECHILD) break; pthread_mutex_lock(&services_lock); int i = reap(pid, status); if (i < 0) goto cont3; if (services[i].level != level) goto cont3; if (services[i].name[2] == 'K') { LOG("oneshot %s exited with status %d", services[i].name, services[i].status); cleanup(i); oneshot--; } else if (services[i].name[2] == 'D') { LOG("daemon %s exited with status %d", services[i].name, services[i].status); if (services[i].logged) close(services[i].logfd[1]); daemons--; } else if (services[i].name[2] == 'L') { LOG("logger %s exited with status %d", services[i].name, services[i].status); loggers--; } cont3: pthread_mutex_unlock(&services_lock); } // only daemons are left, wait up to 7s before sending // them SIGKILL. int slayed = 0; while (daemons + loggers) { LOG("level=%d waiting for %d+%d", level, daemons, loggers); int status = 0; int pid = timedwait(&status, 7); if (pid == 0) { // hit timeout if (slayed) break; LOG("slaying level=%d", level); pthread_mutex_lock(&services_lock); for (i = 0; i < MAX_SV; i++) { if (services[i].level != level) continue; if (services[i].pid > 0) { kill(services[i].pid, SIGKILL); kill(services[i].pid, SIGCONT); } } pthread_mutex_unlock(&services_lock); slayed = 1; continue; } if (pid < 0 && errno == ECHILD) break; pthread_mutex_lock(&services_lock); int i = reap(pid, status); if (i < 0) goto cont4; if (services[i].level != level) goto cont4; if (services[i].name[2] == 'K') { // can't happen LOG("oneshot %s exited with status %d", services[i].name, services[i].status); oneshot--; } else if (services[i].name[2] == 'D') { LOG("daemon %s exited with status %d", services[i].name, services[i].status); if (services[i].logged) close(services[i].logfd[1]); daemons--; } else if (services[i].name[2] == 'L') { LOG("logger %s exited with status %d", services[i].name, services[i].status); loggers--; } cont4: pthread_mutex_unlock(&services_lock); } if (daemons) { LOG("slaying unsuccessful, %d daemons left in level=%d, continuing...", daemons, level); } } if (want_shutdown == 2) dprintf(selflogfd[1], "reboot\n"); else dprintf(selflogfd[1], "shutdown\n"); close(globallogfd[1]); close(selflogfd[1]); pthread_join(logger_thread, 0); #ifdef __linux__ if (pid1) { sync(); if (want_shutdown == 2) { reboot(RB_AUTOBOOT); } else { // falls back to RB_HALT_SYSTEM if not possible reboot(RB_POWER_OFF); } } #endif return 0; }