diff options
author | Marius Halden <marius.h@lden.org> | 2015-11-26 22:34:27 +0100 |
---|---|---|
committer | Marius Halden <marius.h@lden.org> | 2015-11-26 22:34:27 +0100 |
commit | cf04e4f8e98df62a361bcddfdd56e0a661f4e7b0 (patch) | |
tree | a347b0d59586e59bb8d1d99fcd9f02b572880b79 /scan.c | |
download | svcmon-cf04e4f8e98df62a361bcddfdd56e0a661f4e7b0.tar.gz svcmon-cf04e4f8e98df62a361bcddfdd56e0a661f4e7b0.tar.bz2 svcmon-cf04e4f8e98df62a361bcddfdd56e0a661f4e7b0.tar.xz |
Initial
Diffstat (limited to 'scan.c')
-rw-r--r-- | scan.c | 332 |
1 files changed, 332 insertions, 0 deletions
@@ -0,0 +1,332 @@ +#include <errno.h> +#include <fcntl.h> +#include <dirent.h> +#include <err.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/event.h> +#include <sys/procctl.h> +#include <sys/queue.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <unistd.h> + +const char supdir[] = "/home/marius/r/svc"; +char *super_path[] = { "/home/marius/r/supervise", NULL, NULL }; + +volatile sig_atomic_t wait_for_child = 0; /* Do we have to call wait in the main loop? */ +volatile sig_atomic_t terminate = 0; /* Exit the main-loop */ + +struct svc { + char dir[MAXNAMLEN + 1]; + pid_t supervisor; + + TAILQ_ENTRY(svc) entries; +}; + +TAILQ_HEAD(svclist, svc) services = TAILQ_HEAD_INITIALIZER(services); + +struct svc * +find_svc_sup(pid_t p) +{ + struct svc *np; + + TAILQ_FOREACH(np, &services, entries) { + if (np->supervisor == p) + return np; + } + + return NULL; +} + +struct svc * +find_svc_dir(const char *dir) +{ + struct svc *np; + + TAILQ_FOREACH(np, &services, entries) { + if (strcmp(np->dir, dir) == 0) + return np; + } + + return NULL; +} + +void +remove_svc(struct svc *service) +{ + TAILQ_REMOVE(&services, service, entries); + + if (service->supervisor != -1) + fprintf(stderr, "Removed service (%s) with pid (%d) for supervisor\n", service->dir, service->supervisor); + + free(service); +} + +int +direxists(const char *dir) +{ + int r = 0; + struct stat sb; + char *path; + + if ((asprintf(&path, "%s/%s", supdir, dir)) == -1 || path == NULL) + err(1, "asprintf()"); + + if (stat(path, &sb) != -1) { + if (S_ISDIR(sb.st_mode)) + r = 1; + } else { + if (errno != ENOENT) + perror("stat()"); + } + + free(path); + + return r; +} + +void +start_supervisor(struct svc *service) +{ + char *path; + if ((asprintf(&path, "%s/%s", supdir, service->dir)) == -1 || + path == NULL) + err(1, "asprintf()"); + + pid_t p = fork(); + if (p == 0) { /* Child */ + super_path[1] = path; + + if (execv(super_path[0], super_path)) + err(1, "execv()"); + } else if (p > 0) { /* Parent */ + service->supervisor = p; + } else { + err(1, "fork()"); + } + + free(path); +} + +void +add_missing_svc(const char *name) +{ + struct svc *tmp; + + tmp = find_svc_dir(name); + if (tmp != NULL) + return; /* We already have it */ + + tmp = malloc(sizeof(struct svc)); + if (tmp == NULL) + err(1, "malloc()"); + + if (strlcpy(tmp->dir, name, (MAXNAMLEN + 1)) >= (MAXNAMLEN + 1)) + errx(1, "strlcpy()"); + + tmp->supervisor = -1; + + TAILQ_INSERT_TAIL(&services, tmp, entries); + + start_supervisor(tmp); +} + +void +scan_svcdir(int dir_fd) +{ + DIR *dir; + struct dirent *dp; + + if ((dir = fdopendir(dir_fd)) == NULL) + err(1, "opendir()"); + + while ((dp = readdir(dir)) != NULL) { + if (dp->d_type != DT_DIR) + continue; + if (dp->d_name[0] == '.') + continue; + + add_missing_svc(dp->d_name); + } + + rewinddir(dir); + + if (fdclosedir(dir) == -1) + err(1, "closedir()"); +} + +void +handle_sigchild(int sig, siginfo_t *siginfo, void *ucontext) +{ + wait_for_child = 1; +} + +void +handle_fatal(int sig, siginfo_t *siginfo, void *ucontext) +{ + terminate = 1; +} + +void +setup_signals() +{ + struct sigaction act; + memset(&act, 0, sizeof(act)); + + act.sa_flags = SA_SIGINFO; + + act.sa_sigaction = &handle_sigchild; + if (sigaction(SIGCHLD, &act, NULL) == -1) + err(1, "sigaction()"); + + act.sa_sigaction = &handle_fatal; + if (sigaction(SIGHUP, &act, NULL) == -1) + err(1, "sigaction()"); + if (sigaction(SIGINT, &act, NULL) == -1) + err(1, "sigaction()"); + if (sigaction(SIGTERM, &act, NULL) == -1) + err(1, "sigaction()"); +} + +void +reset_signals() +{ + struct sigaction act; + memset(&act, 0, sizeof(act)); + + act.sa_handler = SIG_DFL; + + if (sigaction(SIGCHLD, &act, NULL) == -1) + err(1, "sigaction()"); + + if (sigaction(SIGHUP, &act, NULL) == -1) + err(1, "sigaction()"); + if (sigaction(SIGINT, &act, NULL) == -1) + err(1, "sigaction()"); + if (sigaction(SIGTERM, &act, NULL) == -1) + err(1, "sigaction()"); +} + +void +reap_all() +{ + int r; + + for (;;) { + r = wait(NULL); + if (r != -1) + continue; + + if (errno == EINTR) { + continue; + } else if (errno == ECHILD) { + break; + } else { + perror("wait()"); + break; + } + } +} + +void +signal_services(int sig) +{ + struct svc *np; + + TAILQ_FOREACH(np, &services, entries) { + if (np->supervisor != -1) { + if (kill(np->supervisor, sig) == -1) /* XXX: Handle failure better? */ + perror("kill()"); + } + } +} + +void +try_wait() +{ + int p, s; + for (;;) { + struct svc *tmp; + + p = waitpid(-1, &s, WNOHANG); + if (p == 0) + break; + if (p == -1) { + if (errno == EINTR) /* Impossible? */ + continue; + else if (errno == ECHILD) /* In case there are no children */ + break; + else + err(1, "waitpid()"); + } + + tmp = find_svc_sup(p); + if (tmp == NULL) + continue; /* XXX: Log something here? */ + + tmp->supervisor = -1; + + if (direxists(tmp->dir)) + start_supervisor(tmp); + else + remove_svc(tmp); + } +} + +int +main(int argc, char **argv) +{ + int kq, dir_fd; + struct kevent evt; + pid_t mypid = getpid(); + + if (procctl(P_PID, mypid, PROC_REAP_ACQUIRE, NULL) == -1) + err(1, "procctl()"); + + dir_fd = open(supdir, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (dir_fd == -1) + err(1, "open()"); + + setup_signals(); + + scan_svcdir(dir_fd); + + kq = kqueue(); + if (kq == -1) + err(1, "kqueue()"); + + EV_SET(&evt, dir_fd, EVFILT_VNODE, EV_ADD | EV_ENABLE, NOTE_WRITE | NOTE_EXTEND, 0, 0); + + for (;;) { + try_wait(); + + if (terminate) { + signal_services(SIGTERM); /* XXX: Forward the received signal? */ + break; + } + + struct kevent revt; + int e = kevent(kq, &evt, 1, &revt, 1, NULL); + if (e == -1) { + if (errno != EINTR) + err(1, "kevent()"); + } else if (e > 0) /* XXX: Check revt instead of blindly scanning? */ + scan_svcdir(dir_fd); + } + + if (close(kq) == -1) + perror("close()"); + if (close(dir_fd) == -1) + perror("close()"); + + reset_signals(); /* Make SIGTERM/INT work again in case reap_all uses a long time. */ + + reap_all(); + + return 0; +} |