[systemd-commits] 3 commits - TODO man/systemctl.xml src/shared src/systemctl

Lennart Poettering lennart at kemper.freedesktop.org
Mon Nov 11 06:59:46 PST 2013


 TODO                      |    2 
 man/systemctl.xml         |   11 +
 src/shared/time-util.c    |   73 ++++++++-----
 src/shared/time-util.h    |    5 
 src/systemctl/systemctl.c |  258 ++++++++++++++++++++++++++++++++++++++++++----
 5 files changed, 303 insertions(+), 46 deletions(-)

New commits:
commit cbb76c29ccd1a639ada00425fa7cc408507f171d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Nov 11 03:06:14 2013 +0100

    systemctl: add new "list-timers" command

diff --git a/man/systemctl.xml b/man/systemctl.xml
index d842b78..33a2935 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -555,6 +555,17 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
         </varlistentry>
 
         <varlistentry>
+          <term><command>list-timers</command></term>
+
+          <listitem>
+            <para>List timer units ordered by the time they elapse next.</para>
+
+            <para>See also the options <option>--all</option> and
+            <option>--failed</option>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
           <term><command>start <replaceable>NAME</replaceable>...</command></term>
 
           <listitem>
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index df4bd21..c5166d5 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -532,13 +532,12 @@ static int get_triggered_units(
 static int get_listening(
                 sd_bus *bus,
                 const char* unit_path,
-                char*** listening,
-                unsigned *c) {
+                char*** listening) {
 
-        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         const char *type, *path;
-        int r;
+        int r, n = 0;
 
         r = sd_bus_get_property(
                         bus,
@@ -568,7 +567,7 @@ static int get_listening(
                 if (r < 0)
                         return log_oom();
 
-                (*c)++;
+                n++;
         }
         if (r < 0)
                 return bus_log_parse_error(r);
@@ -577,7 +576,7 @@ static int get_listening(
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        return 0;
+        return n;
 }
 
 struct socket_info {
@@ -595,9 +594,12 @@ struct socket_info {
         bool own_triggered;
 };
 
-static int socket_info_compare(struct socket_info *a, struct socket_info *b) {
+static int socket_info_compare(const struct socket_info *a, const struct socket_info *b) {
         int o;
 
+        assert(a);
+        assert(b);
+
         o = strcmp(a->path, b->path);
         if (o == 0)
                 o = strcmp(a->type, b->type);
@@ -674,21 +676,19 @@ static int list_sockets(sd_bus *bus, char **args) {
         struct socket_info *socket_infos = NULL;
         const UnitInfo *u;
         struct socket_info *s;
-        unsigned cu = 0, cs = 0;
+        unsigned cs = 0;
         size_t size = 0;
-        int r;
+        int r, n;
 
         pager_open_if_enabled();
 
-        r = get_unit_list(bus, &reply, &unit_infos);
-        if (r < 0)
-                return r;
+        n = get_unit_list(bus, &reply, &unit_infos);
+        if (n < 0)
+                return n;
 
-        cu = (unsigned) r;
-
-        for (u = unit_infos; u < unit_infos + cu; u++) {
+        for (u = unit_infos; u < unit_infos + n; u++) {
                 _cleanup_strv_free_ char **listening = NULL, **triggered = NULL;
-                unsigned c = 0, i;
+                int i, c;
 
                 if (!output_show_unit(u))
                         continue;
@@ -700,9 +700,11 @@ static int list_sockets(sd_bus *bus, char **args) {
                 if (r < 0)
                         goto cleanup;
 
-                r = get_listening(bus, u->unit_path, &listening, &c);
-                if (r < 0)
+                c = get_listening(bus, u->unit_path, &listening);
+                if (c < 0) {
+                        r = c;
                         goto cleanup;
+                }
 
                 if (!GREEDY_REALLOC(socket_infos, size, cs + c)) {
                         r = log_oom();
@@ -742,6 +744,224 @@ static int list_sockets(sd_bus *bus, char **args) {
         return r;
 }
 
+static int get_next_elapse(
+                sd_bus *bus,
+                const char *path,
+                dual_timestamp *next) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        dual_timestamp t;
+        int r;
+
+        assert(bus);
+        assert(path);
+        assert(next);
+
+        r = sd_bus_get_property_trivial(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        path,
+                        "org.freedesktop.systemd1.Timer",
+                        "NextElapseUSecMonotonic",
+                        &error,
+                        't',
+                        &t.monotonic);
+        if (r < 0) {
+                log_error("Failed to get next elapsation time: %s", bus_error_message(&error, r));
+                return r;
+        }
+
+        r = sd_bus_get_property_trivial(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        path,
+                        "org.freedesktop.systemd1.Timer",
+                        "NextElapseUSecRealtime",
+                        &error,
+                        't',
+                        &t.realtime);
+        if (r < 0) {
+                log_error("Failed to get next elapsation time: %s", bus_error_message(&error, r));
+                return r;
+        }
+
+        *next = t;
+        return 0;
+}
+
+struct timer_info {
+        const char* id;
+        usec_t next_elapse;
+        char** triggered;
+};
+
+static int timer_info_compare(const struct timer_info *a, const struct timer_info *b) {
+        assert(a);
+        assert(b);
+
+        if (a->next_elapse < b->next_elapse)
+                return -1;
+        if (a->next_elapse > b->next_elapse)
+                return 1;
+
+        return strcmp(a->id, b->id);
+}
+
+static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
+        struct timer_info *t;
+        unsigned
+                nextlen = sizeof("NEXT") - 1,
+                leftlen = sizeof("LEFT") - 1,
+                unitlen = sizeof("UNIT") - 1,
+                activatelen = sizeof("ACTIVATES") - 1;
+
+        const char *on, *off;
+
+        assert(timer_infos || n == 0);
+
+        for (t = timer_infos; t < timer_infos + n; t++) {
+                unsigned ul = 0;
+                char **a;
+
+                if (t->next_elapse > 0) {
+                        char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "";
+
+                        format_timestamp(tstamp, sizeof(tstamp), t->next_elapse);
+                        nextlen = MAX(nextlen, strlen(tstamp) + 1);
+
+                        format_timestamp_relative(trel, sizeof(trel), t->next_elapse);
+                        leftlen = MAX(leftlen, strlen(trel));
+                }
+
+                unitlen = MAX(unitlen, strlen(t->id));
+
+                STRV_FOREACH(a, t->triggered)
+                        ul += strlen(*a) + 2*(a != t->triggered);
+                activatelen = MAX(activatelen, ul);
+        }
+
+        if (n > 0) {
+                if (!arg_no_legend)
+                        printf("%-*s %-*s %-*s %s\n",
+                               nextlen, "NEXT",
+                               leftlen, "LEFT",
+                               unitlen, "UNIT",
+                                        "ACTIVATES");
+
+                for (t = timer_infos; t < timer_infos + n; t++) {
+                        char tstamp[FORMAT_TIMESTAMP_MAX] = "n/a", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
+                        char **a;
+
+                        format_timestamp(tstamp, sizeof(tstamp), t->next_elapse);
+                        format_timestamp_relative(trel, sizeof(trel), t->next_elapse);
+
+                        printf("%-*s %-*s %-*s",
+                               nextlen, tstamp, leftlen, trel, unitlen, t->id);
+
+                        STRV_FOREACH(a, t->triggered)
+                                printf("%s %s",
+                                       a == t->triggered ? "" : ",", *a);
+                        printf("\n");
+                }
+
+                on = ansi_highlight();
+                off = ansi_highlight_off();
+                if (!arg_no_legend)
+                        printf("\n");
+        } else {
+                on = ansi_highlight_red();
+                off = ansi_highlight_off();
+        }
+
+        if (!arg_no_legend) {
+                printf("%s%u timers listed.%s\n", on, n, off);
+                if (!arg_all)
+                        printf("Pass --all to see loaded but inactive timers, too.\n");
+        }
+
+        return 0;
+}
+
+static int list_timers(sd_bus *bus, char **args) {
+
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        _cleanup_free_ struct timer_info *timer_infos = NULL;
+        _cleanup_free_ UnitInfo *unit_infos = NULL;
+        struct timer_info *t;
+        const UnitInfo *u;
+        size_t size = 0;
+        int n, r, c = 0;
+        dual_timestamp nw;
+
+        pager_open_if_enabled();
+
+        n = get_unit_list(bus, &reply, &unit_infos);
+        if (n < 0)
+                return n;
+
+        dual_timestamp_get(&nw);
+
+        for (u = unit_infos; u < unit_infos + n; u++) {
+                _cleanup_strv_free_ char **triggered = NULL;
+                dual_timestamp next;
+                usec_t m;
+
+                if (!output_show_unit(u))
+                        continue;
+
+                if (!endswith(u->id, ".timer"))
+                        continue;
+
+                r = get_triggered_units(bus, u->unit_path, &triggered);
+                if (r < 0)
+                        goto cleanup;
+
+                r = get_next_elapse(bus, u->unit_path, &next);
+                if (r < 0)
+                        goto cleanup;
+
+                if (next.monotonic != (usec_t) -1 && next.monotonic > 0) {
+                        usec_t converted;
+
+                        if (next.monotonic > nw.monotonic)
+                                converted = nw.realtime + (next.monotonic - nw.monotonic);
+                        else
+                                converted = nw.realtime - (nw.monotonic - next.monotonic);
+
+                        if (next.realtime != (usec_t) -1 && next.realtime > 0)
+                                m = MIN(converted, next.realtime);
+                        else
+                                m = converted;
+                } else
+                        m = next.realtime;
+
+                if (!GREEDY_REALLOC(timer_infos, size, c+1)) {
+                        r = log_oom();
+                        goto cleanup;
+                }
+
+                timer_infos[c++] = (struct timer_info) {
+                        .id = u->id,
+                        .next_elapse = m,
+                        .triggered = triggered,
+                };
+
+                triggered = NULL; /* avoid cleanup */
+        }
+
+        qsort_safe(timer_infos, c, sizeof(struct timer_info),
+                   (__compar_fn_t) timer_info_compare);
+
+        output_timers_list(timer_infos, c);
+
+ cleanup:
+        for (t = timer_infos; t < timer_infos + c; t++)
+                strv_free(t->triggered);
+
+        return r;
+}
+
 static int compare_unit_file_list(const void *a, const void *b) {
         const char *d1, *d2;
         const UnitFileList *u = a, *v = b;
@@ -4404,6 +4624,7 @@ static int systemctl_help(void) {
                "Unit Commands:\n"
                "  list-units                      List loaded units\n"
                "  list-sockets                    List loaded sockets ordered by address\n"
+               "  list-timers                     List loaded timers ordered by next elapse\n"
                "  start [NAME...]                 Start (activate) one or more units\n"
                "  stop [NAME...]                  Stop (deactivate) one or more units\n"
                "  reload [NAME...]                Reload one or more units\n"
@@ -5376,6 +5597,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 { "list-units",            LESS,  1, list_units        },
                 { "list-unit-files",       EQUAL, 1, list_unit_files   },
                 { "list-sockets",          LESS,  1, list_sockets      },
+                { "list-timers",           LESS,  1, list_timers       },
                 { "list-jobs",             EQUAL, 1, list_jobs         },
                 { "clear-jobs",            EQUAL, 1, daemon_reload     },
                 { "cancel",                MORE,  2, cancel_job        },

commit 966204e010ed432a1d7a0481d41a326d8ec7b0c8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Nov 11 03:03:17 2013 +0100

    timer: consider (usec_t) -1 an invalid timestamp

diff --git a/TODO b/TODO
index fe5d2f5..efc7e2a 100644
--- a/TODO
+++ b/TODO
@@ -43,6 +43,8 @@ CGroup Rework Completion:
 
 Features:
 
+* be more careful what we export on the bus as (usec_t) 0 and (usec_t) -1
+
 * check :no-sender logic after PID 1 conversion
 
 * increase journal files by a few MB each time, instead of piecemeal
diff --git a/src/shared/time-util.c b/src/shared/time-util.c
index b29d8c6..55428c4 100644
--- a/src/shared/time-util.c
+++ b/src/shared/time-util.c
@@ -158,7 +158,7 @@ char *format_timestamp(char *buf, size_t l, usec_t t) {
         assert(buf);
         assert(l > 0);
 
-        if (t <= 0)
+        if (t <= 0 || t == (usec_t) -1)
                 return NULL;
 
         sec = (time_t) (t / USEC_PER_SEC);
@@ -176,7 +176,7 @@ char *format_timestamp_us(char *buf, size_t l, usec_t t) {
         assert(buf);
         assert(l > 0);
 
-        if (t <= 0)
+        if (t <= 0 || t == (usec_t) -1)
                 return NULL;
 
         sec = (time_t) (t / USEC_PER_SEC);
diff --git a/src/shared/time-util.h b/src/shared/time-util.h
index a51317d..0a3a98b 100644
--- a/src/shared/time-util.h
+++ b/src/shared/time-util.h
@@ -64,7 +64,10 @@ dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
 
-#define dual_timestamp_is_set(ts) ((ts)->realtime > 0)
+static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
+        return ((ts->realtime > 0 && ts->realtime != (usec_t) -1) ||
+                (ts->monotonic > 0 && ts->monotonic != (usec_t) -1));
+}
 
 usec_t timespec_load(const struct timespec *ts) _pure_;
 struct timespec *timespec_store(struct timespec *ts, usec_t u);

commit 1fcf71f562a83a59f853f982306fca7d009bb30d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Nov 11 03:02:52 2013 +0100

    timer: properly format relative timestamps in the future

diff --git a/src/shared/time-util.c b/src/shared/time-util.c
index 7bb7d4e..b29d8c6 100644
--- a/src/shared/time-util.c
+++ b/src/shared/time-util.c
@@ -192,55 +192,62 @@ char *format_timestamp_us(char *buf, size_t l, usec_t t) {
 }
 
 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
+        const char *s;
         usec_t n, d;
 
         n = now(CLOCK_REALTIME);
 
-        if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
+        if (t <= 0 || (t == (usec_t) -1))
                 return NULL;
 
-        d = n - t;
+        if (n > t) {
+                d = n - t;
+                s = "ago";
+        } else {
+                d = t - n;
+                s = "left";
+        }
 
         if (d >= USEC_PER_YEAR)
-                snprintf(buf, l, "%llu years %llu months ago",
+                snprintf(buf, l, "%llu years %llu months %s",
                          (unsigned long long) (d / USEC_PER_YEAR),
-                         (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
+                         (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH), s);
         else if (d >= USEC_PER_MONTH)
-                snprintf(buf, l, "%llu months %llu days ago",
+                snprintf(buf, l, "%llu months %llu days %s",
                          (unsigned long long) (d / USEC_PER_MONTH),
-                         (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
+                         (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY), s);
         else if (d >= USEC_PER_WEEK)
-                snprintf(buf, l, "%llu weeks %llu days ago",
+                snprintf(buf, l, "%llu weeks %llu days %s",
                          (unsigned long long) (d / USEC_PER_WEEK),
-                         (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
+                         (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY), s);
         else if (d >= 2*USEC_PER_DAY)
-                snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
+                snprintf(buf, l, "%llu days %s", (unsigned long long) (d / USEC_PER_DAY), s);
         else if (d >= 25*USEC_PER_HOUR)
-                snprintf(buf, l, "1 day %lluh ago",
-                         (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
+                snprintf(buf, l, "1 day %lluh %s",
+                         (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR), s);
         else if (d >= 6*USEC_PER_HOUR)
-                snprintf(buf, l, "%lluh ago",
-                         (unsigned long long) (d / USEC_PER_HOUR));
+                snprintf(buf, l, "%lluh %s",
+                         (unsigned long long) (d / USEC_PER_HOUR), s);
         else if (d >= USEC_PER_HOUR)
-                snprintf(buf, l, "%lluh %llumin ago",
+                snprintf(buf, l, "%lluh %llumin %s",
                          (unsigned long long) (d / USEC_PER_HOUR),
-                         (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
+                         (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE), s);
         else if (d >= 5*USEC_PER_MINUTE)
-                snprintf(buf, l, "%llumin ago",
-                         (unsigned long long) (d / USEC_PER_MINUTE));
+                snprintf(buf, l, "%llumin %s",
+                         (unsigned long long) (d / USEC_PER_MINUTE), s);
         else if (d >= USEC_PER_MINUTE)
-                snprintf(buf, l, "%llumin %llus ago",
+                snprintf(buf, l, "%llumin %llus %s",
                          (unsigned long long) (d / USEC_PER_MINUTE),
-                         (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
+                         (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC), s);
         else if (d >= USEC_PER_SEC)
-                snprintf(buf, l, "%llus ago",
-                         (unsigned long long) (d / USEC_PER_SEC));
+                snprintf(buf, l, "%llus %s",
+                         (unsigned long long) (d / USEC_PER_SEC), s);
         else if (d >= USEC_PER_MSEC)
-                snprintf(buf, l, "%llums ago",
-                         (unsigned long long) (d / USEC_PER_MSEC));
+                snprintf(buf, l, "%llums %s",
+                         (unsigned long long) (d / USEC_PER_MSEC), s);
         else if (d > 0)
-                snprintf(buf, l, "%lluus ago",
-                         (unsigned long long) d);
+                snprintf(buf, l, "%lluus %s",
+                         (unsigned long long) d, s);
         else
                 snprintf(buf, l, "now");
 
@@ -480,6 +487,18 @@ int parse_timestamp(const char *t, usec_t *usec) {
                         return r;
 
                 goto finish;
+        } else if (endswith(t, " left")) {
+                _cleanup_free_ char *z;
+
+                z = strndup(t, strlen(t) - 4);
+                if (!z)
+                        return -ENOMEM;
+
+                r = parse_sec(z, &plus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
         }
 
         for (i = 0; i < ELEMENTSOF(day_nr); i++) {



More information about the systemd-commits mailing list