diff options
-rw-r--r-- | Makefile.in | 12 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | doc/info/chap/hooks.texinfo | 4 | ||||
-rw-r--r-- | doc/man/satd.1 | 5 | ||||
-rw-r--r-- | src/client.c | 2 | ||||
-rw-r--r-- | src/daemon.c | 17 | ||||
-rw-r--r-- | src/daemon.h | 47 | ||||
-rw-r--r-- | src/parse_time.c | 53 | ||||
-rw-r--r-- | src/sat.c | 2 | ||||
-rw-r--r-- | src/satd-diminished.c | 24 | ||||
-rw-r--r-- | src/satd-list.c | 15 | ||||
-rw-r--r-- | src/satd-run.c | 5 | ||||
-rw-r--r-- | src/satd-timer.c | 2 | ||||
-rw-r--r-- | src/satd.c | 10 |
14 files changed, 140 insertions, 61 deletions
diff --git a/Makefile.in b/Makefile.in index dcc4126..2dcd2b0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -103,6 +103,16 @@ _EVERYTHING = $(foreach F,$(___EVERYTHING_INFO),doc/info/$(F).texinfo) \ include $(v)mk/all.mk ifdef DEBUG -_CPPFLAGS += -DDEBUG=1 +ifneq ($(DEBUG),strace) +ifneq ($(DEBUG),valgrind) +_CPPFLAGS += -DDEBUG='1' +endif +endif +ifeq ($(DEBUG),strace) +_CPPFLAGS += -DDEBUG='2' +endif +ifeq ($(DEBUG),valgrind) +_CPPFLAGS += -DDEBUG='3' +endif endif @@ -0,0 +1,3 @@ +We probably shall save the working directory too. +The daemon shall always send an ACK when it is done. + diff --git a/doc/info/chap/hooks.texinfo b/doc/info/chap/hooks.texinfo index 9dec27d..fe6e753 100644 --- a/doc/info/chap/hooks.texinfo +++ b/doc/info/chap/hooks.texinfo @@ -16,6 +16,8 @@ arguments are job's command line arguments including the zeroth argument (the name of the command). Possible actions are: @table @code +@item queued +The job with queued using @command{sat}. @item expired The job will run momentarily as scheduled. @item forced @@ -38,7 +40,7 @@ is to use the script #!/bin/sh action="$1" shift 1 -exec wall -n "sat: $action: $JOBNAME ($*)" +exec wall "sat: $action: $JOBNAME ($*)" @end example @noindent and always set the environment variable @env{JOBNAME} diff --git a/doc/man/satd.1 b/doc/man/satd.1 index 3875b4f..d86c43a 100644 --- a/doc/man/satd.1 +++ b/doc/man/satd.1 @@ -32,6 +32,7 @@ either .B failure if the job was not executed successfully, including if the job exited with any status other than zero, or +.TP .B success if the job was executed successfully and exited with status zero. @@ -40,6 +41,10 @@ When a job is removed using .BR satrm (1), the hook script is run with the action .BR removed . +When a job is queued using +.BR sat (1), +the hook script is run with the action +.BR queued . .PP When the hook script runs, the first argument (not counting the zeroth argument: the pathname of hook diff --git a/src/client.c b/src/client.c index 9e5600e..415ed79 100644 --- a/src/client.c +++ b/src/client.c @@ -73,7 +73,7 @@ send_command(enum command cmd, size_t n, const char *restrict msg) if (fd == -1) { t ((errno != ENOENT) && (errno != ENOTDIR)); } else { - if (flock(fd, LOCK_EX | LOCK_NB /* and LOCK_DRY if that was ever added... */)) + 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); diff --git a/src/daemon.c b/src/daemon.c index 977e845..12c1aea 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -117,13 +117,13 @@ int readall(int fd, char **buf, size_t *n) { char *buffer = NULL; - size_t ptr = 0, size = 0; + size_t ptr = 0, size = 128; ssize_t got = 1; char *new; int saved_errno; for (; got; ptr += (size_t)got) { - if (ptr == size) { + if ((buffer == NULL) || (ptr == size)) { t (!(new = realloc(buffer, size <<= 1))); buffer = new; } @@ -131,7 +131,7 @@ readall(int fd, char **buf, size_t *n) } new = realloc(buffer, ptr); - *buf = new ? new : buffer; + *buf = ptr ? (new ? new : buffer) : NULL; *n = ptr; shutdown(SOCK_FILENO, SHUT_RD); return 0; @@ -167,7 +167,7 @@ restore_array(char *buf, size_t len, size_t *n) t (!rc); while (i < len) { rc[e++] = buf + i; - i += strlen(buf + i); + i += strlen(buf + i) + 1; } rc[e] = NULL; new = realloc(rc, (e + 1) * sizeof(char*)); @@ -280,6 +280,7 @@ run_job_or_hook(struct job *job, const char *hook) t (!(args = restore_array(job->payload, job->n, &argsn))); t (!(argv = sublist(args, (size_t)(job->argc)))); t (!(envp = sublist(args + job->argc, argsn - (size_t)(job->argc)))); + free(args), args = NULL; if (hook) { t (!(new = realloc(argv, ((size_t)(job->argc) + 3) * sizeof(*argv)))); @@ -298,7 +299,7 @@ run_job_or_hook(struct job *job, const char *hook) close(BOOT_FILENO); close(REAL_FILENO); environ = envp; - execve(*argv, argv, envp); + execvp(*argv, argv); exit(1); default: t (waitpid(pid, &status, 0) != pid); @@ -350,12 +351,12 @@ remove_job(const char *jobno, int runjob) goto found_it; } t (flock(STATE_FILENO, LOCK_UN)); - return 0; + return errno = 0, -1; found_it: job_full = malloc(sizeof(job) + job.n); *job_full = job; - t (preadn(STATE_FILENO, job_full->payload, job.n, off) < (ssize_t)(job.n)); + t (preadn(STATE_FILENO, job_full->payload, job.n, off + sizeof(job)) < (ssize_t)(job.n)); n -= off + sizeof(job) + job.n; t (!(buf = malloc(n))); t (r = preadn(STATE_FILENO, buf, n, off + sizeof(job) + job.n), r < 0); @@ -411,7 +412,7 @@ get_jobs(void) while (off < n) { t (preadn(STATE_FILENO, &job, sizeof(job), off) < (ssize_t)sizeof(job)); off += sizeof(job); - t (!(js[j] = malloc(sizeof(job) + sizeof(job.n)))); + t (!(js[j] = malloc(sizeof(job) + job.n))); *(js[j]) = job; t (preadn(STATE_FILENO, js[j++]->payload, job.n, off) < (ssize_t)(job.n)); off += job.n; diff --git a/src/daemon.h b/src/daemon.h index 567e268..980cd64 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -279,3 +279,50 @@ int remove_job(const char *jobno, int runjob); */ struct job **get_jobs(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 + diff --git a/src/parse_time.c b/src/parse_time.c index 7761557..83b0e65 100644 --- a/src/parse_time.c +++ b/src/parse_time.c @@ -92,10 +92,6 @@ strtotime(const char *str, const char **end) time_t rc; long long int rcll; long int rcl; - char **end_ = (char **)end; -#ifdef __GNUC__ -# pragma GCC diagnostic pop -#endif if (!isdigit(*str)) FAIL(EINVAL); @@ -106,14 +102,17 @@ strtotime(const char *str, const char **end) errno = 0; if (sizeof(time_t) == sizeof(long long int)) { - rcll = strtoll(str, end_, 10); + rcll = strtoll(str, (char **)end, 10); rc = (time_t)rcll; } else { - rcl = strtol(str, end_, 10); + rcl = strtol(str, (char **)end, 10); rc = (time_t)rcl; } return errno ? 0 : rc; +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif } @@ -149,13 +148,10 @@ parse_time_time(const char *str, struct timespec *ts, const char **end) MUL(tm, (time_t)60); ADD(ts->tv_sec, tm); - switch (*str++) { - case 0: return 0; - case ':': break; - default: FAIL(EINVAL); - } + if (*str++ != ':') + return 0; - tm = strtotime(str, end), str = *end; + tm = strtotime(str, end); t (errno); /* Do not restrict to 59, or 60, there can be more than +1 leap second. */ ADD(ts->tv_sec, tm); @@ -219,8 +215,8 @@ parse_time(const char *str, struct timespec *ts, clockid_t *clk) time_t adj; /* Get current time and clock. */ - clock_gettime(CLOCK_REALTIME, &now); *clk = plus ? CLOCK_BOOTTIME : CLOCK_REALTIME; + clock_gettime(*clk, &now); /* Mañana? */ if (!strcmp(str, "mañana")) { /* Do not documented. */ @@ -239,33 +235,30 @@ parse_time(const char *str, struct timespec *ts, clockid_t *clk) } str = end; - /* Any fractions of a second? */ - switch (*str) { - case 0: return 0; - case '.': break; - default: FAIL(EINVAL); - } - /* Parse up to nanosecond resolution. */ - for (points = 0; isdigit(*str); points++) { + if (*str != '.') + goto no_nanoseconds; + for (points = 0, str++; isdigit(*str); points++, str++) { if (points < 9) { ts->tv_nsec *= 10; - ts->tv_nsec += *str++ & 15; - } else if ((points == 10) && (*str >= '5')) { + ts->tv_nsec += *str & 15; + } else if ((points == 9) && (*str >= '5')) { ts->tv_nsec += 1; } } + while (points++ < 9) ts->tv_nsec *= 10; if (ts->tv_nsec > 999999999L) { ts->tv_sec += 1; ts->tv_nsec = 0; } +no_nanoseconds: /* Check for error at end, and missing explicit UTC. */ if (*str) { if (*clk == CLOCK_BOOTTIME) FAIL(EINVAL); while (*str == ' ') str++; - if (!strcasecmp(str, "Z") && !strcasecmp(str, "UTC")) + if (strcasecmp(str, "Z") && strcasecmp(str, "UTC")) FAIL(EINVAL); } else if (*clk == CLOCK_REALTIME) { fprintf(stderr, @@ -273,10 +266,16 @@ parse_time(const char *str, struct timespec *ts, clockid_t *clk) "by adding a 'Z' at the end of the time argument.\n", argv0); } - /* Adjust the day? */ - if (*clk == CLOCK_BOOTTIME) + /* Add offset or djust the day? */ + 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; - if (ts->tv_sec < now.tv_sec) { /* Ignore partial second. */ + } else if (ts->tv_sec < now.tv_sec) { /* Ignore partial second. */ ts->tv_sec += ONE_DAY; if (ts->tv_sec < now.tv_sec) FAIL(EDOM); @@ -65,7 +65,7 @@ main(int argc, char *argv[], char *envp[]) switch (errno) { case EINVAL: fprintf(stderr, - "%s: time parameter cound not be parsed, perhaps you " + "%s: time parameter cound not be parsed, perhaps " "you need an external parser: %s\n", argv0, argv[1]); return 2; case ERANGE: diff --git a/src/satd-diminished.c b/src/satd-diminished.c index f0df567..4bc4992 100644 --- a/src/satd-diminished.c +++ b/src/satd-diminished.c @@ -71,10 +71,17 @@ static void sighandler(int signo) { int saved_errno = errno; - if ((signo == SIGCHLD) && (waitpid(-1, NULL, WNOHANG) == timer_pid)) - timer_pid = NO_TIMER_SPAWNED; - else + pid_t pid; + if (signo == SIGCHLD) { + while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) { + if (pid == timer_pid) + timer_pid = NO_TIMER_SPAWNED; + else + received_signo = (sig_atomic_t)signo; + } + } else { received_signo = (sig_atomic_t)signo; + } errno = saved_errno; } @@ -166,7 +173,7 @@ test_timer(int fd, const fd_set *fdset) }; if (!FD_ISSET(BOOT_FILENO, fdset)) return 0; if (read(BOOT_FILENO, &_overrun, (size_t)8) < 8) return -1; - if (timer_pid == NO_TIMER_SPAWNED) return 0; + if (timer_pid == NO_TIMER_SPAWNED) return 0; return timerfd_settime(fd, TFD_TIMER_ABSTIME, &spec, NULL); } @@ -195,6 +202,7 @@ main(int argc, char *argv[], char *envp[]) /* The magnificent loop. */ again: +#if 0 || !defined(DEBUG) if (accepted && (timer_pid == NO_TIMER_SPAWNED)) { 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; @@ -206,6 +214,7 @@ again: goto done; } not_done: +#endif if (received_signo == SIGHUP) { execve(DAEMON_IMAGE("diminished"), argv, envp); perror(argv[0]); @@ -215,11 +224,10 @@ not_done: received_signo = 0; 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) { + // FIXME ---- FD_SET(BOOT_FILENO, &fdset); + // FIXME ---- FD_SET(REAL_FILENO, &fdset); /* This is the highest one. */ + if (select(REAL_FILENO + 1, &fdset, NULL, NULL, NULL) == -1) t (errno != EINTR); - } t (test_timer(BOOT_FILENO, &fdset)); t (test_timer(REAL_FILENO, &fdset)); if (!FD_ISSET(SOCK_FILENO, &fdset)) diff --git a/src/satd-list.c b/src/satd-list.c index 53cbe06..0591368 100644 --- a/src/satd-list.c +++ b/src/satd-list.c @@ -118,12 +118,15 @@ strduration(char *buffer, time_t s) if (s) { buf += sprintf(buf, "%llid", (long long int)s); buf += sprintf(buf, "%02i:", hours); - buf += sprintf(buf, "%02i", minutes); + buf += sprintf(buf, "%02i:", minutes); } else if (hours) { buf += sprintf(buf, "%i:", hours); - buf += sprintf(buf, "%02i", minutes); + buf += sprintf(buf, "%02i:", minutes); } else if (minutes) { - buf += sprintf(buf, "%i", minutes); + buf += sprintf(buf, "%i:", minutes); + } else { + sprintf(buf, "%i", seconds); + return; } sprintf(buf, "%02i", seconds); } @@ -145,7 +148,7 @@ send_job_human(struct job *job) char *qstr = NULL; 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("0000-00-00 00:00:00") + 3 * sizeof(time_t)]; + char timestr_a[sizeof("-00-00 00:00:00") + 3 * sizeof(time_t)]; char timestr_b[10]; char **args = NULL; char **arg; @@ -157,8 +160,8 @@ send_job_human(struct job *job) /* Get remaining time. */ if (clock_gettime(job->clk, &rem)) return errno == EINVAL ? 0 : -1; - rem.tv_sec -= job->ts.tv_sec; - rem.tv_nsec -= job->ts.tv_nsec; + 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; diff --git a/src/satd-run.c b/src/satd-run.c index 00b642e..9543ca4 100644 --- a/src/satd-run.c +++ b/src/satd-run.c @@ -53,9 +53,8 @@ main(int argc, char *argv[]) for (arg = msg_argv; *arg; arg++) t (remove_job(*arg, 1) && errno); } else { - for (;;) - if (remove_job(NULL, 1)) - t (errno); + while (!remove_job(NULL, 1)); + t (errno); } DAEMON_CLEANUP_START; diff --git a/src/satd-timer.c b/src/satd-timer.c index 310c23d..671555e 100644 --- a/src/satd-timer.c +++ b/src/satd-timer.c @@ -74,7 +74,7 @@ main(int argc, char *argv[]) t (clock_gettime(CLOCK_REALTIME, &realnow)); t (!(jobs = get_jobs())); for (job = jobs; *job; job++) { - if (timecmp(&(job[0]->ts), TIME(*job, now)) >= 0) { + if (timecmp(&(job[0]->ts), TIME(*job, now)) <= 0) { sprintf(jobno, "%zu", job[0]->no); remove_job(jobno, 2); } else if (timecmp(&(job[0]->ts), &(TIME(*job, spec)->it_value)) > 0) { @@ -91,10 +91,11 @@ fail: /** * Create the state file. * - * @return A file descriptor to the state file, -1 on error. + * @param state_path Output parameter for the state file's pathname. + * @return A file descriptor to the state file, -1 on error. */ static int -create_state(void) +create_state(char **state_path) { const char *dir; char *path; @@ -105,6 +106,7 @@ create_state(void) t (!(path = malloc(strlen(dir) * sizeof(char) + sizeof("/" PACKAGE "/state")))); stpcpy(stpcpy(path, dir), "/" PACKAGE "/state"); t (fd = open(path, O_RDWR | O_CREAT /* but not O_EXCL or O_TRUNC */, S_IRUSR | S_IWUSR), fd == -1); + *state_path = path, path = NULL; fail: saved_errno = errno; @@ -272,7 +274,7 @@ main(int argc, char *argv[]) /* Open/create lock file and state file, and create socket. */ GET_FD(lock, LOCK_FILENO, create_lock()); - GET_FD(state, STATE_FILENO, create_state()); + GET_FD(state, STATE_FILENO, create_state(&path)); GET_FD(sock, SOCK_FILENO, create_socket(&address)); /* Create timers. */ @@ -295,7 +297,7 @@ main(int argc, char *argv[]) t (foreground ? 0 : daemonise("satd", DAEMONISE_KEEP_FDS | DAEMONISE_NEW_PID, 3, 4, 5, 6, 7, -1)); /* Change to a process image without all this initialisation text. */ - execl(LIBEXECDIR "/" PACKAGE "/satd-diminished", argv0, address.sun_path, NULL); + execl(LIBEXECDIR "/" PACKAGE "/satd-diminished", argv0, address.sun_path, path, NULL); fail: if (errno) |