]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/systemctl/systemctl-list-machines.c
Merge pull request #22791 from keszybz/bootctl-invert-order
[thirdparty/systemd.git] / src / systemctl / systemctl-list-machines.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "sd-login.h"
6
7 #include "bus-map-properties.h"
8 #include "hostname-util.h"
9 #include "locale-util.h"
10 #include "memory-util.h"
11 #include "sort-util.h"
12 #include "systemctl-list-machines.h"
13 #include "systemctl-util.h"
14 #include "systemctl.h"
15 #include "terminal-util.h"
16
17 const struct bus_properties_map machine_info_property_map[] = {
18 { "SystemState", "s", NULL, offsetof(struct machine_info, state) },
19 { "NJobs", "u", NULL, offsetof(struct machine_info, n_jobs) },
20 { "NFailedUnits", "u", NULL, offsetof(struct machine_info, n_failed_units) },
21 { "ControlGroup", "s", NULL, offsetof(struct machine_info, control_group) },
22 { "UserspaceTimestamp", "t", NULL, offsetof(struct machine_info, timestamp) },
23 {}
24 };
25
26 void machine_info_clear(struct machine_info *info) {
27 assert(info);
28
29 free(info->name);
30 free(info->state);
31 free(info->control_group);
32 zero(*info);
33 }
34
35 static void free_machines_list(struct machine_info *machine_infos, int n) {
36 if (!machine_infos)
37 return;
38
39 for (int i = 0; i < n; i++)
40 machine_info_clear(&machine_infos[i]);
41
42 free(machine_infos);
43 }
44
45 static int compare_machine_info(const struct machine_info *a, const struct machine_info *b) {
46 int r;
47
48 r = CMP(b->is_host, a->is_host);
49 if (r != 0)
50 return r;
51
52 return strcasecmp(a->name, b->name);
53 }
54
55 static int get_machine_properties(sd_bus *bus, struct machine_info *mi) {
56 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *container = NULL;
57 int r;
58
59 assert(mi);
60
61 if (!bus) {
62 r = sd_bus_open_system_machine(&container, mi->name);
63 if (r < 0)
64 return r;
65
66 bus = container;
67 }
68
69 r = bus_map_all_properties(
70 bus,
71 "org.freedesktop.systemd1",
72 "/org/freedesktop/systemd1",
73 machine_info_property_map,
74 BUS_MAP_STRDUP,
75 NULL,
76 NULL,
77 mi);
78 if (r < 0)
79 return r;
80
81 return 0;
82 }
83
84 static bool output_show_machine(const char *name, char **patterns) {
85 return strv_fnmatch_or_empty(patterns, name, FNM_NOESCAPE);
86 }
87
88 static int get_machine_list(
89 sd_bus *bus,
90 struct machine_info **_machine_infos,
91 char **patterns) {
92
93 struct machine_info *machine_infos = NULL;
94 _cleanup_strv_free_ char **m = NULL;
95 _cleanup_free_ char *hn = NULL;
96 int c = 0, r;
97
98 hn = gethostname_malloc();
99 if (!hn)
100 return log_oom();
101
102 if (output_show_machine(hn, patterns)) {
103 if (!GREEDY_REALLOC0(machine_infos, c+1))
104 return log_oom();
105
106 machine_infos[c].is_host = true;
107 machine_infos[c].name = TAKE_PTR(hn);
108
109 (void) get_machine_properties(bus, &machine_infos[c]);
110 c++;
111 }
112
113 r = sd_get_machine_names(&m);
114 if (r < 0)
115 return log_error_errno(r, "Failed to get machine list: %m");
116
117 STRV_FOREACH(i, m) {
118 _cleanup_free_ char *class = NULL;
119
120 if (!output_show_machine(*i, patterns))
121 continue;
122
123 sd_machine_get_class(*i, &class);
124 if (!streq_ptr(class, "container"))
125 continue;
126
127 if (!GREEDY_REALLOC0(machine_infos, c+1)) {
128 free_machines_list(machine_infos, c);
129 return log_oom();
130 }
131
132 machine_infos[c].is_host = false;
133 machine_infos[c].name = strdup(*i);
134 if (!machine_infos[c].name) {
135 free_machines_list(machine_infos, c);
136 return log_oom();
137 }
138
139 (void) get_machine_properties(NULL, &machine_infos[c]);
140 c++;
141 }
142
143 *_machine_infos = machine_infos;
144 return c;
145 }
146
147 static int output_machines_list(struct machine_info *machine_infos, unsigned n) {
148 _cleanup_(table_unrefp) Table *table = NULL;
149 bool state_missing = false;
150 int r;
151
152 assert(machine_infos || n == 0);
153
154 table = table_new("", "name", "state", "failed", "jobs");
155 if (!table)
156 return log_oom();
157
158 table_set_header(table, arg_legend != 0);
159 if (arg_plain) {
160 /* Hide the 'glyph' column when --plain is requested */
161 r = table_hide_column_from_display(table, 0);
162 if (r < 0)
163 return log_error_errno(r, "Failed to hide column: %m");
164 }
165 if (arg_full)
166 table_set_width(table, 0);
167
168 (void) table_set_empty_string(table, "-");
169
170 for (struct machine_info *m = machine_infos; m < machine_infos + n; m++) {
171 _cleanup_free_ char *mname = NULL;
172 const char *on_state = "", *on_failed = "";
173 bool circle = false;
174
175 if (streq_ptr(m->state, "degraded")) {
176 on_state = ansi_highlight_red();
177 circle = true;
178 } else if (!streq_ptr(m->state, "running")) {
179 on_state = ansi_highlight_yellow();
180 circle = true;
181 }
182
183 if (m->n_failed_units > 0)
184 on_failed = ansi_highlight_red();
185 else
186 on_failed = "";
187
188 if (!m->state)
189 state_missing = true;
190
191 if (m->is_host)
192 mname = strjoin(strna(m->name), " (host)");
193
194 r = table_add_many(table,
195 TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
196 TABLE_SET_COLOR, on_state,
197 TABLE_STRING, m->is_host ? mname : strna(m->name),
198 TABLE_STRING, strna(m->state),
199 TABLE_SET_COLOR, on_state,
200 TABLE_UINT32, m->n_failed_units,
201 TABLE_SET_COLOR, on_failed,
202 TABLE_UINT32, m->n_jobs);
203 if (r < 0)
204 return table_log_add_error(r);
205 }
206
207 r = output_table(table);
208 if (r < 0)
209 return r;
210
211 if (arg_legend != 0) {
212 printf("\n");
213 if (state_missing && geteuid() != 0)
214 printf("Notice: some information only available to privileged users was not shown.\n");
215 printf("%u machines listed.\n", n);
216 }
217
218 return 0;
219 }
220
221 int verb_list_machines(int argc, char *argv[], void *userdata) {
222 struct machine_info *machine_infos = NULL;
223 sd_bus *bus;
224 int r, rc;
225
226 r = acquire_bus(BUS_MANAGER, &bus);
227 if (r < 0)
228 return r;
229
230 r = get_machine_list(bus, &machine_infos, strv_skip(argv, 1));
231 if (r < 0)
232 return r;
233
234 pager_open(arg_pager_flags);
235
236 typesafe_qsort(machine_infos, r, compare_machine_info);
237 rc = output_machines_list(machine_infos, r);
238 free_machines_list(machine_infos, r);
239
240 return rc;
241 }