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