From 04a2637f23a9d2b1aee83e364450a02377b44cbc Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Fri, 1 Jan 2016 08:39:41 +0100 Subject: small improvements and cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/client.c | 20 +++------ src/client.h | 2 +- src/common.h | 8 ++-- src/daemon.c | 79 +++++++++++++--------------------- src/daemon.h | 21 ++++++--- src/daemonise.c | 2 +- src/daemonise.h | 2 +- src/parse_time.c | 107 ++++++++++++++++++++++------------------------ src/parse_time.h | 2 +- src/sat.c | 10 ++--- src/satd-add.c | 16 +++---- src/satd-diminished.c | 101 +++++++++++++++++++++++--------------------- src/satd-list.c | 115 ++++++++++++++++++-------------------------------- src/satd-rm.c | 5 +-- src/satd-run.c | 7 +-- src/satd-timer.c | 2 +- src/satd.c | 96 +++++++++++++++-------------------------- src/satq.c | 2 +- src/satr.c | 2 +- src/satrm.c | 2 +- 20 files changed, 257 insertions(+), 344 deletions(-) diff --git a/src/client.c b/src/client.c index b37b9e3..3573f46 100644 --- a/src/client.c +++ b/src/client.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -99,7 +99,7 @@ send_command(enum command cmd, size_t n, const char *restrict msg) /* 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)) == -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_)); @@ -133,10 +133,8 @@ receive_again: goto receive_again; done: - shutdown(fd, SHUT_RD); - close(fd); - errno = 0; - return -goterr; + shutdown(fd, SHUT_RD), close(fd); + return errno = 0, -goterr; fail: saved_errno = (goterr ? 0 : errno); @@ -144,8 +142,7 @@ fail: close(fd); free(buf); errno = saved_errno; - if (eot) - goto done; + if (eot) goto done; return -1; } @@ -160,8 +157,7 @@ size_t measure_array(char *array[]) { size_t rc = 0; - for (; *array; array++) - rc += strlen(*array) + 1; + while (*array) rc += strlen(*array++) + 1; return rc * sizeof(char); } @@ -176,10 +172,8 @@ measure_array(char *array[]) char * store_array(char *restrict storage, char *array[]) { - for (; *array; array++) { + for (; *array; array++, storage++) storage = stpcpy(storage, *array); - *storage++ = 0; - } return storage; } diff --git a/src/client.h b/src/client.h index 22ff1ac..a7640e7 100644 --- a/src/client.h +++ b/src/client.h @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), diff --git a/src/common.h b/src/common.h index 1401c37..68d465e 100644 --- a/src/common.h +++ b/src/common.h @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -35,12 +35,12 @@ * * @param ... The statement. */ -# ifdef DEBUG +# 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)(_)) -# else -# define t(...) do { if (__VA_ARGS__) goto fail; } while (0) # endif #endif diff --git a/src/daemon.c b/src/daemon.c index e9c79d9..e4c444e 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -52,9 +52,9 @@ extern char **environ; if (r == 0) \ break; \ n += r; \ - nbyte -= (size_t)r; \ + buffer += r; \ offset += (size_t)r; \ - buffer += (size_t)r; \ + nbyte -= (size_t)r; \ } \ done: \ sigprocmask(SIG_SETMASK, &oldmask, NULL); \ @@ -103,8 +103,6 @@ pwriten(int fildes, const void *buf, size_t nbyte, size_t offset) /** * Wrapper for `read` that reads all available data. * - * Sets `errno` 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. @@ -118,21 +116,20 @@ readall(int fd, char **buf, size_t *n) { char *buffer = NULL; size_t ptr = 0, size = 128; - ssize_t got = 1; + ssize_t got; char *new; int saved_errno; - for (; got; ptr += (size_t)got) { + 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, ptr); + new = realloc(buffer, *n = ptr); *buf = ptr ? (new ? new : buffer) : NULL; - *n = ptr; shutdown(SOCK_FILENO, SHUT_RD); return 0; @@ -164,15 +161,12 @@ restore_array(char *buf, size_t len, size_t *n) char **rc = malloc((len + 1) * sizeof(char*)); char **new = NULL; size_t i = 0, e = 0; + t (!rc); - while (i < len) { - rc[e++] = buf + i; - i += strlen(buf + i) + 1; - } - rc[e] = NULL; - new = realloc(rc, (e + 1) * sizeof(char*)); - if (n) - *n = e; + while (i < len) i += strlen(rc[e++] = buf + i) + 1; + if (n) *n = e; + rc[e++] = NULL; + new = realloc(rc, e * sizeof(char*)); fail: return new ? new : rc; } @@ -216,9 +210,9 @@ reopen(int fd, int oflag) sprintf(path, "/dev/fd/%i", fd); if (r = open(path, oflag), r < 0) return -1; - if (dup2(r, fd) == -1) + if (DUP2_AND_CLOSE(r, fd) == -1) return saved_errno = errno, close(r), errno = saved_errno, -1; - return close(r), 0; + return 0; } @@ -245,7 +239,7 @@ send_string(int sockfd, int outfd, ...) va_end(args); t (write(sockfd, &out, sizeof(out)) < (ssize_t)sizeof(out)); - t (write(sockfd, &n, sizeof(n)) < (ssize_t)sizeof(n)); + t (write(sockfd, &n, sizeof(n)) < (ssize_t)sizeof(n)); va_start(args, outfd); while ((s = va_arg(args, const char *))) @@ -290,28 +284,19 @@ run_job_or_hook(struct job *job, const char *hook) argv[1] = (strstr)(hook, hook); /* strstr: just to remove a warning */ } - switch ((pid = fork())) { - case -1: - goto fail; - case 0: - close(SOCK_FILENO); - close(STATE_FILENO); - close(BOOT_FILENO); - close(REAL_FILENO); + if (!(pid = fork())) { + close(SOCK_FILENO), close(STATE_FILENO); + close(BOOT_FILENO), close(REAL_FILENO); (void)(status = chdir(envp[0])); environ = envp + 1; execvp(*argv, argv); exit(1); - default: - t (waitpid(pid, &status, 0) != pid); - break; } + t ((pid < 0) || (waitpid(pid, &status, 0) != pid)); fail: saved_errno = errno; - free(args); - free(argv); - free(envp); + free(args), free(argv), free(envp); errno = saved_errno; return status ? 1 : -!!saved_errno; } @@ -336,7 +321,7 @@ remove_job(const char *jobno, int runjob) struct stat attr; struct job job; struct job *job_full = NULL; - int rc = 0, saved_errno; + int rc = 0, saved_errno = 0; if (jobno) { no = (errno = 0, strtoul)(jobno, &end, 10); @@ -351,11 +336,11 @@ remove_job(const char *jobno, int runjob) if (!jobno || (job.no == no)) goto found_it; } - t (flock(STATE_FILENO, LOCK_UN)); + flock(STATE_FILENO, LOCK_UN); /* Failure isn't fatal. */ return errno = 0, -1; found_it: - job_full = malloc(sizeof(job) + job.n); + t (!(job_full = malloc(sizeof(job) + job.n))); *job_full = job; t (preadn(STATE_FILENO, job_full->payload, job.n, off + sizeof(job)) < (ssize_t)(job.n)); n -= off + sizeof(job) + job.n; @@ -372,21 +357,19 @@ found_it: saved_errno = errno; run_job_or_hook(job_full, rc ? "failure" : "success"); rc = rc == 1 ? 0 : rc; - free(job_full); - errno = saved_errno; } else { run_job_or_hook(job_full, "removed"); - free(job_full); } - flock(STATE_FILENO, LOCK_UN); /* Unlock late so that hooks are synchronised. */ + free(job_full); + flock(STATE_FILENO, LOCK_UN); /* Unlock late so that hooks are synchronised. Failure isn't fatal. */ + errno = saved_errno; return rc; fail: saved_errno = errno; flock(STATE_FILENO, LOCK_UN); - free(buf); - free(job_full); + free(buf), free(job_full); errno = saved_errno; return -1; } @@ -418,16 +401,14 @@ get_jobs(void) t (preadn(STATE_FILENO, js[j++]->payload, job.n, off) < (ssize_t)(job.n)); off += job.n; } - js[j] = NULL; t (flock(STATE_FILENO, LOCK_UN)); - return js; + return js[j] = NULL, js; fail: saved_errno = errno; - while (j--) - free(js[j]); - free(js); flock(STATE_FILENO, LOCK_UN); + while (j--) free(js[j]); + free(js); errno = saved_errno; return NULL; } diff --git a/src/daemon.h b/src/daemon.h index 5aaa1c6..d860d1f 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -89,17 +89,28 @@ * * @param ... The statement. */ -# ifdef DEBUG +# 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)(_)) -# else -# define t(...) do { if (__VA_ARGS__) goto fail; } while (0) # endif #endif +/** + * `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`. */ @@ -194,8 +205,6 @@ ssize_t pwriten(int fildes, const void *buf, size_t nbyte, size_t offset); /** * Wrapper for `read` that reads all available data. * - * Sets `errno` 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. diff --git a/src/daemonise.c b/src/daemonise.c index f8da122..822e0ed 100644 --- a/src/daemonise.c +++ b/src/daemonise.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), diff --git a/src/daemonise.h b/src/daemonise.h index 6585154..7bcea08 100644 --- a/src/daemonise.h +++ b/src/daemonise.h @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), diff --git a/src/parse_time.c b/src/parse_time.c index 83b0e65..4d4e92d 100644 --- a/src/parse_time.c +++ b/src/parse_time.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -31,6 +31,9 @@ +#define W(...) do { __VA_ARGS__ } while (0) + + /** * The number of seconds in a day. */ @@ -43,15 +46,22 @@ */ #define FAIL(e) return errno = (e), -1 +/** + * Set errno to EINVAL and return if a condition is not met. + * + * @return cond:int The condition. + */ +#define REQUIRE(cond) W(if (!(cond)) FAIL(EINVAL);) + /** * `a *= b` with overflow check. */ -#define MUL(a, b) if (a > timemax / (b)) FAIL(ERANGE); else a *= (b) +#define MUL(a, b) W(if (a > timemax / (b)) FAIL(ERANGE); else a *= (b);) /** * `a += b` with overflow check. */ -#define ADD(a, b) if (a > timemax - (b)) FAIL(ERANGE); else a += (b) +#define ADD(a, b) W(if (a > timemax - (b)) FAIL(ERANGE); else a += (b);) @@ -63,7 +73,7 @@ extern char *argv0; /** * The highest value that can be stored in `time_t`. */ -const time_t timemax = (sizeof(time_t) == sizeof(long long int)) ? LLONG_MAX : LONG_MAX; +static const time_t timemax = (sizeof(time_t) == sizeof(long long int)) ? (time_t)LLONG_MAX : (time_t)LONG_MAX; @@ -90,24 +100,18 @@ strtotime(const char *str, const char **end) # pragma GCC diagnostic ignored "-Wcast-qual" #endif time_t rc; - long long int rcll; - long int rcl; - if (!isdigit(*str)) - FAIL(EINVAL); + REQUIRE(isdigit(*str)); + errno = 0; /* The outer if-statement is for when `time_t` is update to `long long int`. * which is need to avoid the year 2038 problem. Be we have to use `strtol` * if `time_t` is `long int`, otherwise we will not detect overflow. */ - errno = 0; - if (sizeof(time_t) == sizeof(long long int)) { - rcll = strtoll(str, (char **)end, 10); - rc = (time_t)rcll; - } else { - rcl = strtol(str, (char **)end, 10); - rc = (time_t)rcl; - } + if (sizeof(time_t) == sizeof(long long int)) + rc = (time_t)strtoll(str, (char **)end, 10); + else + rc = (time_t)strtol(str, (char **)end, 10); return errno ? 0 : rc; #ifdef __GNUC__ @@ -119,40 +123,35 @@ strtotime(const char *str, const char **end) /** * Parse a time on the format HH:MM[:SS]. * - * @param str The time string. + * @param str Pointer to time string, will be updated to the end + * of the parsing of it. * @param ts Output parameter for the POSIX time the string * represents. - * @param end Output parameter for the end of the parsing of `str`. * @return 0 on success, -1 on error. * * @throws EINVAL `str` could not be parsed. * @throws ERANGE `str` specifies a time beyond what can be stored. */ static int -parse_time_time(const char *str, struct timespec *ts, const char **end) +parse_time_time(const char **str, struct timespec *ts) { time_t tm; - memset(ts, 0, sizeof(*ts)); - ts->tv_sec = strtotime(str, end), str = *end; - t (errno); + /* Hours. */ + t (ts->tv_sec = strtotime(*str, str), errno); /* Must not be restricted to 23, beyond 24 is legal. */ MUL(ts->tv_sec, (time_t)(60 * 60)); - if (*str++ != ':') - FAIL(EINVAL); - tm = strtotime(str, end), str = *end; - t (errno); - if (tm >= 60) - FAIL(EINVAL); + /* Minutes. */ + REQUIRE(*(*str)++ == ':'); + t (tm = strtotime(*str, str), errno); + REQUIRE(tm < 60); MUL(tm, (time_t)60); ADD(ts->tv_sec, tm); - if (*str++ != ':') - return 0; - - tm = strtotime(str, end); - t (errno); + /* Seconds. */ + if (*(*str)++ != ':') return 0; + t (tm = strtotime(*str, str), errno); /* Do not restrict to 59, or 60, there can be more than +1 leap second. */ ADD(ts->tv_sec, tm); @@ -165,20 +164,19 @@ fail: /** * Parse a time with only a second-count. * - * @param str The time string. + * @param str Pointer to time string, will be updated to the end + * of the parsing of it. * @param ts Output parameter for the POSIX time the string * represents. - * @param end Output parameter for the end of the parsing of `str`. * @return 0 on success, -1 on error. * * @throws EINVAL `str` could not be parsed. * @throws ERANGE `str` specifies a time beyond what can be stored. */ static int -parse_time_seconds(const char *str, struct timespec *ts, const char **end) +parse_time_seconds(const char **str, struct timespec *ts) { - memset(ts, 0, sizeof(*ts)); - ts->tv_sec = strtotime(str, end); + ts->tv_sec = strtotime(*str, str); return errno ? -1 : 0; } @@ -208,12 +206,15 @@ parse_time_seconds(const char *str, struct timespec *ts, const char **end) int parse_time(const char *str, struct timespec *ts, clockid_t *clk) { +#define FIX_NSEC(T) (((T)->tv_nsec >= 1000000000L) ? ((T)->tv_sec += 1, (T)->tv_nsec -= 1000000000L) : 0L) + struct timespec now; int points, plus = *str == '+'; const char *start = str; - const char *end; time_t adj; + ts->tv_nsec = 0; + /* Get current time and clock. */ *clk = plus ? CLOCK_BOOTTIME : CLOCK_REALTIME; clock_gettime(*clk, &now); @@ -226,14 +227,13 @@ parse_time(const char *str, struct timespec *ts, clockid_t *clk) } /* HH:MM[:SS[.NNNNNNNNN]] or seconds? */ - if (strchr(str, ':')) { - t (parse_time_time(str, ts, &end)); + if (strchr(str += plus, ':')) { + t (parse_time_time(&str, ts)); adj = now.tv_sec - (now.tv_sec % ONE_DAY); ADD(ts->tv_sec, adj); /* In case the HH is really large. */ } else { - t (parse_time_seconds(str + plus, ts, &end)); + t (parse_time_seconds(&str, ts)); } - str = end; /* Parse up to nanosecond resolution. */ if (*str != '.') @@ -244,22 +244,19 @@ parse_time(const char *str, struct timespec *ts, clockid_t *clk) ts->tv_nsec += *str & 15; } else if ((points == 9) && (*str >= '5')) { ts->tv_nsec += 1; + /* I would like to have FIX_NSEC here, but the + * compile will complain and it is not worth it. */ } } while (points++ < 9) ts->tv_nsec *= 10; - if (ts->tv_nsec > 999999999L) { - ts->tv_sec += 1; - ts->tv_nsec = 0; - } + FIX_NSEC(ts); no_nanoseconds: /* Check for error at end, and missing explicit UTC. */ if (*str) { - if (*clk == CLOCK_BOOTTIME) - FAIL(EINVAL); + REQUIRE(*clk != CLOCK_BOOTTIME); while (*str == ' ') str++; - if (strcasecmp(str, "Z") && strcasecmp(str, "UTC")) - FAIL(EINVAL); + REQUIRE(!strcasecmp(str, "Z") || !strcasecmp(str, "UTC")); } else if (*clk == CLOCK_REALTIME) { fprintf(stderr, "%s: warning: parsing as UTC, you can avoid this warning " @@ -270,11 +267,7 @@ no_nanoseconds: if (*clk == CLOCK_BOOTTIME) { ts->tv_sec += now.tv_sec; ts->tv_nsec += now.tv_nsec; - if (ts->tv_nsec >= 1000000000L) { - ts->tv_sec += 1; - ts->tv_nsec -= 1000000000L; - } - return 0; + FIX_NSEC(ts); } else if (ts->tv_sec < now.tv_sec) { /* Ignore partial second. */ ts->tv_sec += ONE_DAY; if (ts->tv_sec < now.tv_sec) @@ -282,7 +275,7 @@ no_nanoseconds: if (!strchr(start, ':')) fprintf(stderr, "%s: warning: the specified time is in the past, " - "it is being adjust to be tomorrow instead.\n", argv0); + "it is being adjust to be tomorrow instead.\n", argv0); } return 0; diff --git a/src/parse_time.h b/src/parse_time.h index efe5ad5..4b8ada3 100644 --- a/src/parse_time.h +++ b/src/parse_time.h @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), diff --git a/src/sat.c b/src/sat.c index e3e1e84..5431208 100644 --- a/src/sat.c +++ b/src/sat.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -88,11 +88,11 @@ retry: /* Construct message to send to the daemon. */ n = measure_array(argv) + size + measure_array(envp); - t (!(msg = malloc(n + sizeof(int) + sizeof(clk) + sizeof(ts)))); + t (!(msg = malloc(n + sizeof(argc) + sizeof(clk) + sizeof(ts)))); store_array(getcwd(store_array(msg, argv), size) + size, envp); - memcpy(msg + n, &argc, sizeof(int)), n += sizeof(int); - memcpy(msg + n, &clk, sizeof(clk)), n += sizeof(clk); - memcpy(msg + n, &ts, sizeof(ts)), n += sizeof(ts); + memcpy(msg + n, &argc, sizeof(argc)), n += sizeof(argc); + memcpy(msg + n, &clk, sizeof(clk)), n += sizeof(clk); + memcpy(msg + n, &ts, sizeof(ts)), n += sizeof(ts); /* Send job to daemon, start daemon if necessary. */ SEND(SAT_QUEUE, n, msg); diff --git a/src/satd-add.c b/src/satd-add.c index f1fa4ed..fba4465 100644 --- a/src/satd-add.c +++ b/src/satd-add.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -48,7 +48,7 @@ main(int argc, char *argv[]) /* Receive and validate message. */ t (readall(SOCK_FILENO, &message, &n)); t (n < sizeof(int) + sizeof(clockid_t) + sizeof(struct timespec)); - n -= sizeof(int) + sizeof(clockid_t) + sizeof(struct timespec); + n -= sizeof(int) + sizeof(clockid_t) + sizeof(struct timespec); msg_argc = *(int *)(message + n); t ((msg_argc < 1) || !n || message[n - 1]); for (i = n; i--; elements += !message[i]); @@ -56,11 +56,10 @@ main(int argc, char *argv[]) /* Parse message. */ t (!(job = malloc(sizeof(*job) + n))); - job->argc = msg_argc; - job->clk = *(clockid_t *)(message + n + sizeof(int)); - job->ts = *(struct timespec *)(message + n + sizeof(int) + sizeof(clockid_t)); - job->n = n; - memcpy(job->payload, message, n); + job->argc = msg_argc;/* *(int *)(message + n); "See a few lines above."; */ + job->clk = *(clockid_t *)(message + n + sizeof(int)); + job->ts = *(struct timespec *)(message + n + sizeof(int) + sizeof(clockid_t)); + memcpy(job->payload, message, job->n = n); /* Update state file and run hook. */ t (flock(STATE_FILENO, LOCK_EX)); @@ -73,8 +72,7 @@ main(int argc, char *argv[]) t (pwriten(STATE_FILENO, &(job->no), sizeof(job->no), (size_t)0) < (ssize_t)sizeof(job->no)); if (attr.st_size < (off_t)sizeof(job->no)) attr.st_size = (off_t)sizeof(job->no); - n += sizeof(*job); - t (pwriten(STATE_FILENO, job, n, (size_t)(attr.st_size)) < (ssize_t)n); + t (pwriten(STATE_FILENO, job, sizeof(*job) + n, (size_t)(attr.st_size)) < (ssize_t)n); fsync(STATE_FILENO); run_job_or_hook(job, "queued"); t (flock(STATE_FILENO, LOCK_UN)); diff --git a/src/satd-diminished.c b/src/satd-diminished.c index 7a9c353..c8c0b53 100644 --- a/src/satd-diminished.c +++ b/src/satd-diminished.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -65,6 +65,14 @@ static volatile pid_t timer_pid = NO_TIMER_SPAWNED; */ static volatile pid_t child_count = 0; +/** + * Timer specification for an unset timer. + */ +static const struct itimerspec nilspec = { + .it_interval.tv_sec = 0, .it_value.tv_sec = 0, + .it_interval.tv_nsec = 0, .it_value.tv_nsec = 0, +}; + /** @@ -103,45 +111,50 @@ sighandler(int signo) static int spawn(int command, int fd, char *argv[], char *envp[]) { +#define IMAGE(CASE, NAME) case CASE: image = DAEMON_IMAGE(NAME); break + const char *image; pid_t pid; -fork_again: - switch ((pid = fork())) { - case -1: + /* Try forking until we success. */ + while ((pid = fork()) == -1) { if (errno != EAGAIN) return -1; (void) sleep(1); /* Possibly shorter because of SIGCHLD. */ - goto fork_again; - case 0: - switch (command) { - case SAT_QUEUE: image = DAEMON_IMAGE("add"); break; - case SAT_REMOVE: image = DAEMON_IMAGE("rm"); break; - case SAT_PRINT: image = DAEMON_IMAGE("list"); break; - case SAT_RUN: image = DAEMON_IMAGE("run"); break; - case -1: image = DAEMON_IMAGE("timer"); break; - default: - fprintf(stderr, "%s: invalid command received.\n", argv[0]); - goto child_fail; - } - if (command < 0) { - close(SOCK_FILENO), close(LOCK_FILENO); - execve(image, argv, envp); - } else { - close(BOOT_FILENO), close(REAL_FILENO), close(LOCK_FILENO); - if (dup2(fd, SOCK_FILENO) != -1) - close(fd), fd = SOCK_FILENO, execve(image, argv, envp); - } - perror(argv[0]); - child_fail: - close(fd); - exit(1); - default: + } + + /* Parent. */ + if (pid) { child_count++; if (command < 0) timer_pid = pid; return 0; } + + /* Child. */ + switch (command) { + IMAGE(SAT_QUEUE, "add"); + IMAGE(SAT_REMOVE, "rm"); + IMAGE(SAT_PRINT, "list"); + IMAGE(SAT_RUN, "run"); + 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: + perror(argv[0]); +silent_fail: + close(fd); + exit(1); } @@ -173,14 +186,10 @@ static int test_timer(int fd, const fd_set *fdset) { int64_t _overrun; - struct itimerspec spec = { - .it_interval.tv_sec = 0, .it_value.tv_sec = 0, - .it_interval.tv_nsec = 0, .it_value.tv_nsec = 0, - }; if (!FD_ISSET(fd, fdset)) return 0; if (read(fd, &_overrun, (size_t)8) < 8) return -1; if (timer_pid == NO_TIMER_SPAWNED) return 1; - return timerfd_settime(fd, TFD_TIMER_ABSTIME, &spec, NULL) ? -1 : 1; + return timerfd_settime(fd, TFD_TIMER_ABSTIME, &nilspec, NULL) * 2 + 1; } @@ -208,16 +217,17 @@ main(int argc, char *argv[], char *envp[]) /* The magnificent loop. */ again: + /* Update the a newer version of the daemon? */ if (received_signo == SIGHUP) { execve(DAEMON_IMAGE("diminished"), argv, envp); perror(argv[0]); } - if (expired || ((received_signo == SIGCHLD) && (timer_pid == NO_TIMER_SPAWNED))) { - expired = 0; - t (spawn(-1, -1, argv, envp)); - } + /* Need to set new timer values? */ + if (expired || ((received_signo == SIGCHLD) && (timer_pid == NO_TIMER_SPAWNED))) + t (expired = 0, spawn(-1, -1, argv, envp)); received_signo = 0; #if 1 || !defined(DEBUG) + /* Can we quit yet? */ if (accepted && !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; @@ -229,6 +239,7 @@ again: } #endif not_done: + /* Wait for something to happen. */ FD_ZERO(&fdset); FD_SET(SOCK_FILENO, &fdset); FD_SET(BOOT_FILENO, &fdset); @@ -237,20 +248,16 @@ not_done: t (errno != EINTR); goto again; } + /* Was any jobs expired? */ 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) { - switch (errno) { - case ECONNABORTED: - case EINTR: - goto again; - default: - /* Including EMFILE, ENFILE, and ENOMEM - * because of potential resource leak. */ - goto fail; - } + 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) diff --git a/src/satd-list.c b/src/satd-list.c index 75f7996..12c06a7 100644 --- a/src/satd-list.c +++ b/src/satd-list.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -38,7 +38,8 @@ static char * quote(const char *str) { -#define UNSAFE(c) strchr(" \"$()[]{};|&^#!?*~`<>", c) +#define UNSAFE(c) strchr(" \"$()[]{};|&^#!?*~`<>", c) +#define N(I, S, B, Q) (I*in + S*sn + B*bn + Q*qn + rn) size_t in = 0; /* < ' ' or 127 */ size_t sn = 0; /* in UNSAFE */ @@ -49,10 +50,6 @@ quote(const char *str) const unsigned char *s; char *rc = NULL; - if (!*str) { - return strdup("''"); - } - for (s = (const unsigned char *)str; *s; s++) { if (*s < ' ') in++; else if (*s == 127) in++; @@ -61,25 +58,20 @@ quote(const char *str) else if (*s == '\'') qn++; else rn++; } - - switch (in ? 2 : (sn + bn + qn) ? 1 : 0) { - case 0: - return strdup(str); - case 1: - n = rn + sn + bn + 4 * qn + 4 * in + 2; - t (!(rc = malloc((n + 1) * sizeof(char)))); - rc[i++] = '\''; + if (N(1, 1, 1, 1) == rn) + return strdup(rn ? str : "''"); + + n = in ? (N(4, 1, 2, 2) + 3) : (N(0, 1, 1, 4) + 2); + t (!(rc = malloc((n + 1) * sizeof(char)))); + rc[i += !!in] = '$'; + rc[i += 1] = '\''; + if (in == 0) { for (s = (const unsigned char *)str; *s; s++) { rc[i++] = (char)*s; if (*s == '\'') rc[i++] = '\\', rc[i++] = '\'', rc[i++] = '\''; } - break; - default: - n = 4 * in + rn + sn + 2 * bn + 2 * qn + 3; - t (!(rc = malloc((n + 1) * sizeof(char)))); - rc[i++] = '$'; - rc[i++] = '\''; + } else { for (s = (const unsigned char *)str; *s; s++) { if ((*s < ' ') || (*s == 127)) { rc[i++] = '\\'; @@ -90,7 +82,6 @@ quote(const char *str) else if (strchr("\\'", *s)) rc[i++] = '\\', rc[i++] = (char)*s; else rc[i++] = (char)*s; } - break; } rc[i++] = '\''; rc[i] = '\0'; @@ -110,24 +101,14 @@ static void strduration(char *buffer, time_t s) { char *buf = buffer; - int seconds, minutes, hours; - seconds = (int)(s % 60), s /= 60; - minutes = (int)(s % 60), s /= 60; - hours = (int)(s % 24), s /= 24; - if (s) { - buf += sprintf(buf, "%llid", (long long int)s); - buf += sprintf(buf, "%02i:", hours); - buf += sprintf(buf, "%02i:", minutes); - } else if (hours) { - buf += sprintf(buf, "%i:", hours); - buf += sprintf(buf, "%02i:", minutes); - } else if (minutes) { - buf += sprintf(buf, "%i:", minutes); - } else { - sprintf(buf, "%i", seconds); - return; - } - sprintf(buf, "%02i", seconds); + int secs, mins, hours, sd = 0, md = 0, hd = 0; + secs = (int)(s % 60), s /= 60; + mins = (int)(s % 60), s /= 60; + hours = (int)(s % 24), s /= 24; + if (s) hd++, buf += sprintf(buf, "%llid", (long long int)s); + if (hd | hours) md++, buf += sprintf(buf, "%0*i:", ++hd, hours); + if (md | mins) sd++, buf += sprintf(buf, "%0*i:", ++md, mins); + /*just for alignment*/ buf += sprintf(buf, "%0*i:", ++sd, secs); } @@ -140,13 +121,21 @@ strduration(char *buffer, time_t s) static int send_job_human(struct job *job) { +#define FIX_NSEC(T) (((T)->tv_nsec < 0L) ? ((T)->tv_sec -= 1, (T)->tv_nsec += 1000000000L) : 0L) +#define ARRAY(LIST) \ + for (arg = LIST; *arg; arg++) { \ + free(qstr); \ + t (!(qstr = quote(*arg))); \ + t (send_string(SOCK_FILENO, STDOUT_FILENO, " ", qstr, NULL)); \ + } + struct tm *tm; struct timespec rem; const char *clk; char rem_s[3 * sizeof(time_t) + sizeof("d00:00:00")]; char *qstr = NULL; char *wdir = NULL; - char line[sizeof("job: %zu clock: unrecognised argc: %i remaining: , argv[0]: ") + char line[sizeof("job: %zu clock: unrecognised argc: %i remaining: argv[0]: ") + 3 * sizeof(size_t) + 3 * sizeof(int) + sizeof(rem_s) + 9]; char timestr_a[sizeof("-00-00 00:00:00") + 3 * sizeof(time_t)]; char timestr_b[10]; @@ -162,10 +151,7 @@ send_job_human(struct job *job) return errno == EINVAL ? 0 : -1; rem.tv_sec = job->ts.tv_sec - rem.tv_sec; rem.tv_nsec = job->ts.tv_nsec - rem.tv_nsec; - if (rem.tv_nsec < 0) { - rem.tv_sec -= 1; - rem.tv_nsec += 1000000000L; - } + FIX_NSEC(&rem); if (rem.tv_sec < 0) /* This job will be removed momentarily, do not list it. (To simply things.) */ return 0; @@ -181,14 +167,11 @@ send_job_human(struct job *job) strduration(rem_s, rem.tv_sec); /* Get textual representation of the expiration time. */ - switch (job->clk) { - case CLOCK_REALTIME: + if (job->clk == CLOCK_REALTIME) { t (!(tm = localtime(&(job->ts.tv_sec)))); strftime(timestr_a, sizeof(timestr_a), "%Y-%m-%d %H:%M:%S", tm); - break; - default: + } else { strduration(timestr_a, job->ts.tv_sec); - break; } sprintf(timestr_b, "%09li", job->ts.tv_nsec); @@ -202,37 +185,19 @@ send_job_human(struct job *job) t (!(wdir = quote(envp[0]))); sprintf(line, "job: %zu clock: %s argc: %i remaining: %s.%09li argv[0]: ", job->no, clk, job->argc, rem_s, rem.tv_nsec); - t (send_string(SOCK_FILENO, STDOUT_FILENO, - line, qstr, "\n", - " time: ", timestr_a, ".", timestr_b, "\n", - " wdir:", wdir, "\n", - " argv:", - NULL)); - for (arg = argv; *arg; arg++) { - free(qstr); - t (!(qstr = quote(*arg))); - t (send_string(SOCK_FILENO, STDOUT_FILENO, " ", qstr, NULL)); - } - free(qstr), qstr = NULL; - t (send_string(SOCK_FILENO, STDOUT_FILENO, "\n envp:", NULL)); - for (arg = envp + 1; *arg; arg++) { - t (!(qstr = quote(*arg))); - t (send_string(SOCK_FILENO, STDOUT_FILENO, " ", qstr, NULL)); - free(qstr); - } - qstr = NULL; - t (send_string(SOCK_FILENO, STDOUT_FILENO, "\n\n", NULL)); + t (send_string(SOCK_FILENO, STDOUT_FILENO, line, qstr, + "\n time: ", timestr_a, ".", timestr_b, + "\n wdir: ", wdir, + "\n argv:", NULL)); + free(qstr); + ARRAY(argv); t (send_string(SOCK_FILENO, STDOUT_FILENO, "\n envp:", NULL)); + ARRAY(envp + 1); t (send_string(SOCK_FILENO, STDOUT_FILENO, "\n\n", NULL)); done: saved_errno = errno; - free(qstr); - free(args); - free(argv); - free(wdir); - free(envp); + free(qstr), free(args), free(argv), free(wdir), free(envp); errno = saved_errno; return rc; - fail: rc = -1; goto done; diff --git a/src/satd-rm.c b/src/satd-rm.c index 435c302..d33160c 100644 --- a/src/satd-rm.c +++ b/src/satd-rm.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -43,8 +43,7 @@ main(int argc, char *argv[]) /* Receive and validate message. */ t (readall(SOCK_FILENO, &message, &n) || !n || message[n - 1]); - msg_argv = restore_array(message, n, NULL); - t (!msg_argv); + t (!(msg_argv = restore_array(message, n, NULL))); /* Perform action. */ for (arg = msg_argv; *arg; arg++) diff --git a/src/satd-run.c b/src/satd-run.c index 9543ca4..cf3f065 100644 --- a/src/satd-run.c +++ b/src/satd-run.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -43,10 +43,7 @@ main(int argc, char *argv[]) /* Receive and validate message. */ t (readall(SOCK_FILENO, &message, &n) || (n && message[n - 1])); - if (n) { - msg_argv = restore_array(message, n, NULL); - t (!msg_argv); - } + t (n && !(msg_argv = restore_array(message, n, NULL))); /* Perform action. */ if (msg_argv) { diff --git a/src/satd-timer.c b/src/satd-timer.c index 671555e..113b50a 100644 --- a/src/satd-timer.c +++ b/src/satd-timer.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), diff --git a/src/satd.c b/src/satd.c index 9ec322b..8865c51 100644 --- a/src/satd.c +++ b/src/satd.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), @@ -39,6 +39,12 @@ #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)) @@ -56,10 +62,9 @@ USAGE("[-f]") static int create_socket(struct sockaddr_un *address) { - int fd = -1, bound = 0; const void *_cvoid; const char *dir; - int saved_errno; + int fd = -1, saved_errno; /* Get socket address. */ dir = getenv("XDG_RUNTIME_DIR"), dir = (dir ? dir : "/run"); @@ -71,19 +76,13 @@ create_socket(struct sockaddr_un *address) /* Create socket. */ unlink(address->sun_path); t ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1); - t (fchmod(fd, S_IRWXU) == -1); - t (bind(fd, (const struct sockaddr *)(_cvoid = address), (socklen_t)sizeof(*address)) == -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. */ - bound = 1; return fd; fail: - saved_errno = errno; - if (fd >= 0) - close(fd); - if (bound) - unlink(address->sun_path); - errno = saved_errno; + saved_errno = errno, close(fd), errno = saved_errno; return -1; } @@ -109,9 +108,7 @@ create_state(char **state_path) *state_path = path, path = NULL; fail: - saved_errno = errno; - free(path); - errno = saved_errno; + saved_errno = errno, free(path), errno = saved_errno; return fd; } @@ -127,7 +124,7 @@ create_lock(void) const char *dir; char *path; char *p; - int fd = -1, saved_errno; + int fd = -1, saved_errno = 0; /* Create directory. */ dir = getenv("XDG_RUNTIME_DIR"), dir = (dir ? dir : "/run"); @@ -141,16 +138,17 @@ create_lock(void) /* Check that the daemon is not running, and mark it as running. */ if (flock(fd, LOCK_EX | LOCK_NB)) { - t (fd = -1, errno != EWOULDBLOCK); + t (errno != EWOULDBLOCK); fprintf(stderr, "%s: the daemon is already in reading.\n", argv0); errno = 0; goto fail; } + goto done; fail: - saved_errno = errno; - free(path); - errno = saved_errno; + saved_errno = errno, close(fd); +done: + free(path), errno = saved_errno; return fd; } @@ -169,29 +167,23 @@ fail: static char * hookpath(const char *env, const char *suffix) { + struct passwd *pwd; const char *prefix = NULL; char *path; - struct passwd *pwd; - if (!env) { - if (!getuid()) - goto try_next; + if (env) { + prefix = getenv(env); + } else if (getuid()) { pwd = getpwuid(getuid()); prefix = pwd ? pwd->pw_dir : NULL; - } else { - prefix = getenv(env); } if (!prefix || !*prefix) - goto try_next; + return errno = 0, NULL; t (!(path = malloc((strlen(prefix) + strlen(suffix) + 1) * sizeof(char)))); stpcpy(stpcpy(path, prefix), suffix); - - return path; -try_next: - errno = 0; fail: - return NULL; + return path; } @@ -208,25 +200,14 @@ fail: static int dup2_and_null(int old, int new) { - int fd = -1; - int saved_errno; - - if (old == new) goto done; - t (dup2(old, new) == -1); - close(old); - if (old >= 3) goto done; - t (fd = open("/dev/null", O_RDWR), fd == -1); - if (fd == old) goto done; - t (dup2(fd, old) == -1); - close(fd), fd = -1; + int fd = -1, saved_errno; -done: + if (old == new) return new; t (DUP2_AND_CLOSE(old, new)); + if (old >= 3) return new; t (fd = open("/dev/null", O_RDWR), fd == -1); + if (fd == old) return new; t (DUP2_AND_CLOSE(fd, old)); return new; fail: - saved_errno = errno; - if (fd >= 0) - close(fd); - errno = saved_errno; + saved_errno = errno, close(fd), errno = saved_errno; return -1; } @@ -259,9 +240,8 @@ main(int argc, char *argv[]) /* Parse command line. */ if (argc > 0) argv0 = argv[0]; if (argc > 2) usage(); - if (argc == 2) - if (!(foreground = !strcmp(argv[1], "-f"))) - usage(); + if ((argc == 2) && (!(foreground = !strcmp(argv[1], "-f")))) + usage(); /* Get hook-script pathname. */ if (!getenv("SAT_HOOK_PATH")) { @@ -287,11 +267,7 @@ main(int argc, char *argv[]) t (timerfd_settime(real, TFD_TIMER_ABSTIME, &spec, NULL)); /* Listen for incoming conections. */ -#if SOMAXCONN < SATD_BACKLOG - t (listen(sock, SOMAXCONN)); -#else t (listen(sock, SATD_BACKLOG)); -#endif /* Daemonise. */ t (foreground ? 0 : daemonise("satd", DAEMONISE_KEEP_FDS | DAEMONISE_NEW_PID, 3, 4, 5, 6, 7, -1)); @@ -303,14 +279,8 @@ fail: if (errno) perror(argv0); free(path); - if (sock >= 0) { - unlink(address.sun_path); - close(sock); - } - if (state >= 0) close(state); - if (boot >= 0) close(boot); - if (real >= 0) close(real); - if (lock >= 0) close(lock); + if (sock >= 0) unlink(address.sun_path); + close(sock), close(state), close(boot), close(real), close(lock); undaemonise(); return 1; } diff --git a/src/satq.c b/src/satq.c index 048634d..e851c1b 100644 --- a/src/satq.c +++ b/src/satq.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), diff --git a/src/satr.c b/src/satr.c index b1a587b..0543d58 100644 --- a/src/satr.c +++ b/src/satr.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), diff --git a/src/satrm.c b/src/satrm.c index 7904416..45f4bc2 100644 --- a/src/satrm.c +++ b/src/satrm.c @@ -1,5 +1,5 @@ /** - * Copyright © 2015 Mattias Andrée + * 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"), -- cgit v1.2.3-70-g09d2