diff options
Diffstat (limited to 'src/systemctl/systemctl-list-machines.c')
-rw-r--r-- | src/systemctl/systemctl-list-machines.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/systemctl/systemctl-list-machines.c b/src/systemctl/systemctl-list-machines.c new file mode 100644 index 0000000..4407d25 --- /dev/null +++ b/src/systemctl/systemctl-list-machines.c @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <unistd.h> + +#include "sd-login.h" + +#include "bus-map-properties.h" +#include "hostname-util.h" +#include "locale-util.h" +#include "memory-util.h" +#include "sort-util.h" +#include "systemctl-list-machines.h" +#include "systemctl-util.h" +#include "systemctl.h" +#include "terminal-util.h" + +const struct bus_properties_map machine_info_property_map[] = { + /* Might good to keep same order here as in bus_manager_vtable[], server side */ + { "Version", "s", NULL, offsetof(struct machine_info, version) }, + { "Tainted", "s", NULL, offsetof(struct machine_info, tainted) }, + { "UserspaceTimestamp", "t", NULL, offsetof(struct machine_info, timestamp) }, + { "NNames", "u", NULL, offsetof(struct machine_info, n_names) }, + { "NFailedUnits", "u", NULL, offsetof(struct machine_info, n_failed_units) }, + { "NJobs", "u", NULL, offsetof(struct machine_info, n_jobs) }, + { "ControlGroup", "s", NULL, offsetof(struct machine_info, control_group) }, + { "SystemState", "s", NULL, offsetof(struct machine_info, state) }, + {} +}; + +void machine_info_clear(struct machine_info *info) { + assert(info); + + free(info->name); + free(info->version); + free(info->tainted); + free(info->control_group); + free(info->state); + zero(*info); +} + +static void free_machines_list(struct machine_info *machine_infos, int n) { + if (!machine_infos) + return; + + for (int i = 0; i < n; i++) + machine_info_clear(&machine_infos[i]); + + free(machine_infos); +} + +static int compare_machine_info(const struct machine_info *a, const struct machine_info *b) { + int r; + + r = CMP(b->is_host, a->is_host); + if (r != 0) + return r; + + return strcasecmp(a->name, b->name); +} + +static int get_machine_properties(sd_bus *bus, struct machine_info *mi) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *container = NULL; + int r; + + assert(mi); + + if (!bus) { + r = sd_bus_open_system_machine(&container, mi->name); + if (r < 0) + return r; + + bus = container; + } + + r = bus_map_all_properties( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + machine_info_property_map, + BUS_MAP_STRDUP, + NULL, + NULL, + mi); + if (r < 0) + return r; + + return 0; +} + +static bool output_show_machine(const char *name, char **patterns) { + return strv_fnmatch_or_empty(patterns, name, FNM_NOESCAPE); +} + +static int get_machine_list( + sd_bus *bus, + struct machine_info **_machine_infos, + char **patterns) { + + struct machine_info *machine_infos = NULL; + _cleanup_strv_free_ char **m = NULL; + _cleanup_free_ char *hn = NULL; + int c = 0, r; + + hn = gethostname_malloc(); + if (!hn) + return log_oom(); + + if (output_show_machine(hn, patterns)) { + if (!GREEDY_REALLOC0(machine_infos, c+1)) + return log_oom(); + + machine_infos[c].is_host = true; + machine_infos[c].name = TAKE_PTR(hn); + + (void) get_machine_properties(bus, &machine_infos[c]); + c++; + } + + r = sd_get_machine_names(&m); + if (r < 0) + return log_error_errno(r, "Failed to get machine list: %m"); + + STRV_FOREACH(i, m) { + _cleanup_free_ char *class = NULL; + + if (!output_show_machine(*i, patterns)) + continue; + + sd_machine_get_class(*i, &class); + if (!streq_ptr(class, "container")) + continue; + + if (!GREEDY_REALLOC0(machine_infos, c+1)) { + free_machines_list(machine_infos, c); + return log_oom(); + } + + machine_infos[c].is_host = false; + machine_infos[c].name = strdup(*i); + if (!machine_infos[c].name) { + free_machines_list(machine_infos, c); + return log_oom(); + } + + (void) get_machine_properties(NULL, &machine_infos[c]); + c++; + } + + *_machine_infos = machine_infos; + return c; +} + +static int output_machines_list(struct machine_info *machine_infos, unsigned n) { + _cleanup_(table_unrefp) Table *table = NULL; + bool state_missing = false; + int r; + + assert(machine_infos || n == 0); + + table = table_new("", "name", "state", "failed", "jobs"); + if (!table) + return log_oom(); + + table_set_header(table, arg_legend != 0); + if (arg_plain) { + /* Hide the 'glyph' column when --plain is requested */ + r = table_hide_column_from_display(table, 0); + if (r < 0) + return log_error_errno(r, "Failed to hide column: %m"); + } + if (arg_full) + table_set_width(table, 0); + + table_set_ersatz_string(table, TABLE_ERSATZ_DASH); + + for (struct machine_info *m = machine_infos; m < machine_infos + n; m++) { + _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(); + circle = true; + } else if (!streq_ptr(m->state, "running")) { + on_state = ansi_highlight_yellow(); + circle = true; + } + + if (m->n_failed_units > 0) + on_failed = ansi_highlight_red(); + else + on_failed = ""; + + if (!m->state) + state_missing = true; + + if (m->is_host) + 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 = output_table(table); + if (r < 0) + return r; + + if (arg_legend != 0) { + 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; +} + +int verb_list_machines(int argc, char *argv[], void *userdata) { + struct machine_info *machine_infos = NULL; + sd_bus *bus; + int r, rc; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + r = get_machine_list(bus, &machine_infos, strv_skip(argv, 1)); + if (r < 0) + return r; + + pager_open(arg_pager_flags); + + typesafe_qsort(machine_infos, r, compare_machine_info); + rc = output_machines_list(machine_infos, r); + free_machines_list(machine_infos, r); + + return rc; +} |