aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Makefile.in12
-rw-r--r--TODO3
-rw-r--r--doc/info/chap/hooks.texinfo4
-rw-r--r--doc/man/satd.15
-rw-r--r--src/client.c2
-rw-r--r--src/daemon.c17
-rw-r--r--src/daemon.h47
-rw-r--r--src/parse_time.c53
-rw-r--r--src/sat.c2
-rw-r--r--src/satd-diminished.c24
-rw-r--r--src/satd-list.c15
-rw-r--r--src/satd-run.c5
-rw-r--r--src/satd-timer.c2
-rw-r--r--src/satd.c10
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
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..37dc44f
--- /dev/null
+++ b/TODO
@@ -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);
diff --git a/src/sat.c b/src/sat.c
index b86ae39..2cc4f09 100644
--- a/src/sat.c
+++ b/src/sat.c
@@ -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) {
diff --git a/src/satd.c b/src/satd.c
index 37700df..9ec322b 100644
--- a/src/satd.c
+++ b/src/satd.c
@@ -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)