diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/README | 11 | ||||
-rw-r--r-- | src/client.c | 178 | ||||
-rw-r--r-- | src/client.h | 89 | ||||
-rw-r--r-- | src/common.c (renamed from src/daemon.c) | 213 | ||||
-rw-r--r-- | src/common.h | 240 | ||||
-rw-r--r-- | src/daemon.h | 287 | ||||
-rw-r--r-- | src/sat.c | 139 | ||||
-rw-r--r-- | src/satd-add.c | 76 | ||||
-rw-r--r-- | src/satd-diminished.c | 82 | ||||
-rw-r--r-- | src/satd-timer.c | 9 | ||||
-rw-r--r-- | src/satd.c | 125 | ||||
-rw-r--r-- | src/satq.c | 8 | ||||
-rw-r--r-- | src/satr.c | 7 | ||||
-rw-r--r-- | src/satrm.c | 7 |
14 files changed, 469 insertions, 1002 deletions
@@ -1,11 +1,10 @@ -sat.c The satd program, ask satd to queue a job. +sat.c The satd program, queues a job and the pokes or starts the daemon. satq.c The satq program, prints the job queue. satr.c The satr program, runs jobs and then pokes the daemon. satrm.c The satrm program, removes jobs and then pokes the daemon. satd.c The initialisation part of satd. satd-diminished.c The rest of satd, satd.c exec:s to this. -satd-add.c The part of satd responding to sat, satd-diminished.c fork–exec:s to this. satd-timer.c The part of satd responsible for running expired jobs and setting timers to wait for new expirations, satd-diminished.c fork–exec:s to this. @@ -15,11 +14,5 @@ parse_time.[ch] Use by sat.c to parse the time argument. daemonise.[ch] From <http://github.com/maandree/slibc>; daemonisation of the process. Used by satd.c -client.[ch] Used by sat.c, code for communicating - with satd, starts satd transparently if necessary. - -daemon.[ch] Used by sat{q,r,rm,d*}.c, some shared code for daemons objects. - -common.h Used by sat{,q,r,rm,d*}.c, some shared code. - Included via client.h and daemon.h. +common.[ch] Used by sat{q,r,rm,d*}.c, some shared code. diff --git a/src/client.c b/src/client.c deleted file mode 100644 index 80cd527..0000000 --- a/src/client.c +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright © 2015, 2016 Mattias Andrée <maandree@member.fsf.org> - * - * 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 "client.h" -#include <string.h> -#include <unistd.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/file.h> -#include <sys/wait.h> - - - -/** - * The name of the process. - */ -extern char *argv0; - - - -/** - * Send a command to satd. Start satd if it is not running. - * - * @param cmd Command type. - * @param n The length of the message, 0 if - * `msg` is `NULL` or NUL-terminated. - * @param msg The message to send. - * @return 0 on success, -1 on error. - * - * @throws 0 Error at the daemon-side. - */ -int -send_command(enum command cmd, size_t n, const char *restrict msg) -{ - struct sockaddr_un address; - const void *_cvoid; - int fd = -1, start = 1, status, outfd, goterr = 0; - const char *dir; - pid_t pid; - ssize_t r, wrote; - char *buf = NULL; - signed char cmd_ = (signed char)cmd; - int eot = 0, saved_errno; - - /* Get socket address. */ - dir = getenv("XDG_RUNTIME_DIR"), dir = (dir ? dir : "/run"); - if (strlen(dir) + sizeof("/" PACKAGE "/socket") > sizeof(address.sun_path)) - t ((errno = ENAMETOOLONG)); - stpcpy(stpcpy(address.sun_path, dir), "/" PACKAGE "/lock"); /* Yes, "lock", It is temporary. */ - address.sun_family = AF_UNIX; - - /* Any daemon listening? */ - fd = open(address.sun_path, O_RDONLY); - if (fd == -1) { - t ((errno != ENOENT) && (errno != ENOTDIR)); - } else { - if (flock(fd, LOCK_SH | LOCK_NB /* and LOCK_DRY if that was ever added... */)) - t (start = 0, errno != EWOULDBLOCK); - else - flock(fd, LOCK_UN); - close(fd), fd = -1; - } - - /* Start daemon if not running. */ - if (start) { - switch ((pid = fork())) { - case -1: - goto fail; - case 0: - execl(BINDIR "/satd", BINDIR "/satd", NULL); - perror(argv0); - exit(1); - default: - t (waitpid(pid, &status, 0) != pid); - t (errno = 0, status); - break; - } - } - - /* Create socket. */ - stpcpy(strrchr(address.sun_path, '/'), "/socket"); - t ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1); - t (connect(fd, (const struct sockaddr *)(_cvoid = &address), (socklen_t)sizeof(address))); - - /* Send message. */ - t (write(fd, &cmd_, sizeof(cmd_)) < (ssize_t)sizeof(cmd_)); - while (n) { - t (r = write(fd, msg, n), r <= 0); - msg += (size_t)r; - n -= (size_t)r; - } - t (shutdown(fd, SHUT_WR)); /* Very important. */ - - /* Receive. */ -receive_again: - t (r = read(fd, &cmd_, sizeof(cmd_)), r < (ssize_t)sizeof(cmd_)); - if (r == 0) - goto done; - outfd = (int)cmd_; - goterr |= outfd == STDERR_FILENO; - t (r = read(fd, &n, sizeof(n)), r < (ssize_t)sizeof(n)); - eot = (outfd == 127) & !n; - t (!(buf = malloc(n))); - while (n) { - t (r = read(fd, buf, n), r < 0); - t (errno = 0, r == 0); - n -= (size_t)r; - for (dir = buf; r;) { - t (wrote = write(outfd, dir, (size_t)r), wrote <= 0); - dir += wrote, r -= wrote; - } - } - free(buf), buf = NULL; - goto receive_again; - -done: - shutdown(fd, SHUT_RD), close(fd); - return errno = 0, -goterr; - -fail: - saved_errno = (goterr ? 0 : errno); - if (fd >= 0) - close(fd); - free(buf); - errno = saved_errno; - if (eot) goto done; - return -1; -} - - -/** - * Return the number of bytes required to store a string array. - * - * @param array The string array. - * @return The number of bytes required to store the array. - */ -size_t -measure_array(char *array[]) -{ - size_t rc = 0; - while (*array) rc += strlen(*array++) + 1; - return rc * sizeof(char); -} - - -/** - * Store a string array. - * - * @param storage The buffer where the array is to be stored. - * @param array The array to store. - * @return Where in the buffer the array ends. - */ -char * -store_array(char *restrict storage, char *array[]) -{ - for (; *array; array++, storage++) - storage = stpcpy(storage, *array); - return storage; -} - diff --git a/src/client.h b/src/client.h deleted file mode 100644 index 8f27d73..0000000 --- a/src/client.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright © 2015, 2016 Mattias Andrée <maandree@member.fsf.org> - * - * 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. - */ -#ifndef _DEFAULT_SOURCE -# define _DEFAULT_SOURCE -#endif -#include "common.h" -#include <stddef.h> - - - -/** - * Commands for `send_command`. - */ -enum command { - /** - * Queue a job. - */ - SAT_QUEUE = 0, - - /** - * Remove jobs. - */ - SAT_REMOVE = 1, - - /** - * Print job queue. - */ - SAT_PRINT = 2, - - /** - * Run jobs. - */ - SAT_RUN = 3 -}; - - - -/** - * Send a command to satd. Start satd if it is not running. - * - * @param cmd Command type. - * @param n The length of the message, 0 if - * `msg` is `NULL` or NUL-terminated. - * @param msg The message to send. - * @return 0 on success, -1 on error. - * - * @throws 0 Error at the daemon-side. - */ -int send_command(enum command cmd, size_t n, const char *restrict msg); - -/** - * Return the number of bytes required to store a string array. - * - * @param array The string array. - * @return The number of bytes required to store the array. - */ -#ifdef __GNUC__ -__attribute__((__pure__)) -#endif -size_t measure_array(char *array[]); - -/** - * Store a string array. - * - * @param storage The buffer where the array is to be stored. - * @param array The array to store. - * @return Where in the buffer the array ends. - */ -char *store_array(char *restrict storage, char *array[]); - diff --git a/src/daemon.c b/src/common.c index a39b0e3..a6f4c7c 100644 --- a/src/daemon.c +++ b/src/common.c @@ -19,12 +19,10 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "daemon.h" +#include "common.h" #include <ctype.h> -#include <signal.h> #include <stdarg.h> -#include <sys/stat.h> -#include <sys/file.h> +#include <pwd.h> #include <sys/wait.h> @@ -101,50 +99,6 @@ pwriten(int fildes, const void *buf, size_t nbyte, size_t offset) /** - * Wrapper for `read` that reads all available data. - * - * `errno` is set to `EBADMSG` on success. - * - * @param fd The file descriptor from which to to read. - * @param buf Output parameter for the data. - * @param n Output parameter for the number of read bytes. - * @return 0 on success, -1 on error. - * - * @throws Any exception specified for read(3). - * @throws Any exception specified for realloc(3). - */ -int -readall(int fd, char **buf, size_t *n) -{ - char *buffer = NULL; - size_t ptr = 0, size = 128; - ssize_t got; - char *new; - int saved_errno; - - do { - if ((buffer == NULL) || (ptr == size)) { - t (!(new = realloc(buffer, size <<= 1))); - buffer = new; - } - t (got = read(fd, buffer + ptr, size - ptr), got < 0); - } while (ptr += (size_t)got, got); - - new = realloc(buffer, *n = ptr); - *buf = ptr ? (new ? new : buffer) : NULL; - shutdown(SOCK_FILENO, SHUT_RD); - return errno = EBADMSG, 0; - -fail: - saved_errno = errno; - free(buffer); - shutdown(SOCK_FILENO, SHUT_RD); - errno = saved_errno; - return -1; -} - - -/** * Unmarshal a `NULL`-terminated string array. * * The elements are not actually copied, subpointers @@ -213,49 +167,12 @@ reopen(int fd, int oflag) if (r = open(path, oflag), r < 0) return -1; if (DUP2_AND_CLOSE(r, fd) == -1) - return saved_errno = errno, close(r), errno = saved_errno, -1; + return S(close(r)), -1; return 0; } /** - * Send a string to a client. - * - * @param sockfd The file descriptor of the socket. - * @param outfd The file descriptor to which the client shall output the message. - * @param ... `NULL`-terminated list of string to concatenate. - * @return 0 on success, -1 on error. - */ -int -send_string(int sockfd, int outfd, ...) -{ - va_list args; - size_t i, n = 0; - ssize_t r; - char out = (char)outfd; - const char *s; - - va_start(args, outfd); - while ((s = va_arg(args, const char *))) - n += strlen(s); - va_end(args); - - t (write(sockfd, &out, sizeof(out)) < (ssize_t)sizeof(out)); - t (write(sockfd, &n, sizeof(n)) < (ssize_t)sizeof(n)); - - va_start(args, outfd); - while ((s = va_arg(args, const char *))) - for (i = 0, n = strlen(s); i < n; i += (size_t)r) - t (r = write(sockfd, s + i, n - i), r <= 0); - va_end(args); - - return 0; -fail: - return -1; -} - - -/** * Run a job or a hook. * * @param job The job. @@ -287,8 +204,7 @@ run_job_or_hook(struct job *job, const char *hook) } if (!(pid = fork())) { - close(SOCK_FILENO), close(STATE_FILENO); - close(BOOT_FILENO), close(REAL_FILENO); + close(STATE_FILENO), close(BOOT_FILENO), close(REAL_FILENO); (void)(status = chdir(envp[0])); environ = envp + 1; execvp(*argv, argv); @@ -297,9 +213,7 @@ run_job_or_hook(struct job *job, const char *hook) t ((pid < 0) || (waitpid(pid, &status, 0) != pid)); fail: - saved_errno = errno; - free(args), free(argv), free(envp); - errno = saved_errno; + S(free(args), free(argv), free(envp)); return status ? 1 : -!!saved_errno; } @@ -369,10 +283,7 @@ found_it: return rc; fail: - saved_errno = errno; - flock(STATE_FILENO, LOCK_UN); - free(buf), free(job_full); - errno = saved_errno; + S(flock(STATE_FILENO, LOCK_UN), free(buf), free(job_full)); return -1; } @@ -436,8 +347,7 @@ dup2_and_null(int old, int new) if (fd == old) return new; t (DUP2_AND_CLOSE(fd, old)); return new; fail: - saved_errno = errno, close(fd), errno = saved_errno; - return -1; + return S(close(fd)), -1; } @@ -467,7 +377,7 @@ open_state(int open_flags, char **state_path) if (state_path) *state_path = path, path = NULL; else free(path), path = NULL; fail: - saved_errno = errno, free(path), errno = saved_errno; + S(free(path)); if (!(open_flags & O_CREAT) && ((errno == ENOENT) || (errno == ENOTDIR))) errno = 0; return fd; @@ -478,11 +388,114 @@ fail: * Let the daemon know that it may need to * update the timers, and perhaps exit. * + * @param start Start the daemon if it is not running? + * @param name The name of the process. + * @return 0 on success, -1 on error. + */ +int +poke_daemon(int start, const char *name) +{ + char *path = NULL; + const char *dir; + pid_t pid; + int fd = -1, status, saved_errno; + + /* Get the lock file's pathname. */ + dir = getenv("XDG_RUNTIME_DIR"), dir = (dir ? dir : "/run"); + t (!(path = malloc(strlen(dir) * sizeof(char) + sizeof("/" PACKAGE "/lock")))); + stpcpy(stpcpy(path, dir), "/" PACKAGE "/lock"); + + /* Any daemon listening? */ + fd = open(path, O_RDONLY); + if (fd == -1) { + t ((errno != ENOENT) && (errno != ENOTDIR)); + } else { + if (flock(fd, LOCK_SH | LOCK_NB /* and LOCK_DRY if that was ever added... */)) + t (start = 0, errno != EWOULDBLOCK); + else + flock(fd, LOCK_UN); + t (read(fd, &pid, sizeof(pid)) < (ssize_t)sizeof(pid)); + close(fd), fd = -1; + } + + /* Start daemon if not running, otherwise poke it. */ + if (start) { + switch ((pid = fork())) { + case -1: + goto fail; + case 0: + execl(BINDIR "/satd", BINDIR "/satd", NULL); + perror(name); + exit(1); + default: + t (waitpid(pid, &status, 0) != pid); + t (errno = 0, status); + break; + } + } else { + t (kill(pid, SIGCHLD)); + } + + return 0; +fail: + return S(close(fd), free(path)), -1; +} + + +/** + * Construct the pathname for the hook script. + * + * @param env The environment variable to use for the beginning + * of the pathname, `NULL` for the home directory. + * @param suffix The rest of the pathname. + * @return The pathname. + * + * @throws 0 The environment variable is not set, or, if `env` is + * `NULL` the user is root or homeless. + */ +static char * +hookpath(const char *env, const char *suffix) +{ + struct passwd *pwd; + const char *prefix = NULL; + char *path; + + if (env) { + prefix = getenv(env); + } else if (getuid()) { + pwd = getpwuid(getuid()); + prefix = pwd ? pwd->pw_dir : NULL; + } + if (!prefix || !*prefix) + return errno = 0, NULL; + + t (!(path = malloc((strlen(prefix) + strlen(suffix) + 1) * sizeof(char)))); + stpcpy(stpcpy(path, prefix), suffix); +fail: + return path; +} + + +/** + * Set SAT_HOOK_PATH. + * * @return 0 on success, -1 on error. */ int -poke_daemon(void) +set_hookpath(void) { - return 0; /* TODO poke_daemon */ +#define HOOKPATH(PRE, SUF) \ + t (path = path ? path : hookpath(PRE, SUF), !path && errno) + char *path = NULL; + int saved_errno; + if (!getenv("SAT_HOOK_PATH")) { + HOOKPATH("XDG_CONFIG_HOME", "/sat/hook"); + HOOKPATH("HOME", "/.config/sat/hook"); + HOOKPATH(NULL, "/.config/sat/hook"); + t (setenv("SAT_HOOK_PATH", path ? path : "/etc/sat/hook", 1)); + } + return free(path), 0; +fail: + return S(free(path)), -1; } diff --git a/src/common.h b/src/common.h index 187723e..7ee9ae5 100644 --- a/src/common.h +++ b/src/common.h @@ -22,19 +22,28 @@ #ifndef _DEFAULT_SOURCE # define _DEFAULT_SOURCE #endif +#include <stddef.h> +#include <errno.h> #include <string.h> -#include <stdio.h> +#include <time.h> #include <stdlib.h> -#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <assert.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/timerfd.h> -#ifndef t /** * Go to `fail` if a statement evaluates to non-zero. * * @param ... The statement. */ +#ifndef t # ifndef DEBUG # define t(...) do { if (__VA_ARGS__) goto fail; } while (0) # else @@ -45,6 +54,38 @@ static const char *failed__ = NULL; #endif +/** + * Perform some actions without changing `errno`. + * + * You must have `int saved_errno` declared. + * + * @param ... The actions. + * @return The value on `errno`. + */ +#define S(...) (saved_errno = errno, __VA_ARGS__, errno = saved_errno) + + +/** + * The file descriptor for the state file. + */ +#define STATE_FILENO 3 + +/** + * The file descriptor for the CLOCK_BOOTTIME timer. + */ +#define BOOT_FILENO 4 + +/** + * The file descriptor for the CLOCK_REALTIME timer. + */ +#define REAL_FILENO 5 + +/** + * The file descriptor for the lock file. + */ +#define LOCK_FILENO 6 + + /** * A queued job. @@ -84,6 +125,30 @@ struct job { /** + * `dup2(OLD, NEW)` and, on success, `close(OLD)`. + * + * @param OLD:int The file descriptor to duplicate and close. + * @param NEW:int The new file descriptor. + * @return 0 on success, -1 on error. + */ +#define DUP2_AND_CLOSE(OLD, NEW) (dup2(OLD, NEW) == -1 ? -1 : (close(OLD), 0)) + +/** + * Call `CALL` which returns a file descriptor. + * Than make sure that the file descriptor's + * number is `WANT`. Go to `fail' on error. + * + * @param FD:int variable The variable where the file descriptor shall be stored. + * @param WANT:int The file descriptor the file should have. + * @parma CALL:int call Call to function that creates and returns the file descriptor. + */ +#define GET_FD(FD, WANT, CALL) \ + t (FD = CALL, FD == -1); \ + t (dup2_and_null(FD, WANT) == -1); \ + FD = WANT + + +/** * Defines the function `void usage(void)` * that prints usage information. * @@ -100,7 +165,6 @@ usage(void) \ exit(2); \ } - /** * Declares `argv0` and its value to * a specified string. @@ -125,49 +189,159 @@ do { \ usage(); \ } while (0) +/** + * Macro to put directly after the variable definitions in `main`. + */ +#define PROLOGUE(USAGE_ASSUMPTION, ACCESS) \ + int state = -1; \ + if (argc > 0) argv0 = argv[0]; \ + if (!(USAGE_ASSUMPTION)) usage(); \ + GET_FD(state, STATE_FILENO, open_state(ACCESS, NULL)) /** - * Construct a message from `argv` - * to send to the daemon. + * Macro to put before the cleanup code in `main`. */ -#define CONSTRUCT_MESSAGE \ - n = measure_array(argv + 1); \ - t (n ? !(msg = malloc(n)) : 0); \ - store_array(msg, argv + 1) +#define CLEANUP_START \ + errno = 0; \ +fail: \ + if (errno) perror(argv[0]); \ + if (state >= 0) close(state) + +/** + * Macro to put after the cleanup code in `main`. + */ +#define CLEANUP_END \ + return !!errno + /** - * Send message to daemon. + * Wrapper for `pread` that reads the required amount of data. * - * @param cmd:enum command Command type. - * @param n:size_t The length of the message, 0 if - * `msg` is `NULL` or NUL-terminated. - * @param msg:char * The message to send. + * @param fildes See pread(3). + * @param buf See pread(3). + * @param nbyte See pread(3). + * @param offset See pread(3). + * @return See pread(3), only short if the file is shorter. */ -#define SEND(type, n, msg) \ -do { \ - if (send_command(type, n, msg)) { \ - t (errno); \ - free(msg); \ - return 3; \ - } \ -} while (0) +ssize_t preadn(int fildes, void *buf, size_t nbyte, size_t offset); +/** + * Wrapper for `pwrite` that writes all specified data. + * + * @param fildes See pwrite(3). + * @param buf See pwrite(3). + * @param nbyte See pwrite(3). + * @param offset See pwrite(3). + * @return See pwrite(3). + */ +ssize_t pwriten(int fildes, const void *buf, size_t nbyte, size_t offset); + +/** + * Unmarshal a `NULL`-terminated string array. + * + * The elements are not actually copied, subpointers + * to `buf` are stored in the returned list. + * + * @param buf The marshalled array. Must end with a NUL byte. + * @param len The length of `buf`. + * @param n Output parameter for the number of elements. May be `NULL` + * @return The list, `NULL` on error. + * + * @throws Any exception specified for realloc(3). + */ +char **restore_array(char *buf, size_t len, size_t *n); + +/** + * Create `NULL`-terminate subcopy of an list, + * + * @param list The list. + * @param n The number of elements in the new sublist. + * @return The sublist, `NULL` on error. + * + * @throws Any exception specified for malloc(3). + */ +char **sublist(char *const *list, size_t n); /** - * Exit the process with status indicating success. + * Create a new open file descriptor for an already + * existing file descriptor. * - * Defined the label `fail`. + * @param fd The file descriptor that shall be promoted + * to a new open file descriptor. + * @param oflag See open(3), `O_CREAT` is not allowed. + * @return 0 on success, -1 on error. + */ +int reopen(int fd, int oflag); + +/** + * Run a job or a hook. * - * @param msg The message send to the daemon. + * @param job The job. + * @param hook The hook, `NULL` to run the job. + * @return 0 on success, -1 on error, 1 if the child failed. */ -#define END(msg) \ - free(msg); \ - return 0; \ -fail: \ - perror(argv0); \ - free(msg); \ - return 1; +int run_job_or_hook(struct job *job, const char *hook); + +/** + * Removes (and optionally runs) a job. + * + * @param jobno The job number, `NULL` for any job. + * @param runjob Shall we run the job too? 2 if its time has expired (not forced). + * @return 0 on success, -1 on error. + * + * @throws 0 The job is not in the queue. + */ +int remove_job(const char *jobno, int runjob); + +/** + * Get a `NULL`-terminated list of all queued jobs. + * + * @return A `NULL`-terminated list of all queued jobs. `NULL` on error. + */ +struct job **get_jobs(void); + +/** + * Duplicate a file descriptor, and + * open /dev/null to the old file descriptor. + * However, if `old` is 3 or greater, it will + * be closed rather than /dev/null. + * + * @param old The old file descriptor. + * @param new The new file descriptor. + * @return `new`, -1 on error. + */ +int dup2_and_null(int old, int new); + +/** + * Create or open the state file. + * + * @param open_flags Flags (the second parameter) for `open`. + * @param state_path Output parameter for the state file's pathname. + * May be `NULL`; + * @return A file descriptor to the state file, -1 on error. + * + * @throws 0 `!(open_flags & O_CREAT)` and the file does not exist. + */ +int open_state(int open_flags, char **state_path); + +/** + * Let the daemon know that it may need to + * update the timers, and perhaps exit. + * + * @param start Start the daemon if it is not running? + * @param name The name of the process. + * @return 0 on success, -1 on error. + */ +int poke_daemon(int start, const char *name); + +/** + * Set SAT_HOOK_PATH. + * + * @return 0 on success, -1 on error. + */ +int set_hookpath(void); + /** diff --git a/src/daemon.h b/src/daemon.h deleted file mode 100644 index 1d4a747..0000000 --- a/src/daemon.h +++ /dev/null @@ -1,287 +0,0 @@ -/** - * Copyright © 2015, 2016 Mattias Andrée <maandree@member.fsf.org> - * - * 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. - */ -#ifndef _DEFAULT_SOURCE -# define _DEFAULT_SOURCE -#endif -#include "common.h" -#include <stddef.h> -#include <errno.h> -#include <string.h> -#include <time.h> -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <assert.h> -#include <sys/socket.h> - - - -/** - * 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 the CLOCK_BOOTTIME timer. - */ -#define BOOT_FILENO 5 - -/** - * The file descriptor for the CLOCK_REALTIME timer. - */ -#define REAL_FILENO 6 - -/** - * The file descriptor for the lock file. - */ -#define LOCK_FILENO 7 - - -/** - * Command: queue a job. - */ -#define SAT_QUEUE 0 - - - -/** - * `dup2(OLD, NEW)` and, on success, `close(OLD)`. - * - * @param OLD:int The file descriptor to duplicate and close. - * @param NEW:int The new file descriptor. - * @return 0 on success, -1 on error. - */ -#define DUP2_AND_CLOSE(OLD, NEW) (dup2(OLD, NEW) == -1 ? -1 : (close(OLD), 0)) - - - -/** - * Macro to put directly after the variable definitions in `main`. - */ -#define DAEMON_PROLOGUE \ - int rc = 0; \ - assert(argc == 3); \ - t (reopen(STATE_FILENO, O_RDWR)) \ - -/** - * Macro to put before the cleanup code in `main`. - */ -#define DAEMON_CLEANUP_START \ -done: \ - (void) send_string(SOCK_FILENO, 127, NULL); \ - shutdown(SOCK_FILENO, SHUT_WR); \ - close(SOCK_FILENO); \ - close(STATE_FILENO) - -/** - * Macro to put after the cleanup code in `main`. - */ -#define DAEMON_CLEANUP_END \ - return rc; \ -fail: \ - if (send_string(SOCK_FILENO, STDERR_FILENO, argv[0], ": ", strerror(errno), "\n", NULL)) \ - perror(argv[0]); \ - rc = 1; \ - goto done; \ - (void) argc - -/** - * Call `CALL` which returns a file descriptor. - * Than make sure that the file descriptor's - * number is `WANT`. Go to `fail' on error. - * - * @param FD:int variable The variable where the file descriptor shall be stored. - * @param WANT:int The file descriptor the file should have. - * @parma CALL:int call Call to function that creates and returns the file descriptor. - */ -#define GET_FD(FD, WANT, CALL) \ - t (FD = CALL, FD == -1); \ - t (dup2_and_null(FD, WANT) == -1); \ - FD = WANT - - -#define PROLOGUE(USAGE_ASSUMPTION, ...) \ - int state = -1; \ - if (argc > 0) argv0 = argv[0]; \ - if (!(USAGE_ASSUMPTION)) usage(); \ - GET_FD(state, STATE_FILENO, open_state(__VA_ARGS__)) - -#define CLEANUP_START \ - errno = 0; \ -fail: \ - if (errno) perror(argv[0]); \ - if (state >= 0) close(state) - -#define CLEANUP_END \ - return !!errno - - - -/** - * Wrapper for `pread` that reads the required amount of data. - * - * @param fildes See pread(3). - * @param buf See pread(3). - * @param nbyte See pread(3). - * @param offset See pread(3). - * @return See pread(3), only short if the file is shorter. - */ -ssize_t preadn(int fildes, void *buf, size_t nbyte, size_t offset); - -/** - * Wrapper for `pwrite` that writes all specified data. - * - * @param fildes See pwrite(3). - * @param buf See pwrite(3). - * @param nbyte See pwrite(3). - * @param offset See pwrite(3). - * @return See pwrite(3). - */ -ssize_t pwriten(int fildes, const void *buf, size_t nbyte, size_t offset); - -/** - * Wrapper for `read` that reads all available data. - * - * `errno` is set to `EBADMSG` on success. - * - * @param fd The file descriptor from which to to read. - * @param buf Output parameter for the data. - * @param n Output parameter for the number of read bytes. - * @return 0 on success, -1 on error. - * - * @throws Any exception specified for read(3). - * @throws Any exception specified for realloc(3). - */ -int readall(int fd, char **buf, size_t *n); - -/** - * Unmarshal a `NULL`-terminated string array. - * - * The elements are not actually copied, subpointers - * to `buf` are stored in the returned list. - * - * @param buf The marshalled array. Must end with a NUL byte. - * @param len The length of `buf`. - * @param n Output parameter for the number of elements. May be `NULL` - * @return The list, `NULL` on error. - * - * @throws Any exception specified for realloc(3). - */ -char **restore_array(char *buf, size_t len, size_t *n); - -/** - * Create `NULL`-terminate subcopy of an list, - * - * @param list The list. - * @param n The number of elements in the new sublist. - * @return The sublist, `NULL` on error. - * - * @throws Any exception specified for malloc(3). - */ -char **sublist(char *const *list, size_t n); - -/** - * Create a new open file descriptor for an already - * existing file descriptor. - * - * @param fd The file descriptor that shall be promoted - * to a new open file descriptor. - * @param oflag See open(3), `O_CREAT` is not allowed. - * @return 0 on success, -1 on error. - */ -int reopen(int fd, int oflag); - -/** - * Send a string to a client. - * - * @param sockfd The file descriptor of the socket. - * @param outfd The file descriptor to which the client shall output the message. - * @param ... `NULL`-terminated list of string to concatenate. - * @return 0 on success, -1 on error. - */ -int send_string(int sockfd, int outfd, ...); - -/** - * Run a job or a hook. - * - * @param job The job. - * @param hook The hook, `NULL` to run the job. - * @return 0 on success, -1 on error, 1 if the child failed. - */ -int run_job_or_hook(struct job *job, const char *hook); - -/** - * Removes (and optionally runs) a job. - * - * @param jobno The job number, `NULL` for any job. - * @param runjob Shall we run the job too? 2 if its time has expired (not forced). - * @return 0 on success, -1 on error. - * - * @throws 0 The job is not in the queue. - */ -int remove_job(const char *jobno, int runjob); - -/** - * Get a `NULL`-terminated list of all queued jobs. - * - * @return A `NULL`-terminated list of all queued jobs. `NULL` on error. - */ -struct job **get_jobs(void); - -/** - * Duplicate a file descriptor, and - * open /dev/null to the old file descriptor. - * However, if `old` is 3 or greater, it will - * be closed rather than /dev/null. - * - * @param old The old file descriptor. - * @param new The new file descriptor. - * @return `new`, -1 on error. - */ -int dup2_and_null(int old, int new); - -/** - * Create or open the state file. - * - * @param open_flags Flags (the second parameter) for `open`. - * @param state_path Output parameter for the state file's pathname. - * May be `NULL`; - * @return A file descriptor to the state file, -1 on error. - * - * @throws 0 `!(open_flags & O_CREAT)` and the file does not exist. - */ -int open_state(int open_flags, char **state_path); - -/** - * Let the daemon know that it may need to - * update the timers, and perhaps exit. - * - * @return 0 on success, -1 on error. - */ -int poke_daemon(void); - @@ -19,10 +19,8 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +#include "common.h" #include "parse_time.h" -#include "client.h" -#include <errno.h> -#include <unistd.h> @@ -32,36 +30,61 @@ USAGE("TIME COMMAND...") /** - * Queue a job for later execution. + * Return the number of bytes required to store a string array. * - * @param argc You guess! - * @param argv The first element should be the name of the process, - * the second argument should be the POSIX time (seconds - * since Epoch (1970-01-01 00:00:00 UTC), disregarding - * leap seconds) the job shall be executed. The rest of - * the arguments (being at least one) shoul be the - * command line arguments for the job the run. - * @param envp The environment. - * @return 0 The process was successful. - * @return 1 The process failed queuing the job. - * @return 2 User error, you do not know what you are doing. - * @return 3 satd(1) failed to queue the job. + * @param array The string array. + * @return The number of bytes required to store the array. */ -int -main(int argc, char *argv[], char *envp[]) +#ifdef __GNUC__ +__attribute__((__pure__)) +#endif +static size_t +measure_array(char *array[]) +{ + size_t rc = 0; + while (*array) rc += strlen(*array++) + 1; + return rc * sizeof(char); +} + + +/** + * Store a string array. + * + * @param storage The buffer where the array is to be stored. + * @param array The array to store. + * @return Where in the buffer the array ends. + */ +static char * +store_array(char *restrict storage, char *array[]) { -#define E(CASE, DESC) case CASE: return fprintf(stderr, "%s: %s: %s\n", argv0, DESC, argv[1]), 2 + for (; *array; array++, storage++) + storage = stpcpy(storage, *array); + return storage; +} - char *msg = NULL; + +/** + * Construct the job specifications, as a storable unit. + * + * @param argc `argc` from `main`, see `main` for descriptor. + * @param argv `argv` from `main`, see `main` for descriptor. + * @param envp `envp` from `main`, see `main` for descriptor. + * @return The job (sans serial number) on success, `NULL` on error. + */ +static struct job * +construct_job(int argc, char *argv[], char *envp[]) +{ +#define E(CASE, DESC) case CASE: fprintf(stderr, "%s: %s: %s\n", argv0, DESC, argv[1]), exit(2) + + char *dummy = NULL; char *timearg; void *new; size_t size = 64; struct job job = { .no = 0 }; + struct job *job_full = NULL; + int saved_errno; - if ((argc < 3) || (argv[1][0] == '-')) - usage(); - - argv0 = argv[0], timearg = argv[1]; + timearg = argv[1]; job.argc = argc -= 2, argv += 2; /* Parse the time argument. */ @@ -76,22 +99,70 @@ main(int argc, char *argv[], char *envp[]) retry: /* Get the size of the current working directory's pathname. */ - t (!(new = realloc(msg, size <<= 1))); - if (!getcwd(msg = new, size)) { + t (!(new = realloc(dummy, size <<= 1))); + if (!getcwd(dummy = new, size)) { t (errno != ERANGE); goto retry; } - size = strlen(getcwd(msg, size)) + 1, free(msg); + size = strlen(getcwd(dummy, size)) + 1; - /* Construct message to send to the daemon. */ + /* Construct full specification. */ job.n = measure_array(argv) + size + measure_array(envp); - t (!(msg = malloc(sizeof(job) + job.n))); - memcpy(msg, &job, sizeof(job)); - store_array(getcwd(store_array(msg + sizeof(job), argv), size) + size, envp); + t (!(job_full = malloc(sizeof(job) + job.n))); + memcpy(job_full, &job, sizeof(job)); + store_array(getcwd(store_array(job_full->payload, argv), size) + size, envp); + +fail: + return S(free(dummy)), job_full; +} + + +/** + * Queue a job for later execution. + * + * @param argc You guess! + * @param argv The first element should be the name of the process, + * the second argument should be the POSIX time (seconds + * since Epoch (1970-01-01 00:00:00 UTC), disregarding + * leap seconds) the job shall be executed. The rest of + * the arguments (being at least one) shoul be the + * command line arguments for the job the run. + * @param envp The environment. + * @return 0 The process was successful. + * @return 1 The process failed queuing the job. + * @return 2 User error, you do not know what you are doing. + * @return 3 satd(1) failed to queue the job. + */ +int +main(int argc, char *argv[], char *envp[]) +{ +#define WRITE(MSG, N, OFF) t (pwriten(STATE_FILENO, MSG, (size_t)(N), (size_t)(OFF)) < (ssize_t)(N)) + + struct job *job = NULL; + struct stat attr; + ssize_t r; + PROLOGUE((argc > 2) && (argv[1][0] != '-'), O_RDWR); + t (set_hookpath()); + + t (!(job = construct_job(argc, argv, envp))); - /* Send job to daemon, start daemon if necessary. */ - SEND(SAT_QUEUE, sizeof(job) + job.n, msg); + /* Update state file and run hook. */ + t (flock(STATE_FILENO, LOCK_EX)); + t (fstat(STATE_FILENO, &attr)); + t (r = preadn(STATE_FILENO, &(job->no), sizeof(job->no), (size_t)0), r < 0); + if (r < (ssize_t)sizeof(job->no)) job->no = 0; + else job->no += 1; + WRITE(&(job->no), sizeof(job->no), (size_t)0); + if (attr.st_size < (off_t)sizeof(job->no)) + attr.st_size = (off_t)sizeof(job->no); + WRITE(job, sizeof(*job) + job->n, (size_t)(attr.st_size)); + fsync(STATE_FILENO); + run_job_or_hook(job, "queued"); + t (flock(STATE_FILENO, LOCK_UN)); - END(msg); + t (poke_daemon(1, argv0)); + CLEANUP_START; + free(job); + CLEANUP_END; } diff --git a/src/satd-add.c b/src/satd-add.c deleted file mode 100644 index c550072..0000000 --- a/src/satd-add.c +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright © 2015, 2016 Mattias Andrée <maandree@member.fsf.org> - * - * 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 "daemon.h" -#include <sys/file.h> -#include <sys/stat.h> - - - -/** - * Subroutine to the sat daemon: add job. - * - * @param argc Should be 3. - * @param argv The name of the process, the pathname of the socket, - * and the pathname to the state file. - * @return 0 The process was successful. - * @return 1 The process failed queuing the job. - */ -int -main(int argc, char *argv[]) -{ -#define M ((struct job *)message) - - size_t n = 0, elements = 0, i; - ssize_t r; - char *message = NULL; - struct stat attr; - DAEMON_PROLOGUE; - - /* Receive and validate message. */ - t (readall(SOCK_FILENO, &message, &n) || (n < sizeof(struct job))); - t (M->n != (n -= sizeof(struct job))); - t ((M->argc < 1) || !n || message[sizeof(struct job) - 1 + n]); - for (i = n; i--; elements += !message[sizeof(struct job) + i]); - t (elements < (size_t)(M->argc)); - - /* Update state file and run hook. */ - t (flock(STATE_FILENO, LOCK_EX)); - t (fstat(STATE_FILENO, &attr)); - t (r = preadn(STATE_FILENO, &(M->no), sizeof(M->no), (size_t)0), r < 0); - if (r < (ssize_t)sizeof(M->no)) - M->no = 0; - else - M->no += 1; - t (pwriten(STATE_FILENO, &(M->no), sizeof(M->no), (size_t)0) < (ssize_t)sizeof(M->no)); - if (attr.st_size < (off_t)sizeof(M->no)) - attr.st_size = (off_t)sizeof(M->no); - n += sizeof(struct job); - t (pwriten(STATE_FILENO, message, n, (size_t)(attr.st_size)) < (ssize_t)n); - fsync(STATE_FILENO); - run_job_or_hook(M, "queued"); - t (flock(STATE_FILENO, LOCK_UN)); - - DAEMON_CLEANUP_START; - free(message); - DAEMON_CLEANUP_END; -} - diff --git a/src/satd-diminished.c b/src/satd-diminished.c index 0ea60d2..9d5000a 100644 --- a/src/satd-diminished.c +++ b/src/satd-diminished.c @@ -19,17 +19,8 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "daemon.h" -#include <unistd.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <signal.h> -#include <stdint.h> +#include "common.h" #include <sys/wait.h> -#include <sys/stat.h> -#include <sys/select.h> -#include <sys/timerfd.h> @@ -100,20 +91,15 @@ sighandler(int signo) /** - * Spawn a libexec. + * Spawn a satd-timer. * - * @param command The command to spawn, -1 for "timer". - * @param fd File descriptor to the socket, -1 iff `command` is -1. - * @param argv `argv` from `main`. - * @param envp `envp` from `main`. - * @return 0 on success, -1 on error. + * @param argv `argv` from `main`. + * @param envp `envp` from `main`. + * @return 0 on success, -1 on error. */ static int -spawn(int command, int fd, char *argv[], char *envp[]) +spawn(char *argv[], char *envp[]) { -#define IMAGE(CASE, NAME) case CASE: image = DAEMON_IMAGE(NAME); break - - const char *image; pid_t pid; /* Try forking until we success. */ @@ -126,31 +112,14 @@ spawn(int command, int fd, char *argv[], char *envp[]) /* Parent. */ if (pid) { child_count++; - if (command < 0) - timer_pid = pid; + timer_pid = pid; return 0; } /* Child. */ - switch (command) { - IMAGE(SAT_QUEUE, "add"); - IMAGE(-1, "timer"); - default: - fprintf(stderr, "%s: invalid command received.\n", argv[0]); - goto silent_fail; - } - if (command < 0) { - close(LOCK_FILENO), close(SOCK_FILENO); - } else { - close(LOCK_FILENO), close(BOOT_FILENO), close(REAL_FILENO); - t (DUP2_AND_CLOSE(fd, SOCK_FILENO) == -1); - fd = SOCK_FILENO; - } - execve(image, argv, envp); -fail: + close(LOCK_FILENO); + execve(DAEMON_IMAGE("timer"), argv, envp); perror(argv[0]); -silent_fail: - close(fd); exit(1); } @@ -193,9 +162,8 @@ test_timer(int fd, const fd_set *fdset) /** * The sat daemon. * - * @param argc Should be 3. - * @param argv The name of the process, the pathname of the socket, - * and the pathname to the state file. + * @param argc Should be 2. + * @param argv The name of the process, and the pathname to the state file. * @param envp The environment. * @return 0 The process was successful. * @return 1 The process failed queuing the job. @@ -203,8 +171,7 @@ test_timer(int fd, const fd_set *fdset) int main(int argc, char *argv[], char *envp[]) { - int fd = -1, rc = 0, accepted = 0, r, expired = 0; - unsigned char type; + int fd = -1, rc = 0, r, expired = 0; fd_set fdset; struct stat attr; @@ -221,16 +188,16 @@ again: } /* Need to set new timer values? */ if (expired || ((received_signo == SIGCHLD) && (timer_pid == NO_TIMER_SPAWNED))) - t (expired = 0, spawn(-1, -1, argv, envp)); + t (expired = 0, spawn(argv, envp)); received_signo = 0; #if 1 || !defined(DEBUG) /* Can we quit yet? */ - if (accepted && !child_count) { + if (expired && !child_count) { t (r = is_timer_set(BOOT_FILENO), r < 0); if (r) goto not_done; t (r = is_timer_set(REAL_FILENO), r < 0); if (r) goto not_done; t (fstat(STATE_FILENO, &attr)); if (attr.st_size > (off_t)sizeof(size_t)) - t (spawn(-1, -1, argv, envp)); + t (spawn(argv, envp)); else goto done; } @@ -238,7 +205,6 @@ again: not_done: /* Wait for something to happen. */ FD_ZERO(&fdset); - FD_SET(SOCK_FILENO, &fdset); FD_SET(BOOT_FILENO, &fdset); FD_SET(REAL_FILENO, &fdset); /* This is the highest one. */ if (select(REAL_FILENO + 1, &fdset, NULL, NULL, NULL) == -1) { @@ -249,35 +215,19 @@ not_done: t ((expired |= test_timer(BOOT_FILENO, &fdset)) < 0); t ((expired |= test_timer(REAL_FILENO, &fdset)) < 0); /* Accept connections. */ - if (!FD_ISSET(SOCK_FILENO, &fdset)) - goto again; - if (fd = accept(SOCK_FILENO, NULL, NULL), fd == -1) { - t ((errno != ECONNABORTED) && (errno != EINTR)); - /* Including EMFILE, ENFILE, and ENOMEM because of potential resource leak. */ - goto again; - } - accepted = 1; - if (read(fd, &type, sizeof(type)) <= 0) - perror(argv[0]); - else - t (spawn((int)type, fd, argv, envp)); - close(fd), fd = -1; goto again; fail: perror(argv[0]); - if (fd >= 0) - close(fd); + close(fd); rc = 1; done: while (waitpid(-1, NULL, 0) > 0); unlink(argv[1]); if (!rc) unlink(argv[2]); - close(SOCK_FILENO); close(STATE_FILENO); return rc; - (void) argc; } diff --git a/src/satd-timer.c b/src/satd-timer.c index 113b50a..86feb6a 100644 --- a/src/satd-timer.c +++ b/src/satd-timer.c @@ -19,9 +19,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "daemon.h" -#include <time.h> -#include <sys/timerfd.h> +#include "common.h" @@ -43,9 +41,8 @@ timecmp(const struct timespec *a, const struct timespec *b) /** * Subroutine to the sat daemon: list jobs. * - * @param argc Should be 3. - * @param argv The name of the process, the pathname of the socket, - * and the pathname to the state file. + * @param argc Should be 2. + * @param argv The name of the process, and the pathname to the state file. * @return 0 The process was successful. * @return 1 The process failed queuing the job. */ @@ -19,31 +19,8 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "daemon.h" +#include "common.h" #include "daemonise.h" -#include <unistd.h> -#include <errno.h> -#include <pwd.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/un.h> -#include <sys/file.h> -#include <sys/timerfd.h> - - - -/** - * The size of the backlog on the socket. - */ -#ifndef SATD_BACKLOG -# define SATD_BACKLOG 5 -#endif -#if SATD_BACKLOG > SOMAXCONN -# undef SATD_BACKLOG -# defined SATD_BACKLOG SOMAXCONN -#endif - -#define close(fd) (fd < 0 ? 0 : close(fd)) @@ -53,40 +30,6 @@ USAGE("[-f]") /** - * Create the socket. - * - * @param address Output parameter for the socket address. - * @return The file descriptor for the socket, -1 on error. - */ -static int -create_socket(struct sockaddr_un *address) -{ - const void *_cvoid; - const char *dir; - int fd = -1, saved_errno; - - /* Get socket address. */ - dir = getenv("XDG_RUNTIME_DIR"), dir = (dir ? dir : "/run"); - if (strlen(dir) + sizeof("/" PACKAGE "/socket") > sizeof(address->sun_path)) - t ((errno = ENAMETOOLONG)); - stpcpy(stpcpy(address->sun_path, dir), "/" PACKAGE "/socket"); - address->sun_family = AF_UNIX; - - /* Create socket. */ - unlink(address->sun_path); - t ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1); - t (fchmod(fd, S_IRWXU)); - t (bind(fd, (const struct sockaddr *)(_cvoid = address), (socklen_t)sizeof(*address))); - /* EADDRINUSE just means that the file already exists, not that it is actually used. */ - - return fd; -fail: - saved_errno = errno, close(fd), errno = saved_errno; - return -1; -} - - -/** * Create and lock the lock file, and its directory. * * @return A file descriptor to the lock file, -1 on error. @@ -97,6 +40,7 @@ create_lock(void) const char *dir; char *path; char *p; + pid_t pid = getpid(); int fd = -1, saved_errno = 0; /* Create directory. */ @@ -117,9 +61,13 @@ create_lock(void) goto fail; } + /* Store PID in the file. */ + /* Yes it is coming similar to a PID file, but this works if the started with -f. */ + t (pwrite(fd, &pid, sizeof(pid), (off_t)0) < (size_t)sizeof(pid)); + goto done; fail: - saved_errno = errno, close(fd); + saved_errno = errno, close(fd), fd = -1; done: free(path), errno = saved_errno; return fd; @@ -127,40 +75,6 @@ done: /** - * Construct the pathname for the hook script. - * - * @param env The environment variable to use for the beginning - * of the pathname, `NULL` for the home directory. - * @param suffix The rest of the pathname. - * @return The pathname. - * - * @throws 0 The environment variable is not set, or, if `env` is - * `NULL` the user is root or homeless. - */ -static char * -hookpath(const char *env, const char *suffix) -{ - struct passwd *pwd; - const char *prefix = NULL; - char *path; - - if (env) { - prefix = getenv(env); - } else if (getuid()) { - pwd = getpwuid(getuid()); - prefix = pwd ? pwd->pw_dir : NULL; - } - if (!prefix || !*prefix) - return errno = 0, NULL; - - t (!(path = malloc((strlen(prefix) + strlen(suffix) + 1) * sizeof(char)))); - stpcpy(stpcpy(path, prefix), suffix); -fail: - return path; -} - - -/** * The sat daemon initialisation. * * @param argc Any value in [0, 2] is accepted. @@ -173,11 +87,7 @@ fail: int main(int argc, char *argv[]) { -#define HOOKPATH(PRE, SUF) \ - t (path = path ? path : hookpath(PRE, SUF), !path && errno) - - struct sockaddr_un address; - int sock = -1, state = -1, boot = -1, real = -1, lock = -1, foreground = 0; + int state = -1, boot = -1, real = -1, lock = -1, foreground = 0; char *path = NULL; struct itimerspec spec; @@ -188,18 +98,11 @@ main(int argc, char *argv[]) usage(); /* Get hook-script pathname. */ - if (!getenv("SAT_HOOK_PATH")) { - HOOKPATH("XDG_CONFIG_HOME", "/sat/hook"); - HOOKPATH("HOME", "/.config/sat/hook"); - HOOKPATH(NULL, "/.config/sat/hook"); - t (setenv("SAT_HOOK_PATH", path ? path : "/etc/sat/hook", 1)); - free(path), path = NULL; - } + t (set_hookpath()); /* Open/create lock file and state file, and create socket. */ GET_FD(lock, LOCK_FILENO, create_lock()); GET_FD(state, STATE_FILENO, open_state(O_RDWR | O_CREAT, &path)); - GET_FD(sock, SOCK_FILENO, create_socket(&address)); /* Create timers. */ GET_FD(boot, BOOT_FILENO, timerfd_create(CLOCK_BOOTTIME, 0)); @@ -210,21 +113,17 @@ main(int argc, char *argv[]) t (timerfd_settime(boot, TFD_TIMER_ABSTIME, &spec, NULL)); t (timerfd_settime(real, TFD_TIMER_ABSTIME, &spec, NULL)); - /* Listen for incoming conections. */ - t (listen(sock, SATD_BACKLOG)); - /* Daemonise. */ - t (foreground ? 0 : daemonise("satd", DAEMONISE_KEEP_FDS | DAEMONISE_NEW_PID, 3, 4, 5, 6, 7, -1)); + t (foreground ? 0 : daemonise("satd", DAEMONISE_KEEP_FDS | DAEMONISE_NEW_PID, 3, 4, 5, 6, -1)); /* Change to a process image without all this initialisation text. */ - execl(LIBEXECDIR "/" PACKAGE "/satd-diminished", argv0, address.sun_path, path, NULL); + execl(LIBEXECDIR "/" PACKAGE "/satd-diminished", argv0, path, NULL); fail: if (errno) perror(argv0); free(path); - if (sock >= 0) unlink(address.sun_path); - close(sock), close(state), close(boot), close(real), close(lock); + close(state), close(boot), close(real), close(lock); undaemonise(); return 1; } @@ -19,7 +19,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "daemon.h" +#include "common.h" #include <stdarg.h> @@ -223,9 +223,7 @@ print_job(struct job *job) ARRAY(envp + 1); t (print("\n\n", NULL)); done: - saved_errno = errno; - free(qstr), free(args), free(argv), free(wdir), free(envp); - errno = saved_errno; + S(free(qstr), free(args), free(argv), free(wdir), free(envp)); return rc; fail: rc = -1; @@ -247,7 +245,7 @@ main(int argc, char *argv[]) { struct job **jobs = NULL; struct job **job; - PROLOGUE(argc < 2, O_RDONLY, NULL); + PROLOGUE(argc < 2, O_RDONLY); t (!(jobs = get_jobs())); for (job = jobs; *job; job++) @@ -19,7 +19,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "daemon.h" +#include "common.h" @@ -41,8 +41,9 @@ USAGE("[JOB-ID]...") int main(int argc, char *argv[]) { - PROLOGUE(1, O_RDWR, NULL); + PROLOGUE(1, O_RDWR); NO_OPTIONS; + t (set_hookpath()); if (argc > 1) { for (argv++; *argv; argv++) @@ -51,8 +52,8 @@ main(int argc, char *argv[]) while (!remove_job(NULL, 1)); t (errno); } - t (poke_daemon()); + t (poke_daemon(0, argv0)); CLEANUP_START; CLEANUP_END; } diff --git a/src/satrm.c b/src/satrm.c index 115373c..ac3b5fc 100644 --- a/src/satrm.c +++ b/src/satrm.c @@ -19,7 +19,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "daemon.h" +#include "common.h" @@ -42,13 +42,14 @@ USAGE("JOB-ID...") int main(int argc, char *argv[]) { - PROLOGUE(argc >= 2, O_RDWR, NULL); + PROLOGUE(argc >= 2, O_RDWR); NO_OPTIONS; + t (set_hookpath()); for (argv++; *argv; argv++) t (remove_job(*argv, 0) && errno); - t (poke_daemon()); + t (poke_daemon(0, argv0)); CLEANUP_START; CLEANUP_END; } |