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