]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
systemctl: use format-table.[ch] for tables
authorFrantisek Sumsal <frantisek@sumsal.cz>
Fri, 3 Jan 2020 12:01:26 +0000 (13:01 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 14 Jan 2020 14:26:21 +0000 (15:26 +0100)
src/systemctl/systemctl.c

index 64c1401afc254fa982e12b87ef6efbbae61178cf..60872bc5373126648d66f66bb913d8f69cfc8319 100644 (file)
@@ -39,6 +39,7 @@
 #include "exec-util.h"
 #include "exit-status.h"
 #include "fd-util.h"
+#include "format-table.h"
 #include "format-util.h"
 #include "fs-util.h"
 #include "glob-util.h"
@@ -389,122 +390,42 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) {
 }
 
 static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
-        unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len, max_desc_len;
+        _cleanup_(table_unrefp) Table *table = NULL;
         const UnitInfo *u;
-        unsigned n_shown = 0;
         int job_count = 0;
-        bool full = arg_full || FLAGS_SET(arg_pager_flags, PAGER_DISABLE);
-
-        max_id_len = STRLEN("UNIT");
-        load_len = STRLEN("LOAD");
-        active_len = STRLEN("ACTIVE");
-        sub_len = STRLEN("SUB");
-        job_len = STRLEN("JOB");
-        max_desc_len = STRLEN("DESCRIPTION");
-
-        for (u = unit_infos; u < unit_infos + c; u++) {
-                max_id_len = MAX(max_id_len, strlen(u->id) + (u->machine ? strlen(u->machine)+1 : 0));
-                load_len = MAX(load_len, strlen(u->load_state));
-                active_len = MAX(active_len, strlen(u->active_state));
-                sub_len = MAX(sub_len, strlen(u->sub_state));
-                max_desc_len = MAX(max_desc_len, strlen(u->description));
-
-                if (u->job_id != 0) {
-                        job_len = MAX(job_len, strlen(u->job_type));
-                        job_count++;
-                }
-
-                if (!arg_no_legend &&
-                    (streq(u->active_state, "failed") ||
-                     STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked")))
-                        circle_len = 2;
-        }
-
-        if (!arg_full && original_stdout_is_tty) {
-                unsigned basic_len;
-
-                id_len = MIN(max_id_len, 25u); /* as much as it needs, but at most 25 for now */
-                basic_len = circle_len + 1 + id_len + 1 + load_len + 1 + active_len + 1 + sub_len + 1;
-
-                if (job_count)
-                        basic_len += job_len + 1;
+        int r;
 
-                if (basic_len < (unsigned) columns()) {
-                        unsigned extra_len, incr;
-                        extra_len = columns() - basic_len;
+        table = table_new("", "unit", "load", "active", "sub", "job", "description");
+        if (!table)
+                return log_oom();
 
-                        /* Either UNIT already got 25, or is fully satisfied.
-                         * Grant up to 25 to DESC now. */
-                        incr = MIN(extra_len, 25u);
-                        desc_len = incr;
-                        extra_len -= incr;
-
-                        /* Of the remainder give as much as the ID needs to the ID, and give the rest to the
-                         * description but not more than it needs. */
-                        if (extra_len > 0) {
-                                incr = MIN(max_id_len - id_len, extra_len);
-                                id_len += incr;
-                                desc_len += MIN(extra_len - incr, max_desc_len - desc_len);
-                        }
-                } else
-                        desc_len = 0;
-        } else {
-                id_len = max_id_len;
-                desc_len = max_desc_len;
-        }
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
         for (u = unit_infos; u < unit_infos + c; u++) {
-                _cleanup_free_ char *e = NULL, *j = NULL;
-                const char *on_underline = "", *off_underline = "";
-                const char *on_loaded = "", *off_loaded = "";
-                const char *on_active = "", *off_active = "";
-                const char *on_circle = "", *off_circle = "";
-                const char *id;
+                _cleanup_free_ char *j = NULL;
+                const char *on_underline = "", *on_loaded = "", *on_active = "";
+                const char *on_circle = "", *id;
                 bool circle = false, underline = false;
 
-                if (!n_shown && !arg_no_legend) {
-
-                        if (circle_len > 0)
-                                fputs("  ", stdout);
-
-                        printf("%s%-*s %-*s %-*s %-*s ",
-                               ansi_underline(),
-                               id_len, "UNIT",
-                               load_len, "LOAD",
-                               active_len, "ACTIVE",
-                               sub_len, "SUB");
-
-                        if (job_count)
-                                printf("%-*s ", job_len, "JOB");
-
-                        printf("%-*.*s%s\n",
-                               desc_len,
-                               full ? -1 : (int) desc_len,
-                               "DESCRIPTION",
-                               ansi_normal());
-                }
-
-                n_shown++;
-
                 if (u + 1 < unit_infos + c &&
                     !streq(unit_type_suffix(u->id), unit_type_suffix((u + 1)->id))) {
                         on_underline = ansi_underline();
-                        off_underline = ansi_normal();
                         underline = true;
                 }
 
                 if (STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked") && !arg_plain) {
                         on_circle = ansi_highlight_yellow();
-                        off_circle = ansi_normal();
                         circle = true;
                         on_loaded = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
-                        off_loaded = underline ? on_underline : ansi_normal();
                 } else if (streq(u->active_state, "failed") && !arg_plain) {
                         on_circle = ansi_highlight_red();
-                        off_circle = ansi_normal();
                         circle = true;
                         on_active = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
-                        off_active = underline ? on_underline : ansi_normal();
+                } else {
+                        on_active = on_underline;
+                        on_loaded = on_underline;
                 }
 
                 if (u->machine) {
@@ -516,36 +437,47 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
                 } else
                         id = u->id;
 
-                if (arg_full) {
-                        e = ellipsize(id, id_len, 33);
-                        if (!e)
-                                return log_oom();
-
-                        id = e;
-                }
-
-                if (circle_len > 0)
-                        printf("%s%s%s ", on_circle, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_circle);
+                r = table_add_many(table,
+                                   TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
+                                   TABLE_SET_COLOR, on_circle,
+                                   TABLE_STRING, id,
+                                   TABLE_SET_COLOR, on_active,
+                                   TABLE_STRING, u->load_state,
+                                   TABLE_SET_COLOR, on_loaded,
+                                   TABLE_STRING, u->active_state,
+                                   TABLE_SET_COLOR, on_active,
+                                   TABLE_STRING, u->sub_state,
+                                   TABLE_SET_COLOR, on_active,
+                                   TABLE_STRING, u->job_id ? u->job_type: "",
+                                   TABLE_SET_COLOR, u->job_id ? on_underline : "",
+                                   TABLE_STRING, u->description,
+                                   TABLE_SET_COLOR, on_underline);
+                if (r < 0)
+                        return table_log_add_error(r);
 
-                printf("%s%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s",
-                       on_underline,
-                       on_active, id_len, id, off_active,
-                       on_loaded, load_len, u->load_state, off_loaded,
-                       on_active, active_len, u->active_state,
-                       sub_len, u->sub_state, off_active,
-                       job_count ? job_len + 1 : 0, u->job_id ? u->job_type : "");
+                if (u->job_id != 0)
+                        job_count++;
+        }
 
-                printf("%-*.*s%s\n",
-                       desc_len,
-                       full ? -1 : (int) desc_len,
-                       u->description,
-                       off_underline);
+        if (job_count == 0) {
+                /* There's no data in the JOB column, so let's hide it */
+                /* Also, convert all number constants to size_t so va_arg()
+                 * in table_set_display() fetches a correct number of bytes from
+                 * the stack */
+                r = table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 6, (size_t) -1);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set columns to display: %m");
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend) {
                 const char *on, *off;
+                size_t records = table_get_rows(table) - 1;
 
-                if (n_shown) {
+                if (records > 0) {
                         puts("\n"
                              "LOAD   = Reflects whether the unit definition was properly loaded.\n"
                              "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
@@ -559,15 +491,15 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
                 }
 
                 if (arg_all || strv_contains(arg_states, "inactive"))
-                        printf("%s%u loaded units listed.%s\n"
+                        printf("%s%zu loaded units listed.%s\n"
                                "To show all installed unit files use 'systemctl list-unit-files'.\n",
-                               on, n_shown, off);
+                               on, records, off);
                 else if (!arg_states)
-                        printf("%s%u loaded units listed.%s Pass --all to see loaded but inactive units, too.\n"
+                        printf("%s%zu loaded units listed.%s Pass --all to see loaded but inactive units, too.\n"
                                "To show all installed unit files use 'systemctl list-unit-files'.\n",
-                               on, n_shown, off);
+                               on, records, off);
                 else
-                        printf("%u loaded units listed.\n", n_shown);
+                        printf("%zu loaded units listed.\n", records);
         }
 
         return 0;
@@ -1048,39 +980,30 @@ static int socket_info_compare(const struct socket_info *a, const struct socket_
 }
 
 static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
+        _cleanup_(table_unrefp) Table *table = NULL;
         struct socket_info *s;
-        unsigned pathlen = STRLEN("LISTEN"),
-                typelen = STRLEN("TYPE") * arg_show_types,
-                socklen = STRLEN("UNIT"),
-                servlen = STRLEN("ACTIVATES");
         const char *on, *off;
+        int r;
 
-        for (s = socket_infos; s < socket_infos + cs; s++) {
-                unsigned tmp = 0;
-                char **a;
-
-                socklen = MAX(socklen, strlen(s->id));
-                if (arg_show_types)
-                        typelen = MAX(typelen, strlen(s->type));
-                pathlen = MAX(pathlen, strlen(s->path) + (s->machine ? strlen(s->machine)+1 : 0));
+        table = table_new("listen", "type", "units", "activates");
+        if (!table)
+                return log_oom();
 
-                STRV_FOREACH(a, s->triggered)
-                        tmp += strlen(*a) + 2*(a != s->triggered);
-                servlen = MAX(servlen, tmp);
+        if (!arg_show_types) {
+                /* Hide the second (TYPE) column */
+                r = table_set_display(table, 0, 2, 3, (size_t) -1);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set columns to display: %m");
         }
 
-        if (cs) {
-                if (!arg_no_legend)
-                        printf("%-*s %-*.*s%-*s %s\n",
-                               pathlen, "LISTEN",
-                               typelen + arg_show_types, typelen + arg_show_types, "TYPE ",
-                               socklen, "UNIT",
-                               "ACTIVATES");
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
+        if (cs) {
                 for (s = socket_infos; s < socket_infos + cs; s++) {
-                        _cleanup_free_ char *j = NULL;
+                        _cleanup_free_ char *j = NULL, *activates = NULL;
                         const char *path;
-                        char **a;
 
                         if (s->machine) {
                                 j = strjoin(s->machine, ":", s->path);
@@ -1090,29 +1013,32 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
                         } else
                                 path = s->path;
 
-                        if (arg_show_types)
-                                printf("%-*s %-*s %-*s",
-                                       pathlen, path, typelen, s->type, socklen, s->id);
-                        else
-                                printf("%-*s %-*s",
-                                       pathlen, path, socklen, s->id);
-                        STRV_FOREACH(a, s->triggered)
-                                printf("%s %s",
-                                       a == s->triggered ? "" : ",", *a);
-                        printf("\n");
+                        activates = strv_join(s->triggered, ", ");
+                        if (!activates)
+                                return log_oom();
+
+                        r = table_add_many(table,
+                                           TABLE_STRING, path,
+                                           TABLE_STRING, s->type,
+                                           TABLE_STRING, s->id,
+                                           TABLE_STRING, activates);
+                        if (r < 0)
+                                return table_log_add_error(r);
                 }
 
                 on = ansi_highlight();
                 off = ansi_normal();
-                if (!arg_no_legend)
-                        printf("\n");
         } else {
                 on = ansi_highlight_red();
                 off = ansi_normal();
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend) {
-                printf("%s%u sockets listed.%s\n", on, cs, off);
+                printf("\n%s%u sockets listed.%s\n", on, cs, off);
                 if (!arg_all)
                         printf("Pass --all to see loaded but inactive sockets, too.\n");
         }
@@ -1303,73 +1229,25 @@ static int timer_info_compare(const struct timer_info *a, const struct timer_inf
 }
 
 static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
+        _cleanup_(table_unrefp) Table *table = NULL;
         struct timer_info *t;
-        unsigned
-                nextlen = STRLEN("NEXT"),
-                leftlen = STRLEN("LEFT"),
-                lastlen = STRLEN("LAST"),
-                passedlen = STRLEN("PASSED"),
-                unitlen = STRLEN("UNIT"),
-                activatelen = STRLEN("ACTIVATES");
-
         const char *on, *off;
+        int r;
 
         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));
-                }
-
-                if (t->last_trigger > 0) {
-                        char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "";
-
-                        format_timestamp(tstamp, sizeof(tstamp), t->last_trigger);
-                        lastlen = MAX(lastlen, strlen(tstamp) + 1);
-
-                        format_timestamp_relative(trel, sizeof(trel), t->last_trigger);
-                        passedlen = MAX(passedlen, strlen(trel));
-                }
-
-                unitlen = MAX(unitlen, strlen(t->id) + (t->machine ? strlen(t->machine)+1 : 0));
-
-                STRV_FOREACH(a, t->triggered)
-                        ul += strlen(*a) + 2*(a != t->triggered);
+        table = table_new("next", "left", "last", "passed", "unit", "activates");
+        if (!table)
+                return log_oom();
 
-                activatelen = MAX(activatelen, ul);
-        }
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
         if (n > 0) {
-                if (!arg_no_legend)
-                        printf("%-*s %-*s %-*s %-*s %-*s %s\n",
-                               nextlen,   "NEXT",
-                               leftlen,   "LEFT",
-                               lastlen,   "LAST",
-                               passedlen, "PASSED",
-                               unitlen,   "UNIT",
-                                          "ACTIVATES");
-
                 for (t = timer_infos; t < timer_infos + n; t++) {
-                        _cleanup_free_ char *j = NULL;
+                        _cleanup_free_ char *j = NULL, *activates = NULL;
                         const char *unit;
-                        char tstamp1[FORMAT_TIMESTAMP_MAX] = "n/a", trel1[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
-                        char tstamp2[FORMAT_TIMESTAMP_MAX] = "n/a", trel2[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
-                        char **a;
-
-                        format_timestamp(tstamp1, sizeof(tstamp1), t->next_elapse);
-                        format_timestamp_relative(trel1, sizeof(trel1), t->next_elapse);
-
-                        format_timestamp(tstamp2, sizeof(tstamp2), t->last_trigger);
-                        format_timestamp_relative(trel2, sizeof(trel2), t->last_trigger);
 
                         if (t->machine) {
                                 j = strjoin(t->machine, ":", t->id);
@@ -1379,26 +1257,34 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
                         } else
                                 unit = t->id;
 
-                        printf("%-*s %-*s %-*s %-*s %-*s",
-                               nextlen, tstamp1, leftlen, trel1, lastlen, tstamp2, passedlen, trel2, unitlen, unit);
+                        activates = strv_join(t->triggered, ", ");
+                        if (!activates)
+                                return log_oom();
 
-                        STRV_FOREACH(a, t->triggered)
-                                printf("%s %s",
-                                       a == t->triggered ? "" : ",", *a);
-                        printf("\n");
+                        r = table_add_many(table,
+                                           TABLE_TIMESTAMP, t->next_elapse,
+                                           TABLE_TIMESTAMP_RELATIVE, t->next_elapse,
+                                           TABLE_TIMESTAMP, t->last_trigger,
+                                           TABLE_TIMESTAMP_RELATIVE, t->last_trigger,
+                                           TABLE_STRING, unit,
+                                           TABLE_STRING, activates);
+                        if (r < 0)
+                                return table_log_add_error(r);
                 }
 
                 on = ansi_highlight();
                 off = ansi_normal();
-                if (!arg_no_legend)
-                        printf("\n");
         } else {
                 on = ansi_highlight_red();
                 off = ansi_normal();
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend) {
-                printf("%s%u timers listed.%s\n", on, n, off);
+                printf("\n%s%u timers listed.%s\n", on, n, off);
                 if (!arg_all)
                         printf("Pass --all to see loaded but inactive timers, too.\n");
         }
@@ -1549,42 +1435,22 @@ static bool output_show_unit_file(const UnitFileList *u, char **states, char **p
         return true;
 }
 
-static void output_unit_file_list(const UnitFileList *units, unsigned c) {
-        unsigned max_id_len, id_cols, state_cols, preset_cols;
+static int output_unit_file_list(const UnitFileList *units, unsigned c) {
+        _cleanup_(table_unrefp) Table *table = NULL;
         const UnitFileList *u;
+        int r;
 
-        max_id_len = STRLEN("UNIT FILE");
-        state_cols = STRLEN("STATE");
-        preset_cols = STRLEN("VENDOR PRESET");
-
-        for (u = units; u < units + c; u++) {
-                max_id_len = MAX(max_id_len, strlen(basename(u->path)));
-                state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
-        }
-
-        if (!arg_full) {
-                unsigned basic_cols;
-
-                id_cols = MIN(max_id_len, 25u);
-                basic_cols = 1 + id_cols + state_cols;
-                if (basic_cols < (unsigned) columns())
-                        id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
-        } else
-                id_cols = max_id_len;
+        table = table_new("unit file", "state", "vendor preset");
+        if (!table)
+                return log_oom();
 
-        if (!arg_no_legend && c > 0)
-                printf("%s%-*s %-*s %-*s%s\n",
-                       ansi_underline(),
-                       id_cols, "UNIT FILE",
-                       state_cols, "STATE",
-                       preset_cols, "VENDOR PRESET",
-                       ansi_normal());
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
         for (u = units; u < units + c; u++) {
                 const char *on_underline = NULL, *on_unit_color = NULL, *id;
-                const char *on_preset_color = NULL, *off_preset = NULL, *unit_preset_str;
-                _cleanup_free_ char *e = NULL;
-                int r;
+                const char *on_preset_color = NULL, *unit_preset_str;
                 bool underline;
 
                 underline = u + 1 < units + c &&
@@ -1601,6 +1467,8 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
                         on_unit_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
                 else if (u->state == UNIT_FILE_ENABLED)
                         on_unit_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
+                else
+                        on_unit_color = on_underline;
 
                 id = basename(u->path);
 
@@ -1616,20 +1484,25 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
                         on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
                 }
 
-                if (on_underline || on_preset_color)
-                        off_preset = ansi_normal();
-
-                e = arg_full ? NULL : ellipsize(id, id_cols, 33);
-
-                printf("%s%-*s %s%-*s %s%-*s%s\n",
-                       strempty(on_underline),
-                       id_cols, e ? e : id,
-                       strempty(on_unit_color), state_cols, unit_file_state_to_string(u->state),
-                       strempty(on_preset_color), preset_cols, unit_preset_str, strempty(off_preset));
+                r = table_add_many(table,
+                                   TABLE_STRING, id,
+                                   TABLE_SET_COLOR, strempty(on_underline),
+                                   TABLE_STRING, unit_file_state_to_string(u->state),
+                                   TABLE_SET_COLOR, strempty(on_unit_color),
+                                   TABLE_STRING, unit_preset_str,
+                                   TABLE_SET_COLOR, strempty(on_preset_color));
+                if (r < 0)
+                        return table_log_add_error(r);
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend)
                 printf("\n%u unit files listed.\n", c);
+
+        return 0;
 }
 
 static int list_unit_files(int argc, char *argv[], void *userdata) {
@@ -1772,7 +1645,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
         (void) pager_open(arg_pager_flags);
 
         typesafe_qsort(units, c, compare_unit_file_list);
-        output_unit_file_list(units, c);
+        r = output_unit_file_list(units, c);
+        if (r < 0)
+                return r;
 
         if (install_client_side())
                 for (unit = units; unit < units + c; unit++)
@@ -2081,94 +1956,77 @@ static int get_machine_list(
         return c;
 }
 
-static void output_machines_list(struct machine_info *machine_infos, unsigned n) {
+static int output_machines_list(struct machine_info *machine_infos, unsigned n) {
+        _cleanup_(table_unrefp) Table *table = NULL;
         struct machine_info *m;
-        unsigned
-                circle_len = 0,
-                namelen = STRLEN("NAME"),
-                statelen = STRLEN("STATE"),
-                failedlen = STRLEN("FAILED"),
-                jobslen = STRLEN("JOBS");
         bool state_missing = false;
+        int r;
 
         assert(machine_infos || n == 0);
 
-        for (m = machine_infos; m < machine_infos + n; m++) {
-                namelen = MAX(namelen,
-                              strlen(m->name) + (m->is_host ? STRLEN(" (host)") : 0));
-                statelen = MAX(statelen, strlen_ptr(m->state));
-                failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units));
-                jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs));
-
-                if (!arg_plain && m->state && !streq(m->state, "running"))
-                        circle_len = 2;
-        }
-
-        if (!arg_no_legend) {
-                if (circle_len > 0)
-                        fputs("  ", stdout);
+        table = table_new("", "name", "state", "failed", "jobs");
+        if (!table)
+                return log_oom();
 
-                printf("%-*s %-*s %-*s %-*s\n",
-                         namelen, "NAME",
-                        statelen, "STATE",
-                       failedlen, "FAILED",
-                         jobslen, "JOBS");
-        }
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
         for (m = machine_infos; m < machine_infos + n; m++) {
-                const char *on_state = "", *off_state = "";
-                const char *on_failed = "", *off_failed = "";
+                _cleanup_free_ char *mname = NULL;
+                const char *on_state = "", *on_failed = "";
                 bool circle = false;
 
                 if (streq_ptr(m->state, "degraded")) {
                         on_state = ansi_highlight_red();
-                        off_state = ansi_normal();
                         circle = true;
                 } else if (!streq_ptr(m->state, "running")) {
                         on_state = ansi_highlight_yellow();
-                        off_state = ansi_normal();
                         circle = true;
                 }
 
-                if (m->n_failed_units > 0) {
+                if (m->n_failed_units > 0)
                         on_failed = ansi_highlight_red();
-                        off_failed = ansi_normal();
-                } else
-                        on_failed = off_failed = "";
-
-                if (circle_len > 0)
-                        printf("%s%s%s ", on_state, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_state);
+                else
+                        on_failed =  "";
 
                 if (!m->state)
                         state_missing = true;
 
                 if (m->is_host)
-                        printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
-                               (int) (namelen - strlen(" (host)")),
-                               strna(m->name),
-                               on_state, statelen, strna(m->state), off_state,
-                               on_failed, failedlen, m->n_failed_units, off_failed,
-                               jobslen, m->n_jobs);
-                else
-                        printf("%-*s %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
-                               namelen, strna(m->name),
-                               on_state, statelen, strna(m->state), off_state,
-                               on_failed, failedlen, m->n_failed_units, off_failed,
-                               jobslen, m->n_jobs);
+                        mname = strjoin(strna(m->name), " (host)");
+
+                r = table_add_many(table,
+                                   TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
+                                   TABLE_SET_COLOR, on_state,
+                                   TABLE_STRING, m->is_host ? mname : strna(m->name),
+                                   TABLE_STRING, strna(m->state),
+                                   TABLE_SET_COLOR, on_state,
+                                   TABLE_UINT32, m->n_failed_units,
+                                   TABLE_SET_COLOR, on_failed,
+                                   TABLE_UINT32, m->n_jobs);
+                if (r < 0)
+                        return table_log_add_error(r);
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend) {
                 printf("\n");
                 if (state_missing && geteuid() != 0)
                         printf("Notice: some information only available to privileged users was not shown.\n");
                 printf("%u machines listed.\n", n);
         }
+
+        return 0;
 }
 
 static int list_machines(int argc, char *argv[], void *userdata) {
         struct machine_info *machine_infos = NULL;
         sd_bus *bus;
-        int r;
+        int r, rc;
 
         r = acquire_bus(BUS_MANAGER, &bus);
         if (r < 0)
@@ -2181,10 +2039,10 @@ static int list_machines(int argc, char *argv[], void *userdata) {
         (void) pager_open(arg_pager_flags);
 
         typesafe_qsort(machine_infos, r, compare_machine_info);
-        output_machines_list(machine_infos, r);
+        rc = output_machines_list(machine_infos, r);
         free_machines_list(machine_infos, r);
 
-        return 0;
+        return rc;
 }
 
 static int get_default(int argc, char *argv[], void *userdata) {
@@ -2292,7 +2150,7 @@ finish:
         return r;
 }
 
-static int output_waiting_jobs(sd_bus *bus, uint32_t id, const char *method, const char *prefix) {
+static int output_waiting_jobs(sd_bus *bus, Table *table, uint32_t id, const char *method, const char *prefix) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         const char *name, *type;
@@ -2317,8 +2175,22 @@ static int output_waiting_jobs(sd_bus *bus, uint32_t id, const char *method, con
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, NULL, NULL, NULL)) > 0)
-                printf("%s %u (%s/%s)\n", prefix, other_id, name, type);
+        while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, NULL, NULL, NULL)) > 0) {
+                _cleanup_free_ char *row = NULL;
+                int rc;
+
+                if (asprintf(&row, "%s %u (%s/%s)", prefix, other_id, name, type) < 0)
+                        return log_oom();
+
+                rc = table_add_many(table,
+                                    TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
+                                    TABLE_STRING, row,
+                                    TABLE_EMPTY,
+                                    TABLE_EMPTY);
+                if (rc < 0)
+                        return table_log_add_error(r);
+        }
+
         if (r < 0)
                 return bus_log_parse_error(r);
 
