summary refs log tree commit diff
path: root/daemon.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--daemon.c113
1 files changed, 111 insertions, 2 deletions
diff --git a/daemon.c b/daemon.c
index 3dc0640..cd12f92 100644
--- a/daemon.c
+++ b/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);