/** * Copyright © 2015 Mattias Andrée * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include /** * The file descriptor for the socket. */ #define SOCK_FILENO 3 /** * The file descriptor for the state file. */ #define STATE_FILENO 4 /** * The file descriptor for connection to the current client. */ #define CONN_FILENO 5 /** * Command: queue a job. */ #define SAT_QUEUE 0 /** * Command: remove jobs. */ #define SAT_REMOVE 1 /** * Command: print job queue. */ #define SAT_PRINT 2 /** * Command: run jobs. */ #define SAT_RUN 3 /** * The common beginning of for all daemon pathnames. */ #define DAEMON_PREFIX LIBEXEC "/" PACKAGE "/satd-" /** * Signal that has been received, 0 if none. */ static volatile sig_atomic_to received_signo = 0; /** * Invoked when a signal is received. * * @param int The signal. */ static void sighander(int signo) { received_signo = (sig_atomic_to)signo; } /** * The sat daemon. * * @param argc Should be 4. * @param argv The name of the process, the pathname of the socket, * the pathname to the state file, and $SAT_HOOK_PATH * (the pathname of the hook-script.) * @param envp The environment. * @return 0 The process was successful. * @return 1 The process failed queuing the job. */ int main(int argc, char *argv[], char *envp[]) { int fd = -1, rc = 0; pid_t pid; char type; const char *image; struct stat _attr; /* Set up signal handlers. */ if (signal(SIGHUP, sighandler) == SIG_ERR) goto fail; if (signal(SIGCHLD, sighandler) == SIG_ERR) goto fail; /* Pick-up where we left off. */ if (!fstat(CONN_FILENO, &_attr)) { fd = CONN_FILENO; goto peek_again; } else if (errno != EBADF) { goto fail; } /* The magnificent loop. */ accept_again: while (waitpid(-1, NULL, WNOHANG) > 0); if (received_signo == SIGHUP) { execve(DAEMON_PREFIX "diminished", argv, envp); perror(argv[0]); } received_signo = 0; fd = accept(SOCK_FILENO, NULL, NULL); if (fd == -1) { switch (errno) { case ECONNABORTED: case EINTR: goto accept_again; default: /* Including EMFILE, ENFILE, and ENOMEM * because of potential resource leak. */ goto fail; } } if (fd != CONN_FILENO) { if (dup2(fd, CONN_FILENO) == -1) goto fail; close(fd), fd = CONN_FILENO; } peek_again: if (recv(fd, &type, (size_t)1, MSG_PEEK /* Just peek in case we fail! */) <= 0) { perror(argv[0]); goto connection_done; } fork_again: switch ((pid = fork())) { case -1: if (errno != EAGAIN) goto fail; (void) sleep(1); /* Possibly shorter because of SIGCHLD. */ goto fork_again; case 0: switch (type) { case SAT_QUEUE: image = DAEMON_PREFIX "add"; break; case SAT_REMOVE: image = DAEMON_PREFIX "rm"; break; case SAT_PRINT: image = DAEMON_PREFIX "list"; break; case SAT_RUN: image = DAEMON_PREFIX "run"; break; default: fprintf(stderr, "%s: invalid command received.\n", argv[0]); exit(1); } if (dup2(fd, SOCK_FILENO) != -1) close(fd), fd = SOCK_FILENO, execve(image, argv, envp); perror(argv[0]); close(fd); exit(1); default: break; } connection_done: close(fd), fd = -1; goto accept_again; done: unlink(argv[1]); if (!rc) unlink(argv[2]); close(SOCK_FILENO); close(STATE_FILENO); return 0; fail: perror(argv[0]); if (fd >= 0) close(fd); rc = 1; goto done; (void) argc; }