summaryrefslogtreecommitdiffstats
path: root/src/systemctl/systemctl-list-machines.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/systemctl/systemctl-list-machines.c')
-rw-r--r--src/systemctl/systemctl-list-machines.c247
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;
+}