/* Copyright (C) 2020 C. McEnroe * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "daemon.h" #ifndef RUNDIR #define RUNDIR "/var/run" #endif #ifndef ETCDIR #define ETCDIR "/usr/local/etc" #endif static void parseExits(char *list) { setClear(&stopExits); while (*list) { byte exit = strtoul(list, &list, 10); if (*list) { if (*list != ',') errx(EX_USAGE, "invalid exit status %s", list); list++; } setAdd(&stopExits, exit); } } int main(int argc, char *argv[]) { bool daemonize = true; setAdd(&stopExits, 127); setAdd(&stopExits, EX_USAGE); setAdd(&stopExits, EX_DATAERR); setAdd(&stopExits, EX_NOINPUT); setAdd(&stopExits, EX_OSFILE); setAdd(&stopExits, EX_CANTCREAT); setAdd(&stopExits, EX_CONFIG); const char *pidPath = NULL; const char *configPath = ETCDIR "/spawntab"; const char *fifoPath = RUNDIR "/spawnd.pipe"; const char *user = NULL; const char *group = NULL; for (int opt; 0 < (opt = getopt(argc, argv, "C:c:df:g:p:s:t:u:"));) { switch (opt) { break; case 'C': serviceDir = optarg; break; case 'c': fifoPath = optarg; break; case 'd': daemonize = false; break; case 'f': configPath = optarg; break; case 'g': group = optarg; break; case 'p': pidPath = optarg; break; case 's': parseExits(optarg); break; case 't': restartInterval = strtoul(optarg, NULL, 10); break; case 'u': user = optarg; break; default: return EX_USAGE; } } // TODO: Read config file. int error = access(serviceDir, X_OK); if (error) err(EX_NOINPUT, "%s", serviceDir); errno = 0; serviceUser = (user ? getpwnam(user) : getpwuid(getuid())); if (errno) err(EX_OSFILE, "getpwnam"); if (!serviceUser) errx(EX_USAGE, "no such user %s", user); errno = 0; serviceGroup = (group ? getgrnam(group) : getgrgid(serviceUser->pw_gid)); if (errno) err(EX_OSFILE, "getgrnam"); if (!serviceGroup) errx(EX_USAGE, "no such group %s", group); int pidFile = -1; if (pidPath) { pidFile = open( pidPath, O_WRONLY | O_CREAT | O_EXLOCK | O_CLOEXEC, 0600 ); if (pidFile < 0) err(EX_CANTCREAT, "%s", pidPath); } // We can't lock a named pipe, so just warn if it already exists. error = mkfifo(fifoPath, 0600); if (error) { if (errno != EEXIST) err(EX_CANTCREAT, "%s", fifoPath); warn("%s", fifoPath); } 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); if (daemonize) { error = daemon(0, 0); if (error) { syslog(LOG_ERR, "daemon: %m"); return EX_OSERR; } } if (pidPath) { int len = dprintf(pidFile, "%ju", (uintmax_t)getpid()); if (len < 0) syslog(LOG_WARNING, "%s: %m", pidPath); } // TODO: Main loop. close(fifo); unlink(fifoPath); if (pidPath) { close(pidFile); unlink(pidPath); } }