Reap children
This commit is contained in:
parent
e68a29c15e
commit
ba2175979c
113
daemon.c
113
daemon.c
@ -20,6 +20,7 @@
|
||||
#include <grp.h>
|
||||
#include <poll.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@ -27,7 +28,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timespec.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sysexits.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
@ -47,6 +50,11 @@
|
||||
struct timespec restartInterval = { .tv_sec = 1 };
|
||||
struct Set256 stopExits;
|
||||
|
||||
static volatile sig_atomic_t signals[NSIG];
|
||||
static void signalHandler(int signal) {
|
||||
signals[signal]++;
|
||||
}
|
||||
|
||||
static void configerr(bool exit, const char *format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
@ -219,10 +227,111 @@ int main(int argc, char *argv[]) {
|
||||
if (len < 0) syslog(LOG_WARNING, "%s: %m", pidPath);
|
||||
}
|
||||
|
||||
// TODO: Main loop.
|
||||
|
||||
signal(SIGHUP, signalHandler);
|
||||
signal(SIGINT, signalHandler);
|
||||
signal(SIGTERM, signalHandler);
|
||||
signal(SIGCHLD, signalHandler);
|
||||
signal(SIGINFO, signalHandler);
|
||||
|
||||
for (size_t i = 0; i < services.len; ++i) {
|
||||
serviceStart(&services.ptr[i]);
|
||||
}
|
||||
|
||||
// TODO: setproctitle to number of services currently running.
|
||||
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
for (;;) {
|
||||
struct pollfd fds[1 + 2 * services.len];
|
||||
fds[0].fd = fifo;
|
||||
fds[0].events = POLLIN;
|
||||
|
||||
struct timespec deadline = {0};
|
||||
for (size_t i = 0; i < services.len; ++i) {
|
||||
struct Service *service = &services.ptr[i];
|
||||
|
||||
fds[1 + 2 * i].fd = service->outPipe[0];
|
||||
fds[2 + 2 * i].fd = service->errPipe[0];
|
||||
fds[1 + 2 * i].events = POLLIN;
|
||||
fds[2 + 2 * i].events = POLLIN;
|
||||
|
||||
if (service->intent != Start) continue;
|
||||
if (service->state == Start) continue;
|
||||
if (
|
||||
!timespecisset(&deadline) ||
|
||||
timespeccmp(&service->restartDeadline, &deadline, <)
|
||||
) deadline = service->restartDeadline;
|
||||
}
|
||||
|
||||
struct timespec now = {0};
|
||||
struct timespec timeout = {0};
|
||||
if (timespecisset(&deadline)) {
|
||||
clock_gettime(CLOCK_MONOTONIC_FAST, &now);
|
||||
timespecsub(&deadline, &now, &timeout);
|
||||
}
|
||||
if (timeout.tv_sec < 0 || timeout.tv_nsec < 0) {
|
||||
timespecclear(&timeout);
|
||||
}
|
||||
|
||||
int nfds = ppoll(
|
||||
fds, 1 + 2 * services.len,
|
||||
(timespecisset(&deadline) ? &timeout : NULL),
|
||||
&mask
|
||||
);
|
||||
|
||||
// TODO: Handle FIFO and pipes.
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_FAST, &now);
|
||||
for (size_t i = 0; i < services.len; ++i) {
|
||||
struct Service *service = &services.ptr[i];
|
||||
if (service->intent != Start) continue;
|
||||
if (service->state == Start) continue;
|
||||
if (timespeccmp(&service->restartDeadline, &now, <=)) {
|
||||
serviceStart(service);
|
||||
}
|
||||
}
|
||||
|
||||
if (signals[SIGCHLD]) {
|
||||
int status;
|
||||
pid_t pid;
|
||||
while (0 < (pid = waitpid(-1, &status, WNOHANG))) {
|
||||
serviceReap(pid, status);
|
||||
}
|
||||
if (pid < 0 && errno != ECHILD) syslog(LOG_ERR, "waitpid: %m");
|
||||
signals[SIGCHLD] = 0;
|
||||
}
|
||||
|
||||
if (signals[SIGINT] || signals[SIGTERM]) {
|
||||
break;
|
||||
}
|
||||
if (signals[SIGHUP]) {
|
||||
parseConfig(false, configPath);
|
||||
signals[SIGHUP] = 0;
|
||||
}
|
||||
if (signals[SIGINFO]) {
|
||||
// TODO: status *
|
||||
signals[SIGINFO] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
close(fifo);
|
||||
unlink(fifoPath);
|
||||
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < services.len; ++i) {
|
||||
serviceStop(&services.ptr[i]);
|
||||
if (services.ptr[i].state == Start) count++;
|
||||
}
|
||||
while (count--) {
|
||||
int status;
|
||||
pid_t pid = wait(&status);
|
||||
if (pid < 0) {
|
||||
syslog(LOG_ERR, "wait: %m");
|
||||
continue;
|
||||
}
|
||||
serviceReap(pid, status);
|
||||
}
|
||||
|
||||
if (pidPath) {
|
||||
close(pidFile);
|
||||
unlink(pidPath);
|
||||
|
1
daemon.h
1
daemon.h
@ -100,6 +100,7 @@ void serviceStart(struct Service *service);
|
||||
void serviceStop(struct Service *service);
|
||||
void serviceRestart(struct Service *service);
|
||||
void serviceSignal(struct Service *service, int signal);
|
||||
void serviceReap(pid_t pid, int status);
|
||||
|
||||
extern char configError[];
|
||||
int configParse(const char *path);
|
||||
|
72
service.c
72
service.c
@ -25,6 +25,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
@ -109,6 +110,14 @@ err:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void setDeadline(struct Service *service) {
|
||||
clock_gettime(CLOCK_MONOTONIC_FAST, &service->restartDeadline);
|
||||
timespecadd(
|
||||
&service->restartDeadline, &service->restartInterval,
|
||||
&service->restartDeadline
|
||||
);
|
||||
}
|
||||
|
||||
void serviceStart(struct Service *service) {
|
||||
if (service->state != Stop) return;
|
||||
|
||||
@ -118,11 +127,7 @@ void serviceStart(struct Service *service) {
|
||||
} else {
|
||||
service->restartInterval = restartInterval;
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC_FAST, &service->restartDeadline);
|
||||
timespecadd(
|
||||
&service->restartDeadline, &service->restartInterval,
|
||||
&service->restartDeadline
|
||||
);
|
||||
setDeadline(service);
|
||||
|
||||
service->intent = Start;
|
||||
service->pid = fork();
|
||||
@ -131,6 +136,10 @@ void serviceStart(struct Service *service) {
|
||||
return;
|
||||
}
|
||||
if (service->pid) {
|
||||
syslog(
|
||||
LOG_INFO, "%s[%jd] start",
|
||||
service->name, (intmax_t)service->pid
|
||||
);
|
||||
service->state = Start;
|
||||
return;
|
||||
}
|
||||
@ -172,8 +181,8 @@ void serviceSignal(struct Service *service, int signal) {
|
||||
int error = kill(service->pid, signal);
|
||||
if (error) {
|
||||
syslog(
|
||||
LOG_ERR, "signal %s %s[%ju]: %m",
|
||||
sys_signame[signal], service->name, (uintmax_t)service->pid
|
||||
LOG_ERR, "kill(%s[%jd], %s): %m",
|
||||
service->name, (intmax_t)service->pid, sys_signame[signal]
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -191,3 +200,52 @@ void serviceRestart(struct Service *service) {
|
||||
serviceStart(service);
|
||||
}
|
||||
}
|
||||
|
||||
void serviceReap(pid_t pid, int status) {
|
||||
struct Service *service = NULL;
|
||||
for (size_t i = 0; i < services.len; ++i) {
|
||||
if (services.ptr[i].state != Start) continue;
|
||||
if (services.ptr[i].pid != pid) continue;
|
||||
service = &services.ptr[i];
|
||||
break;
|
||||
}
|
||||
if (!service) {
|
||||
syslog(LOG_WARNING, "reaping unknown child %jd", (intmax_t)pid);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Flush line buffers.
|
||||
|
||||
service->state = Stop;
|
||||
if (WIFEXITED(status)) {
|
||||
int exit = WEXITSTATUS(status);
|
||||
if (exit == StopExit || setTest(&stopExits, exit)) {
|
||||
service->intent = Stop;
|
||||
}
|
||||
if (exit) {
|
||||
syslog(
|
||||
LOG_WARNING, "%s[%jd] exit %d",
|
||||
service->name, (intmax_t)pid, exit
|
||||
);
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
syslog(
|
||||
LOG_WARNING, "%s[%jd] signal %s",
|
||||
service->name, (intmax_t)pid, sys_signame[WTERMSIG(status)]
|
||||
);
|
||||
}
|
||||
|
||||
if (service->intent == Start) {
|
||||
setDeadline(service);
|
||||
syslog(
|
||||
LOG_INFO, "%s[%jd] restart in %jds",
|
||||
service->name, (intmax_t)pid,
|
||||
(intmax_t)service->restartInterval.tv_sec
|
||||
);
|
||||
} else {
|
||||
syslog(LOG_INFO, "%s[%jd] stop", service->name, (intmax_t)pid);
|
||||
}
|
||||
if (service->intent == Restart) {
|
||||
serviceStart(service);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user