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
compare_unit_file_list(const UnitFileList * a,const UnitFileList * b)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
output_show_unit_file(const UnitFileList * u,char ** states,char ** patterns)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_contains(arg_types, dot+1))
42 return false;
43 }
44
45 if (!strv_isempty(states) &&
46 !strv_contains(states, unit_file_state_to_string(u->state)))
47 return false;
48
49 return true;
50 }
51
output_unit_file_list(const UnitFileList * units,unsigned c)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_legend != 0);
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_legend != 0)
131 printf("\n%u unit files listed.\n", c);
132
133 return 0;
134 }
135
verb_list_unit_files(int argc,char * argv[],void * userdata)136 int verb_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 unsigned c = 0;
140 const char *state;
141 char *path;
142 int r;
143 bool fallback = false;
144
145 if (install_client_side()) {
146 Hashmap *h;
147 UnitFileList *u;
148 unsigned n_units;
149
150 h = hashmap_new(&string_hash_ops);
151 if (!h)
152 return log_oom();
153
154 r = unit_file_get_list(arg_scope, arg_root, h, arg_states, strv_skip(argv, 1));
155 if (r < 0) {
156 unit_file_list_free(h);
157 return log_error_errno(r, "Failed to get unit file list: %m");
158 }
159
160 n_units = hashmap_size(h);
161
162 units = new(UnitFileList, n_units ?: 1); /* avoid malloc(0) */
163 if (!units) {
164 unit_file_list_free(h);
165 return log_oom();
166 }
167
168 HASHMAP_FOREACH(u, h) {
169 if (!output_show_unit_file(u, NULL, NULL))
170 continue;
171
172 units[c++] = *u;
173 free(u);
174 }
175
176 assert(c <= n_units);
177 hashmap_free(h);
178
179 r = 0;
180 } else {
181 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
182 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
183 sd_bus *bus;
184
185 r = acquire_bus(BUS_MANAGER, &bus);
186 if (r < 0)
187 return r;
188
189 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitFilesByPatterns");
190 if (r < 0)
191 return bus_log_create_error(r);
192
193 r = sd_bus_message_append_strv(m, arg_states);
194 if (r < 0)
195 return bus_log_create_error(r);
196
197 if (arg_with_dependencies) {
198 _cleanup_strv_free_ char **names_with_deps = NULL;
199
200 r = append_unit_dependencies(bus, strv_skip(argv, 1), &names_with_deps);
201 if (r < 0)
202 return log_error_errno(r, "Failed to append unit dependencies: %m");
203
204 r = sd_bus_message_append_strv(m, names_with_deps);
205 if (r < 0)
206 return bus_log_create_error(r);
207 } else {
208 r = sd_bus_message_append_strv(m, strv_skip(argv, 1));
209 if (r < 0)
210 return bus_log_create_error(r);
211 }
212
213 r = sd_bus_call(bus, m, 0, &error, &reply);
214 if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
215 /* Fallback to legacy ListUnitFiles method */
216 fallback = true;
217 log_debug_errno(r, "Failed to list unit files: %s Falling back to ListUnitsFiles method.", bus_error_message(&error, r));
218 m = sd_bus_message_unref(m);
219 sd_bus_error_free(&error);
220
221 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitFiles");
222 if (r < 0)
223 return bus_log_create_error(r);
224
225 r = sd_bus_call(bus, m, 0, &error, &reply);
226 }
227 if (r < 0)
228 return log_error_errno(r, "Failed to list unit files: %s", bus_error_message(&error, r));
229
230 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ss)");
231 if (r < 0)
232 return bus_log_parse_error(r);
233
234 while ((r = sd_bus_message_read(reply, "(ss)", &path, &state)) > 0) {
235
236 if (!GREEDY_REALLOC(units, c + 1))
237 return log_oom();
238
239 units[c] = (struct UnitFileList) {
240 path,
241 unit_file_state_from_string(state)
242 };
243
244 if (output_show_unit_file(&units[c],
245 fallback ? arg_states : NULL,
246 fallback ? strv_skip(argv, 1) : NULL))
247 c++;
248
249 }
250 if (r < 0)
251 return bus_log_parse_error(r);
252
253 r = sd_bus_message_exit_container(reply);
254 if (r < 0)
255 return bus_log_parse_error(r);
256 }
257
258 pager_open(arg_pager_flags);
259
260 typesafe_qsort(units, c, compare_unit_file_list);
261 r = output_unit_file_list(units, c);
262 if (r < 0)
263 return r;
264
265 if (install_client_side())
266 for (UnitFileList *unit = units; unit < units + c; unit++)
267 free(unit->path);
268
269 if (c == 0)
270 return -ENOENT;
271
272 return 0;
273 }
274