aboutsummaryrefslogtreecommitdiffstats
path: root/src/satd-list.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/satd-list.c')
-rw-r--r--src/satd-list.c230
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;
}