Reap children
This commit is contained in:
parent
e68a29c15e
commit
ba2175979c
111
daemon.c
111
daemon.c
@ -20,6 +20,7 @@
|
|||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -27,7 +28,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include <sys/timespec.h>
|
#include <sys/timespec.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -47,6 +50,11 @@
|
|||||||
struct timespec restartInterval = { .tv_sec = 1 };
|
struct timespec restartInterval = { .tv_sec = 1 };
|
||||||
struct Set256 stopExits;
|
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, ...) {
|
static void configerr(bool exit, const char *format, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
@ -219,10 +227,111 @@ int main(int argc, char *argv[]) {
|
|||||||
if (len < 0) syslog(LOG_WARNING, "%s: %m", pidPath);
|
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);
|
close(fifo);
|
||||||
unlink(fifoPath);
|
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) {
|
if (pidPath) {
|
||||||
close(pidFile);
|
close(pidFile);
|
||||||
unlink(pidPath);
|
unlink(pidPath);
|
||||||
|
1
daemon.h
1
daemon.h
@ -100,6 +100,7 @@ void serviceStart(struct Service *service);
|
|||||||
void serviceStop(struct Service *service);
|
void serviceStop(struct Service *service);
|
||||||
void serviceRestart(struct Service *service);
|
void serviceRestart(struct Service *service);
|
||||||
void serviceSignal(struct Service *service, int signal);
|
void serviceSignal(struct Service *service, int signal);
|
||||||
|
void serviceReap(pid_t pid, int status);
|
||||||
|
|
||||||
extern char configError[];
|
extern char configError[];
|
||||||
int configParse(const char *path);
|
int configParse(const char *path);
|
||||||
|
72
service.c
72
service.c
@ -25,6 +25,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -109,6 +110,14 @@ err:
|
|||||||
return -1;
|
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) {
|
void serviceStart(struct Service *service) {
|
||||||
if (service->state != Stop) return;
|
if (service->state != Stop) return;
|
||||||
|
|
||||||
@ -118,11 +127,7 @@ void serviceStart(struct Service *service) {
|
|||||||
} else {
|
} else {
|
||||||
service->restartInterval = restartInterval;
|
service->restartInterval = restartInterval;
|
||||||
}
|
}
|
||||||
clock_gettime(CLOCK_MONOTONIC_FAST, &service->restartDeadline);
|
setDeadline(service);
|
||||||
timespecadd(
|
|
||||||
&service->restartDeadline, &service->restartInterval,
|
|
||||||
&service->restartDeadline
|
|
||||||
);
|
|
||||||
|
|
||||||
service->intent = Start;
|
service->intent = Start;
|
||||||
service->pid = fork();
|
service->pid = fork();
|
||||||
@ -131,6 +136,10 @@ void serviceStart(struct Service *service) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (service->pid) {
|
if (service->pid) {
|
||||||
|
syslog(
|
||||||
|
LOG_INFO, "%s[%jd] start",
|
||||||
|
service->name, (intmax_t)service->pid
|
||||||
|
);
|
||||||
service->state = Start;
|
service->state = Start;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -172,8 +181,8 @@ void serviceSignal(struct Service *service, int signal) {
|
|||||||
int error = kill(service->pid, signal);
|
int error = kill(service->pid, signal);
|
||||||
if (error) {
|
if (error) {
|
||||||
syslog(
|
syslog(
|
||||||
LOG_ERR, "signal %s %s[%ju]: %m",
|
LOG_ERR, "kill(%s[%jd], %s): %m",
|
||||||
sys_signame[signal], service->name, (uintmax_t)service->pid
|
service->name, (intmax_t)service->pid, sys_signame[signal]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,3 +200,52 @@ void serviceRestart(struct Service *service) {
|
|||||||
serviceStart(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