summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--daemon.c73
-rw-r--r--daemon.h51
-rw-r--r--service.c55
3 files changed, 168 insertions, 11 deletions
diff --git a/daemon.c b/daemon.c
index 7030978..6cbf8eb 100644
--- a/daemon.c
+++ b/daemon.c
@@ -19,6 +19,7 @@
 #include <fcntl.h>
 #include <grp.h>
 #include <pwd.h>
+#include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -39,6 +40,72 @@
 #define ETCDIR "/usr/local/etc"
 #endif
 
+#define WS " \t"
+
+int restartInterval = 1000;
+struct Set256 stopExits;
+
+static void configerr(bool exit, const char *format, ...) {
+	va_list ap;
+	va_start(ap, format);
+	if (exit) {
+		verrx(EX_DATAERR, format, ap);
+	} else {
+		vsyslog(LOG_ERR, format, ap);
+	}
+	va_end(ap);
+}
+
+static void parseConfig(bool exit, const char *path) {
+	size_t cap = 0;
+	char *buf = NULL;
+
+	FILE *file = fopen(path, "r");
+	if (!file) {
+		configerr(exit, "%s: %s", path, strerror(errno));
+		goto err;
+	}
+
+	prependClear();
+
+	int line = 1;
+	for (ssize_t len; 0 <= (len = getline(&buf, &cap, file)); ++line) {
+		if (buf[len - 1] == '\n') buf[len - 1] = '\0';
+
+		char *ptr = &buf[strspn(buf, WS)];
+		if (!ptr[0] || ptr[0] == '#') {
+			continue;
+		} else if (ptr[0] == '%') {
+			int error = prependAdd(&ptr[1]);
+			if (error) {
+				configerr(
+					exit, "cannot add prepend command: %s", strerror(errno)
+				);
+				goto err;
+			}
+		} else {
+			char *name = strsep(&ptr, WS);
+			if (!ptr) {
+				configerr(
+					exit, "%s:%d: no command line for service %s",
+					path, line, name
+				);
+				goto err;
+			}
+			int error = serviceAdd(name, ptr);
+			if (error) {
+				configerr(exit, "cannot add service: %s", strerror(errno));
+				goto err;
+			}
+		}
+	}
+	if (ferror(file)) configerr(exit, "%s: %s", path, strerror(errno));
+
+err:
+	free(buf);
+	fclose(file);
+}
+
 static void parseExits(char *list) {
 	setClear(&stopExits);
 	while (*list) {
@@ -52,6 +119,8 @@ static void parseExits(char *list) {
 }
 
 int main(int argc, char *argv[]) {
+	setprogname(argv[0]);
+
 	bool daemonize = true;
 	setAdd(&stopExits, 127);
 	setAdd(&stopExits, EX_USAGE);
@@ -83,7 +152,7 @@ int main(int argc, char *argv[]) {
 		}
 	}
 
-	// TODO: Read config file.
+	parseConfig(true, configPath);
 	
 	int error = access(serviceDir, X_OK);
 	if (error) err(EX_NOINPUT, "%s", serviceDir);
@@ -116,7 +185,7 @@ int main(int argc, char *argv[]) {
 	int fifo = open(fifoPath, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
 	if (fifo < 0) err(EX_CANTCREAT, "%s", fifoPath);
 
-	openlog("spawnd", LOG_NDELAY | LOG_PID | LOG_PERROR, LOG_DAEMON);
+	openlog(getprogname(), LOG_NDELAY | LOG_PID | LOG_PERROR, LOG_DAEMON);
 
 	if (daemonize) {
 		error = daemon(0, 0);
diff --git a/daemon.h b/daemon.h
index 3a141ba..d7efb28 100644
--- a/daemon.h
+++ b/daemon.h
@@ -17,9 +17,56 @@
 #include <grp.h>
 #include <pwd.h>
 #include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
 
 typedef unsigned char byte;
 
+extern struct Prepend {
+	size_t cap, len;
+	char **commands;
+} prepend;
+
+static inline void prependClear(void) {
+	for (size_t i = 0; i < prepend.len; ++i) {
+		free(prepend.commands[i]);
+	}
+	prepend.len = 0;
+}
+static inline int prependAdd(const char *command) {
+	if (prepend.len == prepend.cap) {
+		size_t cap = (prepend.cap ? prepend.cap * 2 : 8);
+		void *ptr = realloc(prepend.commands, sizeof(*prepend.commands) * cap);
+		if (!ptr) return -1;
+		prepend.cap = cap;
+		prepend.commands = ptr;
+	}
+	prepend.commands[prepend.len] = strdup(command);
+	if (!prepend.commands[prepend.len]) return -1;
+	prepend.len++;
+	return 0;
+}
+
+extern const char *serviceDir;
+extern struct passwd *serviceUser;
+extern struct group *serviceGroup;
+
+struct Service {
+	char *name;
+	char *command;
+	// TODO: And many other things...
+};
+
+extern struct Services {
+	size_t cap, len;
+	struct Service *ptr;
+} services;
+
+int serviceAdd(const char *name, const char *command);
+
+extern char configError[];
+int configParse(const char *path);
+
 struct Set256 {
 	uint32_t bits[8];
 };
@@ -35,7 +82,3 @@ static inline uint32_t setTest(const struct Set256 *set, byte x) {
 
 extern int restartInterval;
 extern struct Set256 stopExits;
-
-extern const char *serviceDir;
-extern struct passwd *serviceUser;
-extern struct group *serviceGroup;
diff --git a/service.c b/service.c
index a81a588..b6e6ce1 100644
--- a/service.c
+++ b/service.c
@@ -14,18 +14,63 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <err.h>
 #include <grp.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sysexits.h>
+#include <string.h>
 
 #include "daemon.h"
 
-struct Set256 stopExits;
-int restartInterval = 1000;
-
 const char *serviceDir = "/";
 struct passwd *serviceUser;
 struct group *serviceGroup;
+
+struct Prepend prepend;
+struct Services services;
+
+static void serviceFree(struct Service *service) {
+	free(service->name);
+	free(service->command);
+}
+
+int serviceAdd(const char *name, const char *command) {
+	struct Service *service = NULL;
+	for (size_t i = 0; i < services.len; ++i) {
+		if (!strcmp(services.ptr[i].name, name)) {
+			service = &services.ptr[i];
+			break;
+		}
+	}
+
+	if (service) {
+		char *dup = strdup(command);
+		if (!dup) return -1;
+		free(service->command);
+		service->command = dup;
+		return 0;
+	}
+
+	if (services.len == services.cap) {
+		size_t cap = (services.cap ? services.cap * 2 : 8);
+		void *ptr = realloc(services.ptr, sizeof(*services.ptr) * cap);
+		if (!ptr) return -1;
+		services.cap = cap;
+		services.ptr = ptr;
+	}
+	service = &services.ptr[services.len];
+	memset(service, 0, sizeof(*service));
+
+	service->name = strdup(name);
+	if (!service->name) goto err;
+
+	service->command = strdup(command);
+	if (!service->command) goto err;
+
+	services.len++;
+	return 0;
+
+err:
+	serviceFree(service);
+	return -1;
+}