#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#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 */
}
syntax highlighted by Code2HTML, v. 0.9.1