]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/systemctl/systemctl-list-unit-files.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / systemctl / systemctl-list-unit-files.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "bus-error.h"
4 #include "bus-locator.h"
5 #include "sort-util.h"
6 #include "systemctl-list-unit-files.h"
7 #include "systemctl-util.h"
8 #include "systemctl.h"
9 #include "terminal-util.h"
10
11 static int compare_unit_file_list(const UnitFileList *a, const UnitFileList *b) {
12 const char *d1, *d2;
13
14 d1 = strrchr(a->path, '.');
15 d2 = strrchr(b->path, '.');
16
17 if (d1 && d2) {
18 int r;
19
20 r = strcasecmp(d1, d2);
21 if (r != 0)
22 return r;
23 }
24
25 return strcasecmp(basename(a->path), basename(b->path));
26 }
27
28 static bool output_show_unit_file(const UnitFileList *u, char **states, char **patterns) {
29 assert(u);
30
31 if (!strv_fnmatch_or_empty(patterns, basename(u->path), FNM_NOESCAPE))
32 return false;
33
34 if (!strv_isempty(arg_types)) {
35 const char *dot;
36
37 dot = strrchr(u->path, '.');
38 if (!dot)
39 return false;
40
41 if (!strv_find(arg_types, dot+1))
42 return false;
43 }
44
45 if (!strv_isempty(states) &&
46 !strv_find(states, unit_file_state_to_string(u->state)))
47 return false;
48
49 return true;
50 }
51
52 static int output_unit_file_list(const UnitFileList *units, unsigned c) {
53 _cleanup_(table_unrefp) Table *table = NULL;
54 _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {};
55 int r;
56
57 table = table_new("unit file", "state", "vendor preset");
58 if (!table)
59 return log_oom();
60
61 table_set_header(table, !arg_no_legend);
62 if (arg_full)
63 table_set_width(table, 0);
64
65 (void) table_set_empty_string(table, "-");
66
67 for (const UnitFileList *u = units; u < units + c; u++) {
68 const char *on_underline = NULL, *on_unit_color = NULL, *id;
69 bool underline;
70
71 underline = u + 1 < units + c &&
72 !streq(unit_type_suffix(u->path), unit_type_suffix((u + 1)->path));
73
74 if (underline)
75 on_underline = ansi_underline();
76
77 if (IN_SET(u->state,
78 UNIT_FILE_MASKED,
79 UNIT_FILE_MASKED_RUNTIME,
80 UNIT_FILE_DISABLED,
81 UNIT_FILE_BAD))
82 on_unit_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
83 else if (IN_SET(u->state,
84 UNIT_FILE_ENABLED,
85 UNIT_FILE_ALIAS))
86 on_unit_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
87 else
88 on_unit_color = on_underline;
89
90 id = basename(u->path);
91
92 r = table_add_many(table,
93 TABLE_STRING, id,
94 TABLE_SET_BOTH_COLORS, strempty(on_underline),
95 TABLE_STRING, unit_file_state_to_string(u->state),
96 TABLE_SET_BOTH_COLORS, strempty(on_unit_color));
97 if (r < 0)
98 return table_log_add_error(r);
99
100 if (show_preset_for_state(u->state)) {
101 const char *unit_preset_str, *on_preset_color;
102
103 r = unit_file_query_preset(arg_scope, arg_root, id, &presets);
104 if (r < 0) {
105 unit_preset_str = "n/a";
106 on_preset_color = underline ? on_underline : ansi_normal();
107 } else if (r == 0) {
108 unit_preset_str = "disabled";
109 on_preset_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
110 } else {
111 unit_preset_str = "enabled";
112 on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
113 }
114
115 r = table_add_many(table,
116 TABLE_STRING, unit_preset_str,
117 TABLE_SET_BOTH_COLORS, strempty(on_preset_color));
118 } else
119 r = table_add_many(table,
120 TABLE_EMPTY,
121 TABLE_SET_BOTH_COLORS, underline ? ansi_grey_underline() : ansi_grey());
122 if (r < 0)
123 return table_log_add_error(r);
124 }
125
126 r = output_table(table);
127 if (r < 0)
128 return r;
129
130 if (!arg_no_legend)
131 printf("\n%u unit files listed.\n", c);
132
133 return 0;
134 }
135
136 int list_unit_files(int argc, char *argv[], void *userdata) {
137 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
138 _cleanup_free_ UnitFileList *units = NULL;
139 UnitFileList *unit;
140 size_t size = 0;
141 unsigned c = 0;
142 const char *state;
143 char *path;
144 int r;
145 bool fallback = false;
146
147 if (install_client_side()) {
148 Hashmap *h;
149 UnitFileList *u;
150 unsigned n_units;
151
152 h = hashmap_new(&string_hash_ops);
153 if (!h)
154 return log_oom();
155
156 r = unit_file_get_list(arg_scope, arg_root, h, arg_states, strv_skip(argv, 1));
157 if (r < 0) {
158 unit_file_list_free(h);
159 return log_error_errno(r, "Failed to get unit file list: %m");
160 }
161
162 n_units = hashmap_size(h);
163
164 units = new(UnitFileList, n_units ?: 1); /* avoid malloc(0) */
165 if (!units) {
166 unit_file_list_free(h);
167 return log_oom();
168 }
169
170 HASHMAP_FOREACH(u, h) {
171 if (!output_show_unit_file(u, NULL, NULL))
172 continue;
173
174 units[c++] = *u;
175 free(u);
176 }
177
178 assert(c <= n_units);
179 hashmap_free(h);
180
181 r = 0;
182 } else {
183 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
184 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
185 sd_bus *bus;
186
187 r = acquire_bus(BUS_MANAGER, &bus);
188 if (r < 0)
189 return r;
190
191 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitFilesByPatterns");
192 if (r < 0)
193 return bus_log_create_error(r);
194
195 r = sd_bus_message_append_strv(m, arg_states);
196 if (r < 0)
197 return bus_log_create_error(r);
198
199 if (arg_with_dependencies) {
200 _cleanup_strv_free_ char **names_with_deps = NULL;
201
202 r = append_unit_dependencies(bus, strv_skip(argv, 1), &names_with_deps);
203 if (r < 0)
204 return log_error_errno(r, "Failed to append unit dependencies: %m");
205
206 r = sd_bus_message_append_strv(m, names_with_deps);
207 if (r < 0)
208 return bus_log_create_error(r);
209 } else {
210 r = sd_bus_message_append_strv(m, strv_skip(argv, 1));
211 if (r < 0)
212 return bus_log_create_error(r);
213 }
214
215 r = sd_bus_call(bus, m, 0, &error, &reply);
216 if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
217 /* Fallback to legacy ListUnitFiles method */
218 fallback = true;
219 log_debug_errno(r, "Failed to list unit files: %s Falling back to ListUnitsFiles method.", bus_error_message(&error, r));
220 m = sd_bus_message_unref(m);
221 sd_bus_error_free(&error);
222
223 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitFiles");
224 if (r < 0)
225 return bus_log_create_error(r);
226
227 r = sd_bus_call(bus, m, 0, &error, &reply);
228 }
229 if (r < 0)
230 return log_error_errno(r, "Failed to list unit files: %s", bus_error_message(&error, r));
231
232 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ss)");
233 if (r < 0)
234 return bus_log_parse_error(r);
235
236 while ((r = sd_bus_message_read(reply, "(ss)", &path, &state)) > 0) {
237
238 if (!GREEDY_REALLOC(units, size, c + 1))
239 return log_oom();
240
241 units[c] = (struct UnitFileList) {
242 path,
243 unit_file_state_from_string(state)
244 };
245
246 if (output_show_unit_file(&units[c],
247 fallback ? arg_states : NULL,
248 fallback ? strv_skip(argv, 1) : NULL))
249 c++;
250
251 }
252 if (r < 0)
253 return bus_log_parse_error(r);
254
255 r = sd_bus_message_exit_container(reply);
256 if (r < 0)
257 return bus_log_parse_error(r);
258 }
259
260 (void) pager_open(arg_pager_flags);
261
262 typesafe_qsort(units, c, compare_unit_file_list);
263 r = output_unit_file_list(units, c);
264 if (r < 0)
265 return r;
266
267 if (install_client_side())
268 for (unit = units; unit < units + c; unit++)
269 free(unit->path);
270
271 if (c == 0)
272 return -ENOENT;
273
274 return 0;
275 }