#include #include #include #include #include #include #include #include #include #include #include "iolib.h" #include "freedt.h" #include "config.h" const char *progname = "svscan"; const char *proghelp = "Usage: svscan [OPTIONS] [dir]\n" "Manage several supervise processes for a directory of services.\n\n"; struct service { char *dir; int logpipe[2]; int found; struct service *next; }; struct service *services = NULL; int numservices = 0; int startdir; void sigchld_handler(int value) { int saved_errno = errno; while (waitpid(0, NULL, WNOHANG) > 0) /* nothing */; errno = saved_errno; } /* Check whether a service is running in dir. Returns 1 if it is, 0 if it isn't, and -1 if dir isn't a service directory or another error occurred. */ int service_running(const char *dir) { int fd; buffer b = BUFFER; bformat(&b, "@c/supervise/control", dir); fd = open(bstr(&b), O_WRONLY | O_NONBLOCK); bfree(&b); if (fd < 0) { if (errno == ENXIO || errno == ENOENT) { /* No supervise is listening, or no supervise dir or pipe exists. */ return 0; } else if (errno == ENOTDIR) { /* What we thought was a service directory is actually a file or a non-service. */ return -1; } else { warn2(dir, "unable to open control pipe"); return -1; } } close(fd); return 1; } /* Check that a supervise process is running happily for dir, and start one if it isn't. Return 0 on success, -1 if dir isn't actually a service or something else goes wrong. */ int check_supervise(const char *dir, int in, int err) { pid_t pid; int running = service_running(dir); if (running == -1) return -1; else if (running == 1) return 0; pid = fork(); if (pid < 0) { warn2(dir, "fork failed"); return -1; } if (pid == 0) { if (in != -1) { if (dup2(in, STDIN_FILENO) < 0) _exit(111); } if (err != -1) { if (dup2(err, STDOUT_FILENO) < 0) _exit(111); } execlp("supervise", "supervise", dir, NULL); _exit(111); } return 0; } /* Check that a supervise or pair of supervises is running for service serv, and start them if not. Return 0 on success, or -1 if we shouldn't supervise this service any more. */ int check_service(struct service *serv) { if (check_supervise(serv->dir, -1, serv->logpipe[1]) < 0) return -1; if (serv->logpipe[0] != -1) { buffer b = BUFFER; int rc; bformat(&b, "@c/log", serv->dir); rc = check_supervise(bstr(&b), serv->logpipe[0], -1); bfree(&b); if (rc < 0) return -1; } return 0; } void remove_service(struct service *this) { struct service *prev; if (services == this) { services = this->next; } else { for (prev = services; prev; prev = prev->next) if (prev->next == this) break; prev->next = this->next; } if (this->logpipe[0] != -1) { close(this->logpipe[0]); close(this->logpipe[1]); } free(this->dir); free(this); } struct service *add_service(const char *dir) { struct service *svc; if (chdir(dir) < 0) { if (errno != ENOTDIR) warn2(dir, "unable to change into service directory"); return NULL; } svc = malloc(sizeof *svc); if (svc == NULL) { warn("out of memory"); goto out; } if (chdir("log") == 0) { if (pipe(svc->logpipe) < 0) { warn("cannot create pipe"); free(svc); svc = NULL; goto out; } set_fd_cloexec(svc->logpipe[0]); set_fd_cloexec(svc->logpipe[1]); } else { svc->logpipe[0] = -1; svc->logpipe[1] = -1; } svc->dir = strdup(dir); svc->next = NULL; if (services == NULL) { services = svc; } else { struct service *last; for (last = services; last->next; last = last->next) ; last->next = svc; } out: if (fchdir(startdir) < 0) die("unable to change back to start directory"); return svc; } struct service *find_service(const char *dir) { struct service *svc; for (svc = services; svc; svc = svc->next) { if (strcmp(svc->dir, dir) == 0) return svc; } return NULL; } int main(int argc, char **argv) { struct sigaction sa; DIR *dir; get_default_args(argc, argv); if ((argc - optind) == 1) { if (chdir(argv[optind]) < 0) die("unable to chdir to specified directory"); } else if (argc == optind) { /* scan current directory */ } else { help(); } startdir = open(".", O_RDONLY); if (startdir < 0) die("unable to open specified directory"); set_fd_cloexec(startdir); sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGCHLD, &sa, NULL) < 0) die("unable to install SIGCHLD handler"); while (1) { struct service *svc; dir = opendir("."); if (dir == NULL) { warn("unable to list directory"); goto next; } for (svc = services; svc; svc = svc->next) svc->found = 0; while (1) { struct dirent *entry = readdir(dir); if (entry == NULL) break; if (entry->d_name[0] == '.') continue; svc = find_service(entry->d_name); if (svc == NULL) svc = add_service(entry->d_name); if (svc != NULL) { if (check_service(svc) < 0) svc->found = 0; else svc->found = 1; } } for (svc = services; svc;) { struct service *next = svc->next; if (svc->found == 0) remove_service(svc); svc = next; } closedir(dir); next: reliable_sleep(5); } return 0; /* NOTREACHED */ }