diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index f79617ee06f..b2909a76a2f 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -29,8 +29,10 @@ #include "copy.h" #include "env-util.h" #include "fd-util.h" +#include "format-table.h" #include "hostname-util.h" #include "import-util.h" +#include "locale-util.h" #include "log.h" #include "logs-show.h" #include "macro.h" @@ -76,8 +78,6 @@ static const char *arg_uid = NULL; static char **arg_setenv = NULL; static int arg_addrs = 1; -static int print_addresses(sd_bus *bus, const char *name, int, const char *pr1, const char *pr2, int n_addr); - static OutputFlags get_output_flags(void) { return arg_all * OUTPUT_SHOW_ALL | @@ -86,34 +86,8 @@ static OutputFlags get_output_flags(void) { !arg_quiet * OUTPUT_WARN_CUTOFF; } -typedef struct MachineInfo { - const char *name; - const char *class; - const char *service; - char *os; - char *version_id; -} MachineInfo; - -static int compare_machine_info(const void *a, const void *b) { - const MachineInfo *x = a, *y = b; - - return strcmp(x->name, y->name); -} - -static void clean_machine_info(MachineInfo *machines, size_t n_machines) { - size_t i; - - if (!machines || n_machines == 0) - return; - - for (i = 0; i < n_machines; i++) { - free(machines[i].os); - free(machines[i].version_id); - } - free(machines); -} - static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) { + _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 *k, *v, *iter, **query_res = NULL; size_t count = 0, awaited_args = 0; @@ -134,9 +108,10 @@ static int call_get_os_release(sd_bus *bus, const char *method, const char *name "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", method, - NULL, &reply, "s", name); + &error, + &reply, "s", name); if (r < 0) - return r; + return log_debug_errno(r, "Failed to call '%s()': %s", method, bus_error_message(&error, r)); r = sd_bus_message_enter_container(reply, 'a', "{ss}"); if (r < 0) @@ -179,16 +154,127 @@ static int call_get_os_release(sd_bus *bus, const char *method, const char *name return 0; } -static int list_machines(int argc, char *argv[], void *userdata) { +static int call_get_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2, int n_addr, char **ret) { - size_t max_name = STRLEN("MACHINE"), max_class = STRLEN("CLASS"), - max_service = STRLEN("SERVICE"), max_os = STRLEN("OS"), max_version_id = STRLEN("VERSION"); _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *prefix = NULL; - MachineInfo *machines = NULL; - const char *name, *class, *service, *object; - size_t n_machines = 0, n_allocated = 0, j; + _cleanup_free_ char *addresses = NULL; + bool truncate = false; + unsigned n = 0; + int r; + + assert(bus); + assert(name); + assert(prefix); + assert(prefix2); + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachineAddresses", + NULL, + &reply, + "s", name); + if (r < 0) + return log_debug_errno(r, "Could not get addresses: %s", bus_error_message(&error, r)); + + addresses = strdup(prefix); + if (!addresses) + return log_oom(); + prefix = ""; + + r = sd_bus_message_enter_container(reply, 'a', "(iay)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { + int family; + const void *a; + size_t sz; + char buf_ifi[DECIMAL_STR_MAX(int) + 2], buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; + + r = sd_bus_message_read(reply, "i", &family); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_array(reply, 'y', &a, &sz); + if (r < 0) + return bus_log_parse_error(r); + + if (n_addr != 0) { + if (family == AF_INET6 && ifi > 0) + xsprintf(buf_ifi, "%%%i", ifi); + else + strcpy(buf_ifi, ""); + + if (!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL)) + return log_oom(); + } else + truncate = true; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + prefix = prefix2; + + if (n_addr > 0) + n_addr --; + + n++; + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + if (truncate) { + + if (!strextend(&addresses, special_glyph(ELLIPSIS), NULL)) + return -ENOMEM; + + } + + *ret = TAKE_PTR(addresses); + return (int) n; +} + +static int show_table(Table *table, const char *word) { + int r; + + assert(table); + assert(word); + + if (table_get_rows(table) > 1) { + r = table_set_sort(table, (size_t) 0, (size_t) -1); + if (r < 0) + return log_error_errno(r, "Failed to sort table: %m"); + + table_set_header(table, arg_legend); + + r = table_print(table, NULL); + if (r < 0) + return log_error_errno(r, "Failed to show table: %m"); + } + + if (arg_legend) { + if (table_get_rows(table) > 1) + printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word); + else + printf("No %s.\n", word); + } + + return 0; +} + +static int list_machines(int argc, char *argv[], void *userdata) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(table_unrefp) Table *table = NULL; sd_bus *bus = userdata; int r; @@ -204,149 +290,73 @@ static int list_machines(int argc, char *argv[], void *userdata) { &error, &reply, NULL); - if (r < 0) { - log_error("Could not get machines: %s", bus_error_message(&error, -r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not get machines: %s", bus_error_message(&error, r)); + + table = table_new("MACHINE", "CLASS", "SERVICE", "OS", "VERSION", "ADDRESSES"); + if (!table) + return log_oom(); r = sd_bus_message_enter_container(reply, 'a', "(ssso)"); if (r < 0) return bus_log_parse_error(r); - while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) { - size_t l; + + for (;;) { + _cleanup_free_ char *os = NULL, *version_id = NULL, *addresses = NULL; + const char *name, *class, *service, *object; + + r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; if (name[0] == '.' && !arg_all) continue; - if (!GREEDY_REALLOC0(machines, n_allocated, n_machines + 1)) { - r = log_oom(); - goto out; - } - - machines[n_machines].name = name; - machines[n_machines].class = class; - machines[n_machines].service = service; - (void) call_get_os_release( bus, "GetMachineOSRelease", name, "ID\0" "VERSION_ID\0", - &machines[n_machines].os, - &machines[n_machines].version_id); + &os, + &version_id); - l = strlen(name); - if (l > max_name) - max_name = l; + (void) call_get_addresses( + bus, + name, + 0, + "", + "", + arg_addrs, + &addresses); - l = strlen(class); - if (l > max_class) - max_class = l; - - l = strlen(service); - if (l > max_service) - max_service = l; - - l = machines[n_machines].os ? strlen(machines[n_machines].os) : 1; - if (l > max_os) - max_os = l; - - l = machines[n_machines].version_id ? strlen(machines[n_machines].version_id) : 1; - if (l > max_version_id) - max_version_id = l; - - n_machines++; - } - if (r < 0) { - r = bus_log_parse_error(r); - goto out; + r = table_add_many(table, + TABLE_STRING, name, + TABLE_STRING, class, + TABLE_STRING, strdash_if_empty(service), + TABLE_STRING, strdash_if_empty(os), + TABLE_STRING, strdash_if_empty(version_id), + TABLE_STRING, strdash_if_empty(addresses)); + if (r < 0) + return log_error_errno(r, "Failed to add table row: %m"); } r = sd_bus_message_exit_container(reply); - if (r < 0) { - r = bus_log_parse_error(r); - goto out; - } + if (r < 0) + return bus_log_parse_error(r); - qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info); - - /* Allocate for prefix max characters for all fields + spaces between them + STRLEN(",\n") */ - r = asprintf(&prefix, "%-*s", - (int) (max_name + - max_class + - max_service + - max_os + - max_version_id + 5 + STRLEN(",\n")), - ",\n"); - if (r < 0) { - r = log_oom(); - goto out; - } - - if (arg_legend && n_machines > 0) - printf("%-*s %-*s %-*s %-*s %-*s %s\n", - (int) max_name, "MACHINE", - (int) max_class, "CLASS", - (int) max_service, "SERVICE", - (int) max_os, "OS", - (int) max_version_id, "VERSION", - "ADDRESSES"); - - for (j = 0; j < n_machines; j++) { - printf("%-*s %-*s %-*s %-*s %-*s ", - (int) max_name, machines[j].name, - (int) max_class, machines[j].class, - (int) max_service, strdash_if_empty(machines[j].service), - (int) max_os, strdash_if_empty(machines[j].os), - (int) max_version_id, strdash_if_empty(machines[j].version_id)); - - r = print_addresses(bus, machines[j].name, 0, "", prefix, arg_addrs); - if (r <= 0) /* error or no addresses defined? */ - fputs("-\n", stdout); - else - fputc('\n', stdout); - } - - if (arg_legend) { - if (n_machines > 0) - printf("\n%zu machines listed.\n", n_machines); - else - printf("No machines.\n"); - } - - r = 0; -out: - clean_machine_info(machines, n_machines); - return r; -} - -typedef struct ImageInfo { - const char *name; - const char *type; - bool read_only; - usec_t crtime; - usec_t mtime; - uint64_t size; -} ImageInfo; - -static int compare_image_info(const void *a, const void *b) { - const ImageInfo *x = a, *y = b; - - return strcmp(x->name, y->name); + return show_table(table, "machines"); } static int list_images(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - size_t max_name = STRLEN("NAME"), max_type = STRLEN("TYPE"), max_size = STRLEN("USAGE"), max_crtime = STRLEN("CREATED"), max_mtime = STRLEN("MODIFIED"); _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ ImageInfo *images = NULL; - size_t n_images = 0, n_allocated = 0, j; - const char *name, *type, *object; + _cleanup_(table_unrefp) Table *table = NULL; sd_bus *bus = userdata; - uint64_t crtime, mtime, size; - int read_only, r; + int r; assert(bus); @@ -359,99 +369,66 @@ static int list_images(int argc, char *argv[], void *userdata) { "ListImages", &error, &reply, - ""); - if (r < 0) { - log_error("Could not get images: %s", bus_error_message(&error, -r)); - return r; - } + NULL); + if (r < 0) + return log_error_errno(r, "Could not get images: %s", bus_error_message(&error, r)); + + table = table_new("NAME", "TYPE", "RO", "USAGE", "CREATED", "MODIFIED"); + if (!table) + return log_oom(); + + (void) table_set_align_percent(table, TABLE_HEADER_CELL(3), 100); r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)"); if (r < 0) return bus_log_parse_error(r); - while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) { - char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)]; - size_t l; + for (;;) { + const char *name, *type, *object; + uint64_t crtime, mtime, size; + TableCell *cell; + bool ro_bool; + int ro_int; + + r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &ro_int, &crtime, &mtime, &size, &object); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; if (name[0] == '.' && !arg_all) continue; - if (!GREEDY_REALLOC(images, n_allocated, n_images + 1)) - return log_oom(); + r = table_add_many(table, + TABLE_STRING, name, + TABLE_STRING, type); + if (r < 0) + return log_error_errno(r, "Failed to add table row: %m"); - images[n_images].name = name; - images[n_images].type = type; - images[n_images].read_only = read_only; - images[n_images].crtime = crtime; - images[n_images].mtime = mtime; - images[n_images].size = size; + ro_bool = ro_int; + r = table_add_cell(table, &cell, TABLE_BOOLEAN, &ro_bool); + if (r < 0) + return log_error_errno(r, "Failed to add table cell: %m"); - l = strlen(name); - if (l > max_name) - max_name = l; - - l = strlen(type); - if (l > max_type) - max_type = l; - - if (crtime != 0) { - l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime))); - if (l > max_crtime) - max_crtime = l; + if (ro_bool) { + r = table_set_color(table, cell, ansi_highlight_red()); + if (r < 0) + return log_error_errno(r, "Failed to set table cell color: %m"); } - if (mtime != 0) { - l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime))); - if (l > max_mtime) - max_mtime = l; - } - - if (size != (uint64_t) -1) { - l = strlen(strna(format_bytes(buf, sizeof(buf), size))); - if (l > max_size) - max_size = l; - } - - n_images++; + r = table_add_many(table, + TABLE_SIZE, size, + TABLE_TIMESTAMP, crtime, + TABLE_TIMESTAMP, mtime); + if (r < 0) + return log_error_errno(r, "Failed to add table row: %m"); } - if (r < 0) - return bus_log_parse_error(r); r = sd_bus_message_exit_container(reply); if (r < 0) return bus_log_parse_error(r); - qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info); - - if (arg_legend && n_images > 0) - printf("%-*s %-*s %-3s %-*s %-*s %-*s\n", - (int) max_name, "NAME", - (int) max_type, "TYPE", - "RO", - (int) max_size, "USAGE", - (int) max_crtime, "CREATED", - (int) max_mtime, "MODIFIED"); - - for (j = 0; j < n_images; j++) { - char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX]; - - printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n", - (int) max_name, images[j].name, - (int) max_type, images[j].type, - images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_normal() : "", - (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)), - (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)), - (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime))); - } - - if (arg_legend) { - if (n_images > 0) - printf("\n%zu images listed.\n", n_images); - else - printf("No images.\n"); - } - - return 0; + return show_table(table, "images"); } static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { @@ -495,85 +472,17 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { } static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2, int n_addr) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *addresses = NULL; - bool truncate = false; - unsigned n = 0; + _cleanup_free_ char *s = NULL; int r; - assert(bus); - assert(name); - assert(prefix); - assert(prefix2); - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineAddresses", - NULL, - &reply, - "s", name); + r = call_get_addresses(bus, name, ifi, prefix, prefix2, n_addr, &s); if (r < 0) return r; - addresses = strdup(prefix); - if (!addresses) - return log_oom(); - prefix = ""; + if (r > 0) + fputs(s, stdout); - r = sd_bus_message_enter_container(reply, 'a', "(iay)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { - int family; - const void *a; - size_t sz; - char buf_ifi[DECIMAL_STR_MAX(int) + 2], buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; - - r = sd_bus_message_read(reply, "i", &family); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_read_array(reply, 'y', &a, &sz); - if (r < 0) - return bus_log_parse_error(r); - - if (n_addr != 0) { - if (family == AF_INET6 && ifi > 0) - xsprintf(buf_ifi, "%%%i", ifi); - else - strcpy(buf_ifi, ""); - - if (!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL)) - return log_oom(); - } else - truncate = true; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - if (prefix != prefix2) - prefix = prefix2; - - if (n_addr > 0) - n_addr -= 1; - - n++; - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - if (n > 0) - fprintf(stdout, "%s%s", addresses, truncate ? "..." : ""); - - return (int) n; + return r; } static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {