diff options
Diffstat (limited to 'src/satd-list.c')
-rw-r--r-- | src/satd-list.c | 230 |
1 files changed, 229 insertions, 1 deletions
diff --git a/src/satd-list.c b/src/satd-list.c index a7d13e3..3a6747a 100644 --- a/src/satd-list.c +++ b/src/satd-list.c @@ -20,6 +20,215 @@ * DEALINGS IN THE SOFTWARE. */ #include "daemon.h" +#include <stdio.h> + + + +/** + * Quote a string, in shell (Bash-only if necessary) compatible + * format, if necessary. + * + * @param str The string. + * @return Return a safe representation of the string, + * `NULL` on error. + */ +static char * +quote(const unsigned char *str) +{ + size_t in = 0; /* < ' ' */ + size_t sn = 0; /* = ' ', '"' or '$' */ + size_t bn = 0; /* = '\\' */ + size_t qn = 0; /* = '\'' */ + size_t rn = 0; /* other */ + size_t n, i = 0; + char *s; + char *rc; + + for (s = str; *s; s++) { + if (*s < ' ') in++; + else if (*s == ' ') sn++; + else if (*s == '"') sn++; + else if (*s == '$') sn++; + else if (*s == '\\') bn++; + 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++] = '\''; + for (s = str; *s; s++) { + rc[i++] = (char)*s; + if (*s == '\'') + rc[i++] = '\\', rc[i++] = '\'', rc[i++] = '\''; + + } + rc[i++] = '\''; + rc[i] = '\0'; + return rc; + case 2: + n = 4 * in + rn + sn + 2 * bn + 2 * qn + 3; + t (!(rc = malloc((n + 1) * sizeof(char)))); + rc[i++] = '$'; + rc[i++] = '\''; + for (s = str; *s; s++) { + if (*s < ' ') { + rc[i++] = '\\'; + rc[i++] = 'x'; + rc[i++] = "0123456789ABCDEF"[(*s >> 4) & 15]; + rc[i++] = "0123456789ABCDEF"[(*s >> 0) & 15]; + } + else if (*s == ' ') rc[i++] = (char)*s; + else if (*s == '"') rc[i++] = (char)*s; + else if (*s == '$') rc[i++] = (char)*s; + else if (*s == '\\') rc[i++] = '\\', rc[i++] = (char)*s; + else if (*s == '\'') rc[i++] = '\\', rc[i++] = (char)*s; + else rc[i++] = (char)*s; + } + rc[i++] = '\''; + rc[i] = '\0'; + break; + } +fail: + return NULL; +} + + +/** + * Create a textual representation of the a duration. + * + * @param buffer Output buffer, with a size of at least + * 10 `char`:s plus enough to encode a `time_t`. + * @param s The duration in seconds. + */ +static void +strduration(char *buffer, time_t s) +{ + char *buf = buffer; + int seconds, minutes, hours, days; + seconds = s % 60, s /= 60; + minutes = s % 60, s /= 60; + hours = 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); + } + sprintf(buf, "%02i", seconds); +} + + +/** + * Dump a job to the socket. + * + * @param job The job. + * @return 0 on success, -1 on error. + */ +static int +send_job_str(struct job* job) +{ + struct tm *tm; + struct timespec rem; + const char *clk; + char rem_s[3 * sizeof(time_t) + sizeof("d00:00:00")]; + 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_b[10]; + char **args = NULL; + const char **arg; + const char **argv = NULL; + const char **envp = NULL; + size_t argsn; + int rc = 0; + + /* 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; + if (rem.tv_nsec < 0) { + rem.tv_sec -= 1; + rem.tv_nsec += 1000000000L; + } + if (rem.tv_sec < 0) + /* This job will be removed momentarily, do not list it. (To simply things.) */ + return 0; + + /* Get clock name. */ + switch (job->clk) { + case CLOCK_REALTIME: clk = "walltime"; break; + case CLOCK_BOOTTIME: clk = "boottime"; break; + default: clk = "unrecognised"; break; + } + + /* Get textual representation of the remaining time. (Seconds only.) */ + strduration(rem_s, rem.tv_sec); + + /* Get textual representation of the expiration time. */ + switch (job->clk) { + case CLOCK_REALTIME: + t (!(tm = localtime(job->ts.tv_sec))); + strftime(timestr_a, sizeof(timestr), "%Y-%m-%d %H:%M:%S", tm); + break; + default: + strduration(timestr_a, job->ts.tv_sec); + break; + } + sprintf(timestr_b, "%09li", job->ts.tv_nsec); + + /* Get arguments. */ + t (!(args = restore_array(job->payload, job->n, &argsn))); + t (!(argv = sublist(args, (size_t)argc))); + t (!(envp = sublist(args + argc, argsn - (size_t)argc))); + + /* Send message. */ + t (!(qstr = quote(args[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", + " 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, *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)); + +done: + saved_errno = errno; + free(qstr); + free(args); + free(argv); + free(envp); + errno = saved_errno; + return rc; + +fail: + rc = -1; + goto done; +} @@ -38,12 +247,31 @@ main(int argc, char *argv[]) { size_t n = 0; char *message = NULL; + struct job** jobs; + struct job** job; /* Receive and validate message. */ t (readall(SOCK_FILENO, &message, &n) || n); shutdown(SOCK_FILENO, SHUT_RD); - return 0; + /* Perform action. */ + t (!(jobs = get_jobs())); + for (job = jobs; *job; job++) + t (send_job_str(*job)); + +done: + /* Cleanup. */ + shutdown(SOCK_FILENO, SHUT_WR); + close(SOCK_FILENO); + for (job = jobs; *job; job++) + free(*job); + free(jobs); + free(message); + return rc; fail: + if (send_string(SOCK_FILENO, STDERR_FILENO, argv[0], ": ", strerror(errno) "\n", NULL)) + perror(argv[0]); + rc = 1; + goto done; } |