From de9a8fe18e0168b65ae50b6dde2865f647fc92a2 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 3 Jan 2020 13:01:26 +0100 Subject: [PATCH] systemctl: use format-table.[ch] for tables --- src/systemctl/systemctl.c | 599 +++++++++++++++----------------------- 1 file changed, 229 insertions(+), 370 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 64c1401afc2..60872bc5373 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -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); + int r; - 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"); + table = table_new("", "unit", "load", "active", "sub", "job", "description"); + if (!table) + return log_oom(); + + table_set_header(table, !arg_no_legend); + if (arg_full) + table_set_width(table, 0); 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; - - if (basic_len < (unsigned) columns()) { - unsigned extra_len, incr; - extra_len = columns() - basic_len; - - /* 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; - } - - 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(); + 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); - id = e; - } - - if (circle_len > 0) - printf("%s%s%s ", on_circle, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_circle); - - 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 : ""); - - printf("%-*.*s%s\n", - desc_len, - full ? -1 : (int) desc_len, - u->description, - off_underline); + if (u->job_id != 0) + job_count++; } + 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; + table = table_new("listen", "type", "units", "activates"); + if (!table) + return log_oom(); - 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)); - - 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; + table = table_new("next", "left", "last", "passed", "unit", "activates"); + if (!table) + return log_oom(); - 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); - - 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"); + table = table_new("unit file", "state", "vendor preset"); + if (!table) + return log_oom(); - 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; - - 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)); + table = table_new("", "name", "state", "failed", "jobs"); + if (!table) + return log_oom(); - if (!arg_plain && m->state && !streq(m->state, "running")) - circle_len = 2; - } - - if (!arg_no_legend) { - if (circle_len > 0) - fputs(" ", stdout); - - 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"); + table = table_new("job", "unit", "type", "state"); + if (!table) + return log_oom(); + + table_set_header(table, !arg_no_legend); + if (arg_full) + table_set_width(table, 0); 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; - } - - if (!arg_no_legend) - printf("%*s %-*s %-*s %-*s\n", - id_len, "JOB", - unit_len, "UNIT", - type_len, "TYPE", - state_len, "STATE"); - - 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) {