blob: 1a2f2d7153d6a8b4c72133f2a242d8b81ef6878d [file] [log] [blame]
/* 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[] = {
{ "SystemState", "s", NULL, offsetof(struct machine_info, state) },
{ "NJobs", "u", NULL, offsetof(struct machine_info, n_jobs) },
{ "NFailedUnits", "u", NULL, offsetof(struct machine_info, n_failed_units) },
{ "ControlGroup", "s", NULL, offsetof(struct machine_info, control_group) },
{ "UserspaceTimestamp", "t", NULL, offsetof(struct machine_info, timestamp) },
{}
};
void machine_info_clear(struct machine_info *info) {
assert(info);
free(info->name);
free(info->state);
free(info->control_group);
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;
char **i;
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);
(void) table_set_empty_string(table, "-");
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 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;
(void) 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;
}