/** * Copyright © 2015, 2016 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. */ #ifndef _DEFAULT_SOURCE # define _DEFAULT_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include /** * 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 # define t(...) do { if ((__VA_ARGS__) ? (failed__ = #__VA_ARGS__) : 0) { (perror)(failed__); goto fail; } } while (0) static const char *failed__ = NULL; # define perror(_) ((void)(_)) # endif #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. */ struct job { /** * The job number. */ size_t no; /** * The number of “argv” elements in `payload`. */ int argc; /** * The clock in which `ts` is measured. */ clockid_t clk; /** * The time when the job shall be executed. */ struct timespec ts; /** * The number of bytes in `payload`. */ size_t n; /** * “argv” followed by “envp”. */ char payload[]; }; /** * `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. * * @param synopsis:const char* The command's synopsis sans command name. * `NULL` if there are not arguments. */ #define USAGE(synopsis) \ static void \ usage(void) \ { \ fprintf(stderr, "usage: %s%s%s\n", \ strrchr(argv0, '/') ? (strrchr(argv0, '/') + 1) : argv0, \ synopsis ? " " : "", synopsis ? synopsis : ""); \ exit(2); \ } /** * Declares `argv0` and its value to * a specified string. * * @param name:sitrng literal The name of the command. */ #define COMMAND(name) \ const char *argv0 = name; /** * Print usage and exit if there is any argument * that is an option. */ #define NO_OPTIONS \ do { \ int i; \ if (!strcmp(argv[1], "--")) \ argv++, argc--; \ for (i = 1; i < argc; i++) \ if (strchr("-", argv[i][0])) \ 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)) /** * Macro to put before the cleanup code in `main`. */ #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 /** * 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); /** * 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); /** * 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. * * @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); /** * This block of code allows us to compile with DEBUG=valgrind * or DEBUG=strace and have all exec:s be wrapped in * valgrind --leak-check=full --show-leak-kinds=all or * strace, respectively. Very useful for debugging. However, * children do not inherit strace, so between forking and * exec:ing we do not have strace. */ #if 1 && defined(DEBUG) # if ((DEBUG == 2) || (DEBUG == 3)) # ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" __attribute__((__used__)) # endif # if DEBUG == 2 # define DEBUGPROG "strace" # else # define DEBUGPROG "valgrind" # endif # define execl(path, _, ...) (execl)("/usr/bin/" DEBUGPROG, "/usr/bin/" DEBUGPROG, path, __VA_ARGS__) # define execve(...) execve_(__VA_ARGS__) static int execve_(const char *path, char *const argv[], char *const envp[]) { size_t n = 0; char **new_argv = NULL; int x = (DEBUG - 2) * 2, saved_errno; while (argv[n++]); t (!(new_argv = malloc((n + 1 + (size_t)x) * sizeof(char *)))); new_argv[x] = "--show-leak-kinds=all"; new_argv[1] = "--leak-check=full"; new_argv[0] = "/usr/bin/" DEBUGPROG; new_argv[1 + x] = path; memcpy(new_argv + 2 + x, argv + 1, (n - 1) * sizeof(char *)); (execve)(*new_argv, new_argv, envp); fail: return saved_errno = errno, free(new_argv), errno = saved_errno, -1; } # ifdef __GNUC__ # pragma GCC diagnostic pop # endif # endif #endif