@@ -2334,11 +2206,11 @@ struct job_info {
         const char *name, *type, *state;
 };
 
-static void output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n, bool skipped) {
-        unsigned id_len, unit_len, type_len, state_len;
+static int output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n, bool skipped) {
+        _cleanup_(table_unrefp) Table *table = NULL;
         const struct job_info *j;
         const char *on, *off;
-        bool shorten = false;
+        int r;
 
         assert(n == 0 || jobs);
 
@@ -2349,66 +2221,54 @@ static void output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned
 
                         printf("%sNo jobs %s.%s\n", on, skipped ? "listed" : "running", off);
                 }
-                return;
+                return 0;
         }
 
         (void) pager_open(arg_pager_flags);
 
-        id_len = STRLEN("JOB");
-        unit_len = STRLEN("UNIT");
-        type_len = STRLEN("TYPE");
-        state_len = STRLEN("STATE");
-
-        for (j = jobs; j < jobs + n; j++) {
-                uint32_t id = j->id;
-                assert(j->name && j->type && j->state);
-
-                id_len = MAX(id_len, DECIMAL_STR_WIDTH(id));
-                unit_len = MAX(unit_len, strlen(j->name));
-                type_len = MAX(type_len, strlen(j->type));
-                state_len = MAX(state_len, strlen(j->state));
-        }
-
-        if (!arg_full && id_len + 1 + unit_len + type_len + 1 + state_len > columns()) {
-                unit_len = MAX(33u, columns() - id_len - type_len - state_len - 3);
-                shorten = true;
-        }
+        table = table_new("job", "unit", "type", "state");
+        if (!table)
+                return log_oom();
 
-        if (!arg_no_legend)
-                printf("%*s %-*s %-*s %-*s\n",
-                       id_len, "JOB",
-                       unit_len, "UNIT",
-                       type_len, "TYPE",
-                       state_len, "STATE");
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
         for (j = jobs; j < jobs + n; j++) {
-                _cleanup_free_ char *e = NULL;
-
-                if (streq(j->state, "running")) {
+                if (streq(j->state, "running"))
                         on = ansi_highlight();
-                        off = ansi_normal();
-                } else
-                        on = off = "";
+                else
+                        on =  "";
+
 
-                e = shorten ? ellipsize(j->name, unit_len, 33) : NULL;
-                printf("%*u %s%-*s%s %-*s %s%-*s%s\n",
-                       id_len, j->id,
-                       on, unit_len, e ? e : j->name, off,
-                       type_len, j->type,
-                       on, state_len, j->state, off);
+                r = table_add_many(table,
+                                   TABLE_UINT, j->id,
+                                   TABLE_STRING, j->name,
+                                   TABLE_SET_COLOR, on,
+                                   TABLE_STRING, j->type,
+                                   TABLE_STRING, j->state,
+                                   TABLE_SET_COLOR, on);
+                if (r < 0)
+                        return table_log_add_error(r);
 
                 if (arg_jobs_after)
-                        output_waiting_jobs(bus, j->id, "GetJobAfter", "\twaiting for job");
+                        output_waiting_jobs(bus, table, j->id, "GetJobAfter", "\twaiting for job");
                 if (arg_jobs_before)
-                        output_waiting_jobs(bus, j->id, "GetJobBefore", "\tblocking job");
+                        output_waiting_jobs(bus, table, j->id, "GetJobBefore", "\tblocking job");
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend) {
                 on = ansi_highlight();
                 off = ansi_normal();
 
                 printf("\n%s%u jobs listed%s.\n", on, n, off);
         }
+
+        return 0;
 }
 
 static bool output_show_job(struct job_info *job, char **patterns) {
@@ -2469,8 +2329,7 @@ static int list_jobs(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_pager_flags);
 
-        output_jobs_list(bus, jobs, c, skipped);
-        return 0;
+        return output_jobs_list(bus, jobs, c, skipped);
 }
 
 static int cancel_job(int argc, char *argv[], void *userdata) {