]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/systemctl/systemctl-list-machines.c
Merge pull request #18007 from fw-strlen/ipv6_masq_and_dnat
[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 size_t sz = 0;
97 char **i;
98 int c = 0, r;
99
100 hn = gethostname_malloc();
101 if (!hn)
102 return log_oom();
103
104 if (output_show_machine(hn, patterns)) {
105 if (!GREEDY_REALLOC0(machine_infos, sz, c+1))
106 return log_oom();
107
108 machine_infos[c].is_host = true;
109 machine_infos[c].name = TAKE_PTR(hn);
110
111 (void) get_machine_properties(bus, &machine_infos[c]);
112 c++;
113 }
114
115 r = sd_get_machine_names(&m);
116 if (r < 0)
117 return log_error_errno(r, "Failed to get machine list: %m");
118
119 STRV_FOREACH(i, m) {
120 _cleanup_free_ char *class = NULL;
121
122 if (!output_show_machine(*i, patterns))
123 continue;
124
125 sd_machine_get_class(*i, &class);
126 if (!streq_ptr(class, "container"))
127 continue;
128
129 if (!GREEDY_REALLOC0(machine_infos, sz, c+1)) {
130 free_machines_list(machine_infos, c);
131 return log_oom();
132 }
133
134 machine_infos[c].is_host = false;
135 machine_infos[c].name = strdup(*i);
136 if (!machine_infos[c].name) {
137 free_machines_list(machine_infos, c);
138 return log_oom();
139 }
140
141 (void) get_machine_properties(NULL, &machine_infos[c]);
142 c++;
143 }
144
145 *_machine_infos = machine_infos;
146 return c;
147 }
148
149 static int output_machines_list(struct machine_info *machine_infos, unsigned n) {
150 _cleanup_(table_unrefp) Table *table = NULL;
151 bool state_missing = false;
152 int r;
153
154 assert(machine_infos || n == 0);
155
156 table = table_new("", "name", "state", "failed", "jobs");
157 if (!table)
158 return log_oom();
159
160 table_set_header(table, !arg_no_legend);
161 if (arg_plain) {
162 /* Hide the 'glyph' column when --plain is requested */
163 r = table_hide_column_from_display(table, 0);
164 if (r < 0)
165 return log_error_errno(r, "Failed to hide column: %m");
166 }
167 if (arg_full)
168 table_set_width(table, 0);
169
170 (void) table_set_empty_string(table, "-");
171
172 for (struct machine_info *m = machine_infos; m < machine_infos + n; m++) {
173 _cleanup_free_ char *mname = NULL;
174 const char *on_state = "", *on_failed = "";
175 bool circle = false;
176
177 if (streq_ptr(m->state, "degraded")) {
178 on_state = ansi_highlight_red();
179 circle = true;
180 } else if (!streq_ptr(m->state, "running")) {
181 on_state = ansi_highlight_yellow();
182 circle = true;
183 }
184
185 if (m->n_failed_units > 0)
186 on_failed = ansi_highlight_red();
187 else
188 on_failed = "";
189
190 if (!m->state)
191 state_missing = true;
192
193 if (m->is_host)
194 mname = strjoin(strna(m->name), " (host)");
195
196 r = table_add_many(table,
197 TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
198 TABLE_SET_COLOR, on_state,
199 TABLE_STRING, m->is_host ? mname : strna(m->name),
200 TABLE_STRING, strna(m->state),
201 TABLE_SET_COLOR, on_state,
202 TABLE_UINT32, m->n_failed_units,
203 TABLE_SET_COLOR, on_failed,
204 TABLE_UINT32, m->n_jobs);
205 if (r < 0)
206 return table_log_add_error(r);
207 }
208
209 r = output_table(table);
210 if (r < 0)
211 return r;
212
213 if (!arg_no_legend) {
214 printf("\n");
215 if (state_missing && geteuid() != 0)
216 printf("Notice: some information only available to privileged users was not shown.\n");
217 printf("%u machines listed.\n", n);
218 }
219
220 return 0;
221 }
222
223 int list_machines(int argc, char *argv[], void *userdata) {
224 struct machine_info *machine_infos = NULL;
225 sd_bus *bus;
226 int r, rc;
227
228 r = acquire_bus(BUS_MANAGER, &bus);
229 if (r < 0)
230 return r;
231
232 r = get_machine_list(bus, &machine_infos, strv_skip(argv, 1));
233 if (r < 0)
234 return r;
235
236 (void) pager_open(arg_pager_flags);
237
238 typesafe_qsort(machine_infos, r, compare_machine_info);
239 rc = output_machines_list(machine_infos, r);
240 free_machines_list(machine_infos, r);
241
242 return rc;
243 }