diff options
Diffstat (limited to 'src/sat.c')
-rw-r--r-- | src/sat.c | 139 |
1 files changed, 105 insertions, 34 deletions
@@ -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; } |