aboutsummaryrefslogtreecommitdiffstats
path: root/scan.c
diff options
context:
space:
mode:
authorMarius Halden <marius.h@lden.org>2015-11-26 22:34:27 +0100
committerMarius Halden <marius.h@lden.org>2015-11-26 22:34:27 +0100
commitcf04e4f8e98df62a361bcddfdd56e0a661f4e7b0 (patch)
treea347b0d59586e59bb8d1d99fcd9f02b572880b79 /scan.c
downloadsvcmon-cf04e4f8e98df62a361bcddfdd56e0a661f4e7b0.tar.gz
svcmon-cf04e4f8e98df62a361bcddfdd56e0a661f4e7b0.tar.bz2
svcmon-cf04e4f8e98df62a361bcddfdd56e0a661f4e7b0.tar.xz
Initial
Diffstat (limited to 'scan.c')
-rw-r--r--scan.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/scan.c b/scan.c
new file mode 100644
index 0000000..f9a6b05
--- /dev/null
+++ b/scan.c
@@ -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;
+}