diff options
-rw-r--r-- | daemon.c | 73 | ||||
-rw-r--r-- | daemon.h | 51 | ||||
-rw-r--r-- | service.c | 55 |
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; +} |