Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : : /***
3 : : Copyright © 2013 Simon Peeters
4 : : ***/
5 : :
6 : : #include <getopt.h>
7 : : #include <inttypes.h>
8 : : #include <locale.h>
9 : : #include <stdio.h>
10 : : #include <stdlib.h>
11 : : #include <unistd.h>
12 : :
13 : : #include "sd-bus.h"
14 : :
15 : : #include "alloc-util.h"
16 : : #include "analyze-condition.h"
17 : : #include "analyze-security.h"
18 : : #include "analyze-verify.h"
19 : : #include "build.h"
20 : : #include "bus-error.h"
21 : : #include "bus-unit-util.h"
22 : : #include "bus-util.h"
23 : : #include "calendarspec.h"
24 : : #include "conf-files.h"
25 : : #include "copy.h"
26 : : #include "def.h"
27 : : #include "exit-status.h"
28 : : #include "fd-util.h"
29 : : #include "fileio.h"
30 : : #include "format-table.h"
31 : : #include "glob-util.h"
32 : : #include "hashmap.h"
33 : : #include "locale-util.h"
34 : : #include "log.h"
35 : : #include "main-func.h"
36 : : #include "nulstr-util.h"
37 : : #include "pager.h"
38 : : #include "parse-util.h"
39 : : #include "path-util.h"
40 : : #include "pretty-print.h"
41 : : #if HAVE_SECCOMP
42 : : # include "seccomp-util.h"
43 : : #endif
44 : : #include "sort-util.h"
45 : : #include "special.h"
46 : : #include "strv.h"
47 : : #include "strxcpyx.h"
48 : : #include "terminal-util.h"
49 : : #include "time-util.h"
50 : : #include "unit-name.h"
51 : : #include "util.h"
52 : : #include "verbs.h"
53 : :
54 : : #define SCALE_X (0.1 / 1000.0) /* pixels per us */
55 : : #define SCALE_Y (20.0)
56 : :
57 : : #define svg(...) printf(__VA_ARGS__)
58 : :
59 : : #define svg_bar(class, x1, x2, y) \
60 : : svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
61 : : (class), \
62 : : SCALE_X * (x1), SCALE_Y * (y), \
63 : : SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
64 : :
65 : : #define svg_text(b, x, y, format, ...) \
66 : : do { \
67 : : svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
68 : : svg(format, ## __VA_ARGS__); \
69 : : svg("</text>\n"); \
70 : : } while (false)
71 : :
72 : : static enum dot {
73 : : DEP_ALL,
74 : : DEP_ORDER,
75 : : DEP_REQUIRE
76 : : } arg_dot = DEP_ALL;
77 : : static char **arg_dot_from_patterns = NULL;
78 : : static char **arg_dot_to_patterns = NULL;
79 : : static usec_t arg_fuzz = 0;
80 : : static PagerFlags arg_pager_flags = 0;
81 : : static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
82 : : static const char *arg_host = NULL;
83 : : static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
84 : : static bool arg_man = true;
85 : : static bool arg_generators = false;
86 : : static const char *arg_root = NULL;
87 : : static unsigned arg_iterations = 1;
88 : :
89 : 16 : STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
90 : 16 : STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
91 : :
92 : : struct boot_times {
93 : : usec_t firmware_time;
94 : : usec_t loader_time;
95 : : usec_t kernel_time;
96 : : usec_t kernel_done_time;
97 : : usec_t initrd_time;
98 : : usec_t userspace_time;
99 : : usec_t finish_time;
100 : : usec_t security_start_time;
101 : : usec_t security_finish_time;
102 : : usec_t generators_start_time;
103 : : usec_t generators_finish_time;
104 : : usec_t unitsload_start_time;
105 : : usec_t unitsload_finish_time;
106 : : usec_t initrd_security_start_time;
107 : : usec_t initrd_security_finish_time;
108 : : usec_t initrd_generators_start_time;
109 : : usec_t initrd_generators_finish_time;
110 : : usec_t initrd_unitsload_start_time;
111 : : usec_t initrd_unitsload_finish_time;
112 : :
113 : : /*
114 : : * If we're analyzing the user instance, all timestamps will be offset
115 : : * by its own start-up timestamp, which may be arbitrarily big.
116 : : * With "plot", this causes arbitrarily wide output SVG files which almost
117 : : * completely consist of empty space. Thus we cancel out this offset.
118 : : *
119 : : * This offset is subtracted from times above by acquire_boot_times(),
120 : : * but it still needs to be subtracted from unit-specific timestamps
121 : : * (so it is stored here for reference).
122 : : */
123 : : usec_t reverse_offset;
124 : : };
125 : :
126 : : struct unit_times {
127 : : bool has_data;
128 : : char *name;
129 : : usec_t activating;
130 : : usec_t activated;
131 : : usec_t deactivated;
132 : : usec_t deactivating;
133 : : usec_t time;
134 : : };
135 : :
136 : : struct host_info {
137 : : char *hostname;
138 : : char *kernel_name;
139 : : char *kernel_release;
140 : : char *kernel_version;
141 : : char *os_pretty_name;
142 : : char *virtualization;
143 : : char *architecture;
144 : : };
145 : :
146 : 0 : static int acquire_bus(sd_bus **bus, bool *use_full_bus) {
147 : 0 : bool user = arg_scope != UNIT_FILE_SYSTEM;
148 : : int r;
149 : :
150 [ # # # # ]: 0 : if (use_full_bus && *use_full_bus) {
151 : 0 : r = bus_connect_transport(arg_transport, arg_host, user, bus);
152 [ # # # # ]: 0 : if (IN_SET(r, 0, -EHOSTDOWN))
153 : 0 : return r;
154 : :
155 : 0 : *use_full_bus = false;
156 : : }
157 : :
158 : 0 : return bus_connect_transport_systemd(arg_transport, arg_host, user, bus);
159 : : }
160 : :
161 : 0 : static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
162 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
163 : : int r;
164 : :
165 [ # # ]: 0 : assert(bus);
166 [ # # ]: 0 : assert(path);
167 [ # # ]: 0 : assert(interface);
168 [ # # ]: 0 : assert(property);
169 [ # # ]: 0 : assert(val);
170 : :
171 : 0 : r = sd_bus_get_property_trivial(
172 : : bus,
173 : : "org.freedesktop.systemd1",
174 : : path,
175 : : interface,
176 : : property,
177 : : &error,
178 : : 't', val);
179 : :
180 [ # # ]: 0 : if (r < 0)
181 [ # # ]: 0 : return log_error_errno(r, "Failed to parse reply: %s", bus_error_message(&error, r));
182 : :
183 : 0 : return 0;
184 : : }
185 : :
186 : 0 : static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
187 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
188 : : int r;
189 : :
190 [ # # ]: 0 : assert(bus);
191 [ # # ]: 0 : assert(path);
192 [ # # ]: 0 : assert(property);
193 [ # # ]: 0 : assert(strv);
194 : :
195 : 0 : r = sd_bus_get_property_strv(
196 : : bus,
197 : : "org.freedesktop.systemd1",
198 : : path,
199 : : "org.freedesktop.systemd1.Unit",
200 : : property,
201 : : &error,
202 : : strv);
203 [ # # ]: 0 : if (r < 0)
204 [ # # ]: 0 : return log_error_errno(r, "Failed to get unit property %s: %s", property, bus_error_message(&error, r));
205 : :
206 : 0 : return 0;
207 : : }
208 : :
209 : 0 : static int compare_unit_start(const struct unit_times *a, const struct unit_times *b) {
210 [ # # ]: 0 : return CMP(a->activating, b->activating);
211 : : }
212 : :
213 : 0 : static void unit_times_free(struct unit_times *t) {
214 : : struct unit_times *p;
215 : :
216 [ # # ]: 0 : for (p = t; p->has_data; p++)
217 : 0 : free(p->name);
218 : 0 : free(t);
219 : 0 : }
220 : :
221 [ # # ]: 0 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct unit_times *, unit_times_free);
222 : :
223 : 0 : static void subtract_timestamp(usec_t *a, usec_t b) {
224 [ # # ]: 0 : assert(a);
225 : :
226 [ # # ]: 0 : if (*a > 0) {
227 [ # # ]: 0 : assert(*a >= b);
228 : 0 : *a -= b;
229 : : }
230 : 0 : }
231 : :
232 : 0 : static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
233 : : static const struct bus_properties_map property_map[] = {
234 : : { "FirmwareTimestampMonotonic", "t", NULL, offsetof(struct boot_times, firmware_time) },
235 : : { "LoaderTimestampMonotonic", "t", NULL, offsetof(struct boot_times, loader_time) },
236 : : { "KernelTimestamp", "t", NULL, offsetof(struct boot_times, kernel_time) },
237 : : { "InitRDTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_time) },
238 : : { "UserspaceTimestampMonotonic", "t", NULL, offsetof(struct boot_times, userspace_time) },
239 : : { "FinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, finish_time) },
240 : : { "SecurityStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, security_start_time) },
241 : : { "SecurityFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, security_finish_time) },
242 : : { "GeneratorsStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, generators_start_time) },
243 : : { "GeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, generators_finish_time) },
244 : : { "UnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, unitsload_start_time) },
245 : : { "UnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, unitsload_finish_time) },
246 : : { "InitRDSecurityStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_security_start_time) },
247 : : { "InitRDSecurityFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_security_finish_time) },
248 : : { "InitRDGeneratorsStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_generators_start_time) },
249 : : { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_generators_finish_time) },
250 : : { "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_unitsload_start_time) },
251 : : { "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_unitsload_finish_time) },
252 : : {},
253 : : };
254 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
255 : : static struct boot_times times;
256 : : static bool cached = false;
257 : : int r;
258 : :
259 [ # # ]: 0 : if (cached)
260 : 0 : goto finish;
261 : :
262 : : assert_cc(sizeof(usec_t) == sizeof(uint64_t));
263 : :
264 : 0 : r = bus_map_all_properties(
265 : : bus,
266 : : "org.freedesktop.systemd1",
267 : : "/org/freedesktop/systemd1",
268 : : property_map,
269 : : BUS_MAP_STRDUP,
270 : : &error,
271 : : NULL,
272 : : ×);
273 [ # # ]: 0 : if (r < 0)
274 [ # # ]: 0 : return log_error_errno(r, "Failed to get timestamp properties: %s", bus_error_message(&error, r));
275 : :
276 [ # # ]: 0 : if (times.finish_time <= 0)
277 [ # # # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINPROGRESS),
278 : : "Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n"
279 : : "Please try again later.\n"
280 : : "Hint: Use 'systemctl%s list-jobs' to see active jobs",
281 : : times.finish_time,
282 : : arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
283 : :
284 [ # # # # ]: 0 : if (arg_scope == UNIT_FILE_SYSTEM && times.security_start_time > 0) {
285 : : /* security_start_time is set when systemd is not running under container environment. */
286 [ # # ]: 0 : if (times.initrd_time > 0)
287 : 0 : times.kernel_done_time = times.initrd_time;
288 : : else
289 : 0 : times.kernel_done_time = times.userspace_time;
290 : : } else {
291 : : /*
292 : : * User-instance-specific or container-system-specific timestamps processing
293 : : * (see comment to reverse_offset in struct boot_times).
294 : : */
295 : 0 : times.reverse_offset = times.userspace_time;
296 : :
297 : 0 : times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time =
298 : 0 : times.userspace_time = times.security_start_time = times.security_finish_time = 0;
299 : :
300 : 0 : subtract_timestamp(×.finish_time, times.reverse_offset);
301 : :
302 : 0 : subtract_timestamp(×.generators_start_time, times.reverse_offset);
303 : 0 : subtract_timestamp(×.generators_finish_time, times.reverse_offset);
304 : :
305 : 0 : subtract_timestamp(×.unitsload_start_time, times.reverse_offset);
306 : 0 : subtract_timestamp(×.unitsload_finish_time, times.reverse_offset);
307 : : }
308 : :
309 : 0 : cached = true;
310 : :
311 : 0 : finish:
312 : 0 : *bt = ×
313 : 0 : return 0;
314 : : }
315 : :
316 : 0 : static void free_host_info(struct host_info *hi) {
317 [ # # ]: 0 : if (!hi)
318 : 0 : return;
319 : :
320 : 0 : free(hi->hostname);
321 : 0 : free(hi->kernel_name);
322 : 0 : free(hi->kernel_release);
323 : 0 : free(hi->kernel_version);
324 : 0 : free(hi->os_pretty_name);
325 : 0 : free(hi->virtualization);
326 : 0 : free(hi->architecture);
327 : 0 : free(hi);
328 : : }
329 : :
330 [ # # ]: 0 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info *, free_host_info);
331 : :
332 : 0 : static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
333 : : static const struct bus_properties_map property_map[] = {
334 : : { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(struct unit_times, activating) },
335 : : { "ActiveEnterTimestampMonotonic", "t", NULL, offsetof(struct unit_times, activated) },
336 : : { "ActiveExitTimestampMonotonic", "t", NULL, offsetof(struct unit_times, deactivating) },
337 : : { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(struct unit_times, deactivated) },
338 : : {},
339 : : };
340 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
341 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
342 : 0 : _cleanup_(unit_times_freep) struct unit_times *unit_times = NULL;
343 : 0 : struct boot_times *boot_times = NULL;
344 : 0 : size_t allocated = 0, c = 0;
345 : : UnitInfo u;
346 : : int r;
347 : :
348 : 0 : r = acquire_boot_times(bus, &boot_times);
349 [ # # ]: 0 : if (r < 0)
350 : 0 : return r;
351 : :
352 : 0 : r = sd_bus_call_method(
353 : : bus,
354 : : "org.freedesktop.systemd1",
355 : : "/org/freedesktop/systemd1",
356 : : "org.freedesktop.systemd1.Manager",
357 : : "ListUnits",
358 : : &error, &reply,
359 : : NULL);
360 [ # # ]: 0 : if (r < 0)
361 [ # # ]: 0 : return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
362 : :
363 : 0 : r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
364 [ # # ]: 0 : if (r < 0)
365 [ # # ]: 0 : return bus_log_parse_error(r);
366 : :
367 [ # # ]: 0 : while ((r = bus_parse_unit_info(reply, &u)) > 0) {
368 : : struct unit_times *t;
369 : :
370 [ # # ]: 0 : if (!GREEDY_REALLOC(unit_times, allocated, c + 2))
371 : 0 : return log_oom();
372 : :
373 : 0 : unit_times[c + 1].has_data = false;
374 : 0 : t = &unit_times[c];
375 : 0 : t->name = NULL;
376 : :
377 : : assert_cc(sizeof(usec_t) == sizeof(uint64_t));
378 : :
379 : 0 : r = bus_map_all_properties(
380 : : bus,
381 : : "org.freedesktop.systemd1",
382 : : u.unit_path,
383 : : property_map,
384 : : BUS_MAP_STRDUP,
385 : : &error,
386 : : NULL,
387 : : t);
388 [ # # ]: 0 : if (r < 0)
389 [ # # ]: 0 : return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s",
390 : : u.id, bus_error_message(&error, r));
391 : :
392 : 0 : subtract_timestamp(&t->activating, boot_times->reverse_offset);
393 : 0 : subtract_timestamp(&t->activated, boot_times->reverse_offset);
394 : 0 : subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
395 : 0 : subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
396 : :
397 [ # # ]: 0 : if (t->activated >= t->activating)
398 : 0 : t->time = t->activated - t->activating;
399 [ # # ]: 0 : else if (t->deactivated >= t->activating)
400 : 0 : t->time = t->deactivated - t->activating;
401 : : else
402 : 0 : t->time = 0;
403 : :
404 [ # # ]: 0 : if (t->activating == 0)
405 : 0 : continue;
406 : :
407 : 0 : t->name = strdup(u.id);
408 [ # # ]: 0 : if (!t->name)
409 : 0 : return log_oom();
410 : :
411 : 0 : t->has_data = true;
412 : 0 : c++;
413 : : }
414 [ # # ]: 0 : if (r < 0)
415 [ # # ]: 0 : return bus_log_parse_error(r);
416 : :
417 : 0 : *out = TAKE_PTR(unit_times);
418 : 0 : return c;
419 : : }
420 : :
421 : 0 : static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
422 : : static const struct bus_properties_map hostname_map[] = {
423 : : { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
424 : : { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
425 : : { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
426 : : { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
427 : : { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
428 : : {}
429 : : };
430 : :
431 : : static const struct bus_properties_map manager_map[] = {
432 : : { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
433 : : { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
434 : : {}
435 : : };
436 : :
437 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
438 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL;
439 : 0 : _cleanup_(free_host_infop) struct host_info *host;
440 : : int r;
441 : :
442 : 0 : host = new0(struct host_info, 1);
443 [ # # ]: 0 : if (!host)
444 : 0 : return log_oom();
445 : :
446 [ # # ]: 0 : if (arg_scope != UNIT_FILE_SYSTEM) {
447 : 0 : r = bus_connect_transport(arg_transport, arg_host, false, &system_bus);
448 [ # # ]: 0 : if (r < 0) {
449 [ # # ]: 0 : log_debug_errno(r, "Failed to connect to system bus, ignoring: %m");
450 : 0 : goto manager;
451 : : }
452 : : }
453 : :
454 : 0 : r = bus_map_all_properties(
455 [ # # ]: 0 : system_bus ?: bus,
456 : : "org.freedesktop.hostname1",
457 : : "/org/freedesktop/hostname1",
458 : : hostname_map,
459 : : BUS_MAP_STRDUP,
460 : : &error,
461 : : NULL,
462 : : host);
463 [ # # ]: 0 : if (r < 0) {
464 [ # # ]: 0 : log_debug_errno(r, "Failed to get host information from systemd-hostnamed, ignoring: %s",
465 : : bus_error_message(&error, r));
466 : 0 : sd_bus_error_free(&error);
467 : : }
468 : :
469 : 0 : manager:
470 : 0 : r = bus_map_all_properties(
471 : : bus,
472 : : "org.freedesktop.systemd1",
473 : : "/org/freedesktop/systemd1",
474 : : manager_map,
475 : : BUS_MAP_STRDUP,
476 : : &error,
477 : : NULL,
478 : : host);
479 [ # # ]: 0 : if (r < 0)
480 [ # # ]: 0 : return log_error_errno(r, "Failed to get host information from systemd: %s",
481 : : bus_error_message(&error, r));
482 : :
483 : 0 : *hi = TAKE_PTR(host);
484 : 0 : return 0;
485 : : }
486 : :
487 : 0 : static int pretty_boot_time(sd_bus *bus, char **_buf) {
488 : : char ts[FORMAT_TIMESPAN_MAX];
489 : : struct boot_times *t;
490 : : static char buf[4096];
491 : : size_t size;
492 : : char *ptr;
493 : : int r;
494 : 0 : usec_t activated_time = USEC_INFINITY;
495 : 0 : _cleanup_free_ char *path = NULL, *unit_id = NULL;
496 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
497 : :
498 : 0 : r = acquire_boot_times(bus, &t);
499 [ # # ]: 0 : if (r < 0)
500 : 0 : return r;
501 : :
502 : 0 : path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
503 [ # # ]: 0 : if (!path)
504 : 0 : return log_oom();
505 : :
506 : 0 : r = sd_bus_get_property_string(
507 : : bus,
508 : : "org.freedesktop.systemd1",
509 : : path,
510 : : "org.freedesktop.systemd1.Unit",
511 : : "Id",
512 : : &error,
513 : : &unit_id);
514 [ # # ]: 0 : if (r < 0) {
515 [ # # ]: 0 : log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
516 : 0 : unit_id = NULL;
517 : : }
518 : :
519 : 0 : r = bus_get_uint64_property(bus, path,
520 : : "org.freedesktop.systemd1.Unit",
521 : : "ActiveEnterTimestampMonotonic",
522 : : &activated_time);
523 [ # # ]: 0 : if (r < 0) {
524 [ # # ]: 0 : log_info_errno(r, "Could not get time to reach default.target, ignoring: %m");
525 : 0 : activated_time = USEC_INFINITY;
526 : : }
527 : :
528 : 0 : ptr = buf;
529 : 0 : size = sizeof(buf);
530 : :
531 : 0 : size = strpcpyf(&ptr, size, "Startup finished in ");
532 [ # # ]: 0 : if (t->firmware_time > 0)
533 : 0 : size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
534 [ # # ]: 0 : if (t->loader_time > 0)
535 : 0 : size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
536 [ # # ]: 0 : if (t->kernel_done_time > 0)
537 : 0 : size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
538 [ # # ]: 0 : if (t->initrd_time > 0)
539 : 0 : size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
540 : :
541 : 0 : size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
542 [ # # ]: 0 : if (t->kernel_done_time > 0)
543 : 0 : strpcpyf(&ptr, size, "= %s ", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
544 : :
545 [ # # # # ]: 0 : if (unit_id && timestamp_is_set(activated_time)) {
546 [ # # ]: 0 : usec_t base = t->userspace_time > 0 ? t->userspace_time : t->reverse_offset;
547 : :
548 : 0 : size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id,
549 : : format_timespan(ts, sizeof(ts), activated_time - base, USEC_PER_MSEC));
550 [ # # # # ]: 0 : } else if (unit_id && activated_time == 0)
551 : 0 : size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id);
552 [ # # # # ]: 0 : else if (unit_id && activated_time == USEC_INFINITY)
553 : 0 : size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.", unit_id);
554 [ # # ]: 0 : else if (!unit_id)
555 : 0 : size = strpcpyf(&ptr, size, "\ncould not find default.target");
556 : :
557 : 0 : ptr = strdup(buf);
558 [ # # ]: 0 : if (!ptr)
559 : 0 : return log_oom();
560 : :
561 : 0 : *_buf = ptr;
562 : 0 : return 0;
563 : : }
564 : :
565 : 0 : static void svg_graph_box(double height, double begin, double end) {
566 : : long long i;
567 : :
568 : : /* outside box, fill */
569 : 0 : svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
570 : : SCALE_X * (end - begin),
571 : : SCALE_Y * height);
572 : :
573 [ # # ]: 0 : for (i = ((long long) (begin / 100000)) * 100000; i <= end; i += 100000) {
574 : : /* lines for each second */
575 [ # # ]: 0 : if (i % 5000000 == 0)
576 : 0 : svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
577 : : " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
578 : : SCALE_X * i,
579 : : SCALE_X * i,
580 : : SCALE_Y * height,
581 : : SCALE_X * i,
582 : : -5.0,
583 : : 0.000001 * i);
584 [ # # ]: 0 : else if (i % 1000000 == 0)
585 : 0 : svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
586 : : " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
587 : : SCALE_X * i,
588 : : SCALE_X * i,
589 : : SCALE_Y * height,
590 : : SCALE_X * i,
591 : : -5.0,
592 : : 0.000001 * i);
593 : : else
594 : 0 : svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
595 : : SCALE_X * i,
596 : : SCALE_X * i,
597 : : SCALE_Y * height);
598 : : }
599 : 0 : }
600 : :
601 : 0 : static int plot_unit_times(struct unit_times *u, double width, int y) {
602 : : char ts[FORMAT_TIMESPAN_MAX];
603 : : bool b;
604 : :
605 [ # # ]: 0 : if (!u->name)
606 : 0 : return 0;
607 : :
608 : 0 : svg_bar("activating", u->activating, u->activated, y);
609 : 0 : svg_bar("active", u->activated, u->deactivating, y);
610 : 0 : svg_bar("deactivating", u->deactivating, u->deactivated, y);
611 : :
612 : : /* place the text on the left if we have passed the half of the svg width */
613 : 0 : b = u->activating * SCALE_X < width / 2;
614 [ # # ]: 0 : if (u->time)
615 [ # # # # ]: 0 : svg_text(b, u->activating, y, "%s (%s)",
616 : : u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
617 : : else
618 [ # # # # ]: 0 : svg_text(b, u->activating, y, "%s", u->name);
619 : :
620 : 0 : return 1;
621 : : }
622 : :
623 : 0 : static int analyze_plot(int argc, char *argv[], void *userdata) {
624 : 0 : _cleanup_(free_host_infop) struct host_info *host = NULL;
625 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
626 : 0 : _cleanup_(unit_times_freep) struct unit_times *times = NULL;
627 : 0 : _cleanup_free_ char *pretty_times = NULL;
628 : 0 : bool use_full_bus = arg_scope == UNIT_FILE_SYSTEM;
629 : : struct boot_times *boot;
630 : : struct unit_times *u;
631 : 0 : int n, m = 1, y = 0, r;
632 : : double width;
633 : :
634 : 0 : r = acquire_bus(&bus, &use_full_bus);
635 [ # # ]: 0 : if (r < 0)
636 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
637 : :
638 : 0 : n = acquire_boot_times(bus, &boot);
639 [ # # ]: 0 : if (n < 0)
640 : 0 : return n;
641 : :
642 : 0 : n = pretty_boot_time(bus, &pretty_times);
643 [ # # ]: 0 : if (n < 0)
644 : 0 : return n;
645 : :
646 [ # # # # ]: 0 : if (use_full_bus || arg_scope != UNIT_FILE_SYSTEM) {
647 : 0 : n = acquire_host_info(bus, &host);
648 [ # # ]: 0 : if (n < 0)
649 : 0 : return n;
650 : : }
651 : :
652 : 0 : n = acquire_time_data(bus, ×);
653 [ # # ]: 0 : if (n <= 0)
654 : 0 : return n;
655 : :
656 : 0 : typesafe_qsort(times, n, compare_unit_start);
657 : :
658 : 0 : width = SCALE_X * (boot->firmware_time + boot->finish_time);
659 [ # # ]: 0 : if (width < 800.0)
660 : 0 : width = 800.0;
661 : :
662 [ # # ]: 0 : if (boot->firmware_time > boot->loader_time)
663 : 0 : m++;
664 [ # # ]: 0 : if (boot->loader_time > 0) {
665 : 0 : m++;
666 [ # # ]: 0 : if (width < 1000.0)
667 : 0 : width = 1000.0;
668 : : }
669 [ # # ]: 0 : if (boot->initrd_time > 0)
670 : 0 : m++;
671 [ # # ]: 0 : if (boot->kernel_done_time > 0)
672 : 0 : m++;
673 : :
674 [ # # ]: 0 : for (u = times; u->has_data; u++) {
675 : : double text_start, text_width;
676 : :
677 [ # # ]: 0 : if (u->activating > boot->finish_time) {
678 : 0 : u->name = mfree(u->name);
679 : 0 : continue;
680 : : }
681 : :
682 : : /* If the text cannot fit on the left side then
683 : : * increase the svg width so it fits on the right.
684 : : * TODO: calculate the text width more accurately */
685 : 0 : text_width = 8.0 * strlen(u->name);
686 : 0 : text_start = (boot->firmware_time + u->activating) * SCALE_X;
687 [ # # # # ]: 0 : if (text_width > text_start && text_width + text_start > width)
688 : 0 : width = text_width + text_start;
689 : :
690 [ # # ]: 0 : if (u->deactivated > u->activating &&
691 [ # # ]: 0 : u->deactivated <= boot->finish_time &&
692 [ # # # # ]: 0 : u->activated == 0 && u->deactivating == 0)
693 : 0 : u->activated = u->deactivating = u->deactivated;
694 [ # # # # ]: 0 : if (u->activated < u->activating || u->activated > boot->finish_time)
695 : 0 : u->activated = boot->finish_time;
696 [ # # # # ]: 0 : if (u->deactivating < u->activated || u->deactivating > boot->finish_time)
697 : 0 : u->deactivating = boot->finish_time;
698 [ # # # # ]: 0 : if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
699 : 0 : u->deactivated = boot->finish_time;
700 : 0 : m++;
701 : : }
702 : :
703 : 0 : svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
704 : : "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
705 : : "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
706 : :
707 : 0 : svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
708 : : "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
709 : : 80.0 + width, 150.0 + (m * SCALE_Y) +
710 : : 5 * SCALE_Y /* legend */);
711 : :
712 : : /* write some basic info as a comment, including some help */
713 : 0 : svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
714 : : "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
715 : : "<!-- that render these files properly but much slower are ImageMagick, -->\n"
716 : : "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
717 : : "<!-- point your browser to this file. -->\n\n"
718 : : "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", GIT_VERSION);
719 : :
720 : : /* style sheet */
721 : 0 : svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
722 : : " rect { stroke-width: 1; stroke-opacity: 0; }\n"
723 : : " rect.background { fill: rgb(255,255,255); }\n"
724 : : " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
725 : : " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
726 : : " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
727 : : " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
728 : : " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
729 : : " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
730 : : " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
731 : : " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
732 : : " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
733 : : " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
734 : : " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
735 : : " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
736 : : " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
737 : : "// line.sec1 { }\n"
738 : : " line.sec5 { stroke-width: 2; }\n"
739 : : " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
740 : : " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
741 : : " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
742 : : " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
743 : : " text.sec { font-size: 10px; }\n"
744 : : " ]]>\n </style>\n</defs>\n\n");
745 : :
746 : 0 : svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
747 : 0 : svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
748 [ # # ]: 0 : if (host)
749 [ # # ]: 0 : svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
750 : : isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
751 : : strempty(host->hostname),
752 : : strempty(host->kernel_name),
753 : : strempty(host->kernel_release),
754 : : strempty(host->kernel_version),
755 : : strempty(host->architecture),
756 : : strempty(host->virtualization));
757 : :
758 : 0 : svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
759 : 0 : svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
760 : :
761 [ # # ]: 0 : if (boot->firmware_time > 0) {
762 : 0 : svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
763 : 0 : svg_text(true, -(double) boot->firmware_time, y, "firmware");
764 : 0 : y++;
765 : : }
766 [ # # ]: 0 : if (boot->loader_time > 0) {
767 : 0 : svg_bar("loader", -(double) boot->loader_time, 0, y);
768 : 0 : svg_text(true, -(double) boot->loader_time, y, "loader");
769 : 0 : y++;
770 : : }
771 [ # # ]: 0 : if (boot->kernel_done_time > 0) {
772 : 0 : svg_bar("kernel", 0, boot->kernel_done_time, y);
773 : 0 : svg_text(true, 0, y, "kernel");
774 : 0 : y++;
775 : : }
776 [ # # ]: 0 : if (boot->initrd_time > 0) {
777 : 0 : svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
778 [ # # ]: 0 : if (boot->initrd_security_start_time < boot->initrd_security_finish_time)
779 : 0 : svg_bar("security", boot->initrd_security_start_time, boot->initrd_security_finish_time, y);
780 [ # # ]: 0 : if (boot->initrd_generators_start_time < boot->initrd_generators_finish_time)
781 : 0 : svg_bar("generators", boot->initrd_generators_start_time, boot->initrd_generators_finish_time, y);
782 [ # # ]: 0 : if (boot->initrd_unitsload_start_time < boot->initrd_unitsload_finish_time)
783 : 0 : svg_bar("unitsload", boot->initrd_unitsload_start_time, boot->initrd_unitsload_finish_time, y);
784 : 0 : svg_text(true, boot->initrd_time, y, "initrd");
785 : 0 : y++;
786 : : }
787 : :
788 [ # # ]: 0 : for (u = times; u->has_data; u++) {
789 [ # # ]: 0 : if (u->activating >= boot->userspace_time)
790 : 0 : break;
791 : :
792 : 0 : y += plot_unit_times(u, width, y);
793 : : }
794 : :
795 : 0 : svg_bar("active", boot->userspace_time, boot->finish_time, y);
796 [ # # ]: 0 : if (boot->security_start_time > 0)
797 : 0 : svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
798 : 0 : svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
799 : 0 : svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
800 : 0 : svg_text(true, boot->userspace_time, y, "systemd");
801 : 0 : y++;
802 : :
803 [ # # ]: 0 : for (; u->has_data; u++)
804 : 0 : y += plot_unit_times(u, width, y);
805 : :
806 : 0 : svg("</g>\n");
807 : :
808 : : /* Legend */
809 : 0 : svg("<g transform=\"translate(20,100)\">\n");
810 : 0 : y++;
811 : 0 : svg_bar("activating", 0, 300000, y);
812 : 0 : svg_text(true, 400000, y, "Activating");
813 : 0 : y++;
814 : 0 : svg_bar("active", 0, 300000, y);
815 : 0 : svg_text(true, 400000, y, "Active");
816 : 0 : y++;
817 : 0 : svg_bar("deactivating", 0, 300000, y);
818 : 0 : svg_text(true, 400000, y, "Deactivating");
819 : 0 : y++;
820 [ # # ]: 0 : if (boot->security_start_time > 0) {
821 : 0 : svg_bar("security", 0, 300000, y);
822 : 0 : svg_text(true, 400000, y, "Setting up security module");
823 : 0 : y++;
824 : : }
825 : 0 : svg_bar("generators", 0, 300000, y);
826 : 0 : svg_text(true, 400000, y, "Generators");
827 : 0 : y++;
828 : 0 : svg_bar("unitsload", 0, 300000, y);
829 : 0 : svg_text(true, 400000, y, "Loading unit files");
830 : 0 : y++;
831 : :
832 : 0 : svg("</g>\n\n");
833 : :
834 : 0 : svg("</svg>\n");
835 : :
836 : 0 : return 0;
837 : : }
838 : :
839 : 0 : static int list_dependencies_print(
840 : : const char *name,
841 : : unsigned level,
842 : : unsigned branches,
843 : : bool last,
844 : : struct unit_times *times,
845 : : struct boot_times *boot) {
846 : :
847 : : unsigned i;
848 : : char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
849 : :
850 [ # # ]: 0 : for (i = level; i != 0; i--)
851 [ # # ]: 0 : printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
852 : :
853 [ # # ]: 0 : printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH));
854 : :
855 [ # # ]: 0 : if (times) {
856 [ # # ]: 0 : if (times->time > 0)
857 : 0 : printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
858 : 0 : format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
859 : : format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal());
860 [ # # ]: 0 : else if (times->activated > boot->userspace_time)
861 : 0 : printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
862 : : else
863 : 0 : printf("%s", name);
864 : : } else
865 : 0 : printf("%s", name);
866 : 0 : printf("\n");
867 : :
868 : 0 : return 0;
869 : : }
870 : :
871 : 0 : static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
872 : 0 : _cleanup_free_ char *path = NULL;
873 : :
874 [ # # ]: 0 : assert(bus);
875 [ # # ]: 0 : assert(name);
876 [ # # ]: 0 : assert(deps);
877 : :
878 : 0 : path = unit_dbus_path_from_name(name);
879 [ # # ]: 0 : if (!path)
880 : 0 : return -ENOMEM;
881 : :
882 : 0 : return bus_get_unit_property_strv(bus, path, "After", deps);
883 : : }
884 : :
885 : : static Hashmap *unit_times_hashmap;
886 : :
887 : 0 : static int list_dependencies_compare(char *const *a, char *const *b) {
888 : 0 : usec_t usa = 0, usb = 0;
889 : : struct unit_times *times;
890 : :
891 : 0 : times = hashmap_get(unit_times_hashmap, *a);
892 [ # # ]: 0 : if (times)
893 : 0 : usa = times->activated;
894 : 0 : times = hashmap_get(unit_times_hashmap, *b);
895 [ # # ]: 0 : if (times)
896 : 0 : usb = times->activated;
897 : :
898 [ # # ]: 0 : return CMP(usb, usa);
899 : : }
900 : :
901 : 0 : static bool times_in_range(const struct unit_times *times, const struct boot_times *boot) {
902 [ # # # # : 0 : return times && times->activated > 0 && times->activated <= boot->finish_time;
# # ]
903 : : }
904 : :
905 : 0 : static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) {
906 : 0 : _cleanup_strv_free_ char **deps = NULL;
907 : : char **c;
908 : 0 : int r = 0;
909 : 0 : usec_t service_longest = 0;
910 : 0 : int to_print = 0;
911 : : struct unit_times *times;
912 : : struct boot_times *boot;
913 : :
914 [ # # ]: 0 : if (strv_extend(units, name))
915 : 0 : return log_oom();
916 : :
917 : 0 : r = list_dependencies_get_dependencies(bus, name, &deps);
918 [ # # ]: 0 : if (r < 0)
919 : 0 : return r;
920 : :
921 : 0 : typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
922 : :
923 : 0 : r = acquire_boot_times(bus, &boot);
924 [ # # ]: 0 : if (r < 0)
925 : 0 : return r;
926 : :
927 [ # # # # ]: 0 : STRV_FOREACH(c, deps) {
928 : 0 : times = hashmap_get(unit_times_hashmap, *c);
929 [ # # # # ]: 0 : if (times_in_range(times, boot) && times->activated >= service_longest)
930 : 0 : service_longest = times->activated;
931 : : }
932 : :
933 [ # # ]: 0 : if (service_longest == 0)
934 : 0 : return r;
935 : :
936 [ # # # # ]: 0 : STRV_FOREACH(c, deps) {
937 : 0 : times = hashmap_get(unit_times_hashmap, *c);
938 [ # # # # ]: 0 : if (times_in_range(times, boot) && service_longest - times->activated <= arg_fuzz)
939 : 0 : to_print++;
940 : : }
941 : :
942 [ # # ]: 0 : if (!to_print)
943 : 0 : return r;
944 : :
945 [ # # # # ]: 0 : STRV_FOREACH(c, deps) {
946 : 0 : times = hashmap_get(unit_times_hashmap, *c);
947 [ # # # # ]: 0 : if (!times_in_range(times, boot) || service_longest - times->activated > arg_fuzz)
948 : 0 : continue;
949 : :
950 : 0 : to_print--;
951 : :
952 : 0 : r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
953 [ # # ]: 0 : if (r < 0)
954 : 0 : return r;
955 : :
956 [ # # ]: 0 : if (strv_contains(*units, *c)) {
957 [ # # ]: 0 : r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
958 : : true, NULL, boot);
959 [ # # ]: 0 : if (r < 0)
960 : 0 : return r;
961 : 0 : continue;
962 : : }
963 : :
964 [ # # ]: 0 : r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (to_print ? 1 : 0));
965 [ # # ]: 0 : if (r < 0)
966 : 0 : return r;
967 : :
968 [ # # ]: 0 : if (to_print == 0)
969 : 0 : break;
970 : : }
971 : 0 : return 0;
972 : : }
973 : :
974 : 0 : static int list_dependencies(sd_bus *bus, const char *name) {
975 : 0 : _cleanup_strv_free_ char **units = NULL;
976 : : char ts[FORMAT_TIMESPAN_MAX];
977 : : struct unit_times *times;
978 : : int r;
979 : : const char *id;
980 : 0 : _cleanup_free_ char *path = NULL;
981 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
982 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
983 : : struct boot_times *boot;
984 : :
985 [ # # ]: 0 : assert(bus);
986 : :
987 : 0 : path = unit_dbus_path_from_name(name);
988 [ # # ]: 0 : if (!path)
989 : 0 : return -ENOMEM;
990 : :
991 : 0 : r = sd_bus_get_property(
992 : : bus,
993 : : "org.freedesktop.systemd1",
994 : : path,
995 : : "org.freedesktop.systemd1.Unit",
996 : : "Id",
997 : : &error,
998 : : &reply,
999 : : "s");
1000 [ # # ]: 0 : if (r < 0)
1001 [ # # ]: 0 : return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1002 : :
1003 : 0 : r = sd_bus_message_read(reply, "s", &id);
1004 [ # # ]: 0 : if (r < 0)
1005 [ # # ]: 0 : return bus_log_parse_error(r);
1006 : :
1007 : 0 : times = hashmap_get(unit_times_hashmap, id);
1008 : :
1009 : 0 : r = acquire_boot_times(bus, &boot);
1010 [ # # ]: 0 : if (r < 0)
1011 : 0 : return r;
1012 : :
1013 [ # # ]: 0 : if (times) {
1014 [ # # ]: 0 : if (times->time)
1015 : 0 : printf("%s%s +%s%s\n", ansi_highlight_red(), id,
1016 : : format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal());
1017 [ # # ]: 0 : else if (times->activated > boot->userspace_time)
1018 : 0 : printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
1019 : : else
1020 : 0 : printf("%s\n", id);
1021 : : }
1022 : :
1023 : 0 : return list_dependencies_one(bus, name, 0, &units, 0);
1024 : : }
1025 : :
1026 : 0 : static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
1027 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1028 : 0 : _cleanup_(unit_times_freep) struct unit_times *times = NULL;
1029 : : struct unit_times *u;
1030 : : Hashmap *h;
1031 : : int n, r;
1032 : :
1033 : 0 : r = acquire_bus(&bus, NULL);
1034 [ # # ]: 0 : if (r < 0)
1035 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1036 : :
1037 : 0 : n = acquire_time_data(bus, ×);
1038 [ # # ]: 0 : if (n <= 0)
1039 : 0 : return n;
1040 : :
1041 : 0 : h = hashmap_new(&string_hash_ops);
1042 [ # # ]: 0 : if (!h)
1043 : 0 : return log_oom();
1044 : :
1045 [ # # ]: 0 : for (u = times; u->has_data; u++) {
1046 : 0 : r = hashmap_put(h, u->name, u);
1047 [ # # ]: 0 : if (r < 0)
1048 [ # # ]: 0 : return log_error_errno(r, "Failed to add entry to hashmap: %m");
1049 : : }
1050 : 0 : unit_times_hashmap = h;
1051 : :
1052 : 0 : (void) pager_open(arg_pager_flags);
1053 : :
1054 : 0 : puts("The time when unit became active or started is printed after the \"@\" character.\n"
1055 : : "The time the unit took to start is printed after the \"+\" character.\n");
1056 : :
1057 [ # # ]: 0 : if (argc > 1) {
1058 : : char **name;
1059 [ # # # # ]: 0 : STRV_FOREACH(name, strv_skip(argv, 1))
1060 : 0 : list_dependencies(bus, *name);
1061 : : } else
1062 : 0 : list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
1063 : :
1064 : 0 : h = hashmap_free(h);
1065 : 0 : return 0;
1066 : : }
1067 : :
1068 : 0 : static int analyze_blame(int argc, char *argv[], void *userdata) {
1069 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1070 : 0 : _cleanup_(unit_times_freep) struct unit_times *times = NULL;
1071 : 0 : _cleanup_(table_unrefp) Table *table = NULL;
1072 : : struct unit_times *u;
1073 : : TableCell *cell;
1074 : : int n, r;
1075 : :
1076 : 0 : r = acquire_bus(&bus, NULL);
1077 [ # # ]: 0 : if (r < 0)
1078 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1079 : :
1080 : 0 : n = acquire_time_data(bus, ×);
1081 [ # # ]: 0 : if (n <= 0)
1082 : 0 : return n;
1083 : :
1084 : 0 : table = table_new("time", "unit");
1085 [ # # ]: 0 : if (!table)
1086 : 0 : return log_oom();
1087 : :
1088 : 0 : table_set_header(table, false);
1089 : :
1090 [ # # ]: 0 : assert_se(cell = table_get_cell(table, 0, 0));
1091 : 0 : r = table_set_ellipsize_percent(table, cell, 100);
1092 [ # # ]: 0 : if (r < 0)
1093 : 0 : return r;
1094 : :
1095 : 0 : r = table_set_align_percent(table, cell, 100);
1096 [ # # ]: 0 : if (r < 0)
1097 : 0 : return r;
1098 : :
1099 [ # # ]: 0 : assert_se(cell = table_get_cell(table, 0, 1));
1100 : 0 : r = table_set_ellipsize_percent(table, cell, 100);
1101 [ # # ]: 0 : if (r < 0)
1102 : 0 : return r;
1103 : :
1104 : 0 : r = table_set_sort(table, 0, SIZE_MAX);
1105 [ # # ]: 0 : if (r < 0)
1106 : 0 : return r;
1107 : :
1108 : 0 : r = table_set_reverse(table, 0, true);
1109 [ # # ]: 0 : if (r < 0)
1110 : 0 : return r;
1111 : :
1112 [ # # ]: 0 : for (u = times; u->has_data; u++) {
1113 [ # # ]: 0 : if (u->time <= 0)
1114 : 0 : continue;
1115 : :
1116 : 0 : r = table_add_cell(table, NULL, TABLE_TIMESPAN_MSEC, &u->time);
1117 [ # # ]: 0 : if (r < 0)
1118 : 0 : return r;
1119 : :
1120 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, u->name);
1121 [ # # ]: 0 : if (r < 0)
1122 : 0 : return r;
1123 : : }
1124 : :
1125 : 0 : (void) pager_open(arg_pager_flags);
1126 : :
1127 : 0 : return table_print(table, NULL);
1128 : : }
1129 : :
1130 : 0 : static int analyze_time(int argc, char *argv[], void *userdata) {
1131 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1132 : 0 : _cleanup_free_ char *buf = NULL;
1133 : : int r;
1134 : :
1135 : 0 : r = acquire_bus(&bus, NULL);
1136 [ # # ]: 0 : if (r < 0)
1137 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1138 : :
1139 : 0 : r = pretty_boot_time(bus, &buf);
1140 [ # # ]: 0 : if (r < 0)
1141 : 0 : return r;
1142 : :
1143 : 0 : puts(buf);
1144 : 0 : return 0;
1145 : : }
1146 : :
1147 : 0 : static int graph_one_property(
1148 : : sd_bus *bus,
1149 : : const UnitInfo *u,
1150 : : const char *prop,
1151 : : const char *color,
1152 : : char *patterns[],
1153 : : char *from_patterns[],
1154 : : char *to_patterns[]) {
1155 : :
1156 : 0 : _cleanup_strv_free_ char **units = NULL;
1157 : : char **unit;
1158 : : int r;
1159 : : bool match_patterns;
1160 : :
1161 [ # # ]: 0 : assert(u);
1162 [ # # ]: 0 : assert(prop);
1163 [ # # ]: 0 : assert(color);
1164 : :
1165 : 0 : match_patterns = strv_fnmatch(patterns, u->id, 0);
1166 : :
1167 [ # # # # : 0 : if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id, 0))
# # ]
1168 : 0 : return 0;
1169 : :
1170 : 0 : r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
1171 [ # # ]: 0 : if (r < 0)
1172 : 0 : return r;
1173 : :
1174 [ # # # # ]: 0 : STRV_FOREACH(unit, units) {
1175 : : bool match_patterns2;
1176 : :
1177 : 0 : match_patterns2 = strv_fnmatch(patterns, *unit, 0);
1178 : :
1179 [ # # # # : 0 : if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit, 0))
# # ]
1180 : 0 : continue;
1181 : :
1182 [ # # # # : 0 : if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
# # ]
1183 : 0 : continue;
1184 : :
1185 : 0 : printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1186 : : }
1187 : :
1188 : 0 : return 0;
1189 : : }
1190 : :
1191 : 0 : static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
1192 : : int r;
1193 : :
1194 [ # # ]: 0 : assert(bus);
1195 [ # # ]: 0 : assert(u);
1196 : :
1197 [ # # # # ]: 0 : if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
1198 : 0 : r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
1199 [ # # ]: 0 : if (r < 0)
1200 : 0 : return r;
1201 : : }
1202 : :
1203 [ # # # # ]: 0 : if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
1204 : 0 : r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
1205 [ # # ]: 0 : if (r < 0)
1206 : 0 : return r;
1207 : 0 : r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
1208 [ # # ]: 0 : if (r < 0)
1209 : 0 : return r;
1210 : 0 : r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
1211 [ # # ]: 0 : if (r < 0)
1212 : 0 : return r;
1213 : 0 : r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
1214 [ # # ]: 0 : if (r < 0)
1215 : 0 : return r;
1216 : : }
1217 : :
1218 : 0 : return 0;
1219 : : }
1220 : :
1221 : 0 : static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1222 : 0 : _cleanup_strv_free_ char **expanded_patterns = NULL;
1223 : : char **pattern;
1224 : : int r;
1225 : :
1226 [ # # # # ]: 0 : STRV_FOREACH(pattern, patterns) {
1227 [ # # # ]: 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1228 [ # # # # : 0 : _cleanup_free_ char *unit = NULL, *unit_id = NULL;
# # ]
1229 : :
1230 [ # # ]: 0 : if (strv_extend(&expanded_patterns, *pattern) < 0)
1231 : 0 : return log_oom();
1232 : :
1233 [ # # ]: 0 : if (string_is_glob(*pattern))
1234 : 0 : continue;
1235 : :
1236 : 0 : unit = unit_dbus_path_from_name(*pattern);
1237 [ # # ]: 0 : if (!unit)
1238 : 0 : return log_oom();
1239 : :
1240 : 0 : r = sd_bus_get_property_string(
1241 : : bus,
1242 : : "org.freedesktop.systemd1",
1243 : : unit,
1244 : : "org.freedesktop.systemd1.Unit",
1245 : : "Id",
1246 : : &error,
1247 : : &unit_id);
1248 [ # # ]: 0 : if (r < 0)
1249 [ # # ]: 0 : return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1250 : :
1251 [ # # ]: 0 : if (!streq(*pattern, unit_id)) {
1252 [ # # ]: 0 : if (strv_extend(&expanded_patterns, unit_id) < 0)
1253 : 0 : return log_oom();
1254 : : }
1255 : : }
1256 : :
1257 : 0 : *ret = expanded_patterns;
1258 : 0 : expanded_patterns = NULL; /* do not free */
1259 : :
1260 : 0 : return 0;
1261 : : }
1262 : :
1263 : 0 : static int dot(int argc, char *argv[], void *userdata) {
1264 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1265 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1266 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1267 : 0 : _cleanup_strv_free_ char **expanded_patterns = NULL;
1268 : 0 : _cleanup_strv_free_ char **expanded_from_patterns = NULL;
1269 : 0 : _cleanup_strv_free_ char **expanded_to_patterns = NULL;
1270 : : int r;
1271 : : UnitInfo u;
1272 : :
1273 : 0 : r = acquire_bus(&bus, NULL);
1274 [ # # ]: 0 : if (r < 0)
1275 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1276 : :
1277 : 0 : r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
1278 [ # # ]: 0 : if (r < 0)
1279 : 0 : return r;
1280 : :
1281 : 0 : r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1282 [ # # ]: 0 : if (r < 0)
1283 : 0 : return r;
1284 : :
1285 : 0 : r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1286 [ # # ]: 0 : if (r < 0)
1287 : 0 : return r;
1288 : :
1289 : 0 : r = sd_bus_call_method(
1290 : : bus,
1291 : : "org.freedesktop.systemd1",
1292 : : "/org/freedesktop/systemd1",
1293 : : "org.freedesktop.systemd1.Manager",
1294 : : "ListUnits",
1295 : : &error,
1296 : : &reply,
1297 : : "");
1298 [ # # ]: 0 : if (r < 0)
1299 [ # # ]: 0 : log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
1300 : :
1301 : 0 : r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1302 [ # # ]: 0 : if (r < 0)
1303 [ # # ]: 0 : return bus_log_parse_error(r);
1304 : :
1305 : 0 : printf("digraph systemd {\n");
1306 : :
1307 [ # # ]: 0 : while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1308 : :
1309 : 0 : r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1310 [ # # ]: 0 : if (r < 0)
1311 : 0 : return r;
1312 : : }
1313 [ # # ]: 0 : if (r < 0)
1314 [ # # ]: 0 : return bus_log_parse_error(r);
1315 : :
1316 : 0 : printf("}\n");
1317 : :
1318 [ # # ]: 0 : log_info(" Color legend: black = Requires\n"
1319 : : " dark blue = Requisite\n"
1320 : : " dark grey = Wants\n"
1321 : : " red = Conflicts\n"
1322 : : " green = After\n");
1323 : :
1324 [ # # ]: 0 : if (on_tty())
1325 [ # # ]: 0 : log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1326 : : "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1327 : :
1328 : 0 : return 0;
1329 : : }
1330 : :
1331 : 0 : static int dump_fallback(sd_bus *bus) {
1332 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1333 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1334 : 0 : const char *text = NULL;
1335 : : int r;
1336 : :
1337 [ # # ]: 0 : assert(bus);
1338 : :
1339 : 0 : r = sd_bus_call_method(
1340 : : bus,
1341 : : "org.freedesktop.systemd1",
1342 : : "/org/freedesktop/systemd1",
1343 : : "org.freedesktop.systemd1.Manager",
1344 : : "Dump",
1345 : : &error,
1346 : : &reply,
1347 : : NULL);
1348 [ # # ]: 0 : if (r < 0)
1349 [ # # ]: 0 : return log_error_errno(r, "Failed to issue method call Dump: %s", bus_error_message(&error, r));
1350 : :
1351 : 0 : r = sd_bus_message_read(reply, "s", &text);
1352 [ # # ]: 0 : if (r < 0)
1353 [ # # ]: 0 : return bus_log_parse_error(r);
1354 : :
1355 : 0 : fputs(text, stdout);
1356 : 0 : return 0;
1357 : : }
1358 : :
1359 : 0 : static int dump(int argc, char *argv[], void *userdata) {
1360 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1361 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1362 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1363 : 0 : int fd = -1;
1364 : : int r;
1365 : :
1366 : 0 : r = acquire_bus(&bus, NULL);
1367 [ # # ]: 0 : if (r < 0)
1368 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1369 : :
1370 : 0 : (void) pager_open(arg_pager_flags);
1371 : :
1372 [ # # ]: 0 : if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD))
1373 : 0 : return dump_fallback(bus);
1374 : :
1375 : 0 : r = sd_bus_call_method(
1376 : : bus,
1377 : : "org.freedesktop.systemd1",
1378 : : "/org/freedesktop/systemd1",
1379 : : "org.freedesktop.systemd1.Manager",
1380 : : "DumpByFileDescriptor",
1381 : : &error,
1382 : : &reply,
1383 : : NULL);
1384 [ # # ]: 0 : if (r < 0) {
1385 : : /* fall back to Dump if DumpByFileDescriptor is not supported */
1386 [ # # # # ]: 0 : if (!IN_SET(r, -EACCES, -EBADR))
1387 [ # # ]: 0 : return log_error_errno(r, "Failed to issue method call DumpByFileDescriptor: %s",
1388 : : bus_error_message(&error, r));
1389 : :
1390 : 0 : return dump_fallback(bus);
1391 : : }
1392 : :
1393 : 0 : r = sd_bus_message_read(reply, "h", &fd);
1394 [ # # ]: 0 : if (r < 0)
1395 [ # # ]: 0 : return bus_log_parse_error(r);
1396 : :
1397 : 0 : fflush(stdout);
1398 : 0 : return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, 0);
1399 : : }
1400 : :
1401 : 0 : static int cat_config(int argc, char *argv[], void *userdata) {
1402 : : char **arg, **list;
1403 : : int r;
1404 : :
1405 : 0 : (void) pager_open(arg_pager_flags);
1406 : :
1407 : 0 : list = strv_skip(argv, 1);
1408 [ # # # # ]: 0 : STRV_FOREACH(arg, list) {
1409 : 0 : const char *t = NULL;
1410 : :
1411 [ # # ]: 0 : if (arg != list)
1412 : 0 : print_separator();
1413 : :
1414 [ # # ]: 0 : if (path_is_absolute(*arg)) {
1415 : : const char *dir;
1416 : :
1417 [ # # # # ]: 0 : NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
1418 : 0 : t = path_startswith(*arg, dir);
1419 [ # # ]: 0 : if (t)
1420 : 0 : break;
1421 : : }
1422 : :
1423 [ # # ]: 0 : if (!t)
1424 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1425 : : "Path %s does not start with any known prefix.", *arg);
1426 : : } else
1427 : 0 : t = *arg;
1428 : :
1429 : 0 : r = conf_files_cat(arg_root, t);
1430 [ # # ]: 0 : if (r < 0)
1431 : 0 : return r;
1432 : : }
1433 : :
1434 : 0 : return 0;
1435 : : }
1436 : :
1437 : 0 : static int set_log_level(int argc, char *argv[], void *userdata) {
1438 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1439 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1440 : : int r;
1441 : :
1442 [ # # ]: 0 : assert(argc == 2);
1443 [ # # ]: 0 : assert(argv);
1444 : :
1445 : 0 : r = acquire_bus(&bus, NULL);
1446 [ # # ]: 0 : if (r < 0)
1447 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1448 : :
1449 : 0 : r = sd_bus_set_property(
1450 : : bus,
1451 : : "org.freedesktop.systemd1",
1452 : : "/org/freedesktop/systemd1",
1453 : : "org.freedesktop.systemd1.Manager",
1454 : : "LogLevel",
1455 : : &error,
1456 : : "s",
1457 : 0 : argv[1]);
1458 [ # # ]: 0 : if (r < 0)
1459 [ # # ]: 0 : return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1460 : :
1461 : 0 : return 0;
1462 : : }
1463 : :
1464 : 0 : static int get_log_level(int argc, char *argv[], void *userdata) {
1465 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1466 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1467 : 0 : _cleanup_free_ char *level = NULL;
1468 : : int r;
1469 : :
1470 : 0 : r = acquire_bus(&bus, NULL);
1471 [ # # ]: 0 : if (r < 0)
1472 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1473 : :
1474 : 0 : r = sd_bus_get_property_string(
1475 : : bus,
1476 : : "org.freedesktop.systemd1",
1477 : : "/org/freedesktop/systemd1",
1478 : : "org.freedesktop.systemd1.Manager",
1479 : : "LogLevel",
1480 : : &error,
1481 : : &level);
1482 [ # # ]: 0 : if (r < 0)
1483 [ # # ]: 0 : return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
1484 : :
1485 : 0 : puts(level);
1486 : 0 : return 0;
1487 : : }
1488 : :
1489 : 0 : static int get_or_set_log_level(int argc, char *argv[], void *userdata) {
1490 [ # # ]: 0 : return (argc == 1) ? get_log_level(argc, argv, userdata) : set_log_level(argc, argv, userdata);
1491 : : }
1492 : :
1493 : 0 : static int set_log_target(int argc, char *argv[], void *userdata) {
1494 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1495 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1496 : : int r;
1497 : :
1498 [ # # ]: 0 : assert(argc == 2);
1499 [ # # ]: 0 : assert(argv);
1500 : :
1501 : 0 : r = acquire_bus(&bus, NULL);
1502 [ # # ]: 0 : if (r < 0)
1503 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1504 : :
1505 : 0 : r = sd_bus_set_property(
1506 : : bus,
1507 : : "org.freedesktop.systemd1",
1508 : : "/org/freedesktop/systemd1",
1509 : : "org.freedesktop.systemd1.Manager",
1510 : : "LogTarget",
1511 : : &error,
1512 : : "s",
1513 : 0 : argv[1]);
1514 [ # # ]: 0 : if (r < 0)
1515 [ # # ]: 0 : return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1516 : :
1517 : 0 : return 0;
1518 : : }
1519 : :
1520 : 0 : static int get_log_target(int argc, char *argv[], void *userdata) {
1521 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1522 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1523 : 0 : _cleanup_free_ char *target = NULL;
1524 : : int r;
1525 : :
1526 : 0 : r = acquire_bus(&bus, NULL);
1527 [ # # ]: 0 : if (r < 0)
1528 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1529 : :
1530 : 0 : r = sd_bus_get_property_string(
1531 : : bus,
1532 : : "org.freedesktop.systemd1",
1533 : : "/org/freedesktop/systemd1",
1534 : : "org.freedesktop.systemd1.Manager",
1535 : : "LogTarget",
1536 : : &error,
1537 : : &target);
1538 [ # # ]: 0 : if (r < 0)
1539 [ # # ]: 0 : return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
1540 : :
1541 : 0 : puts(target);
1542 : 0 : return 0;
1543 : : }
1544 : :
1545 : 0 : static int get_or_set_log_target(int argc, char *argv[], void *userdata) {
1546 [ # # ]: 0 : return (argc == 1) ? get_log_target(argc, argv, userdata) : set_log_target(argc, argv, userdata);
1547 : : }
1548 : :
1549 : 0 : static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int flags) {
1550 : : char **s;
1551 [ # # # # ]: 0 : STRV_FOREACH(s, strv)
1552 [ # # ]: 0 : if (strv_fnmatch_or_empty(patterns, *s, flags))
1553 : 0 : return true;
1554 : :
1555 : 0 : return false;
1556 : : }
1557 : :
1558 : 0 : static int do_unit_files(int argc, char *argv[], void *userdata) {
1559 : 0 : _cleanup_(lookup_paths_free) LookupPaths lp = {};
1560 : 0 : _cleanup_hashmap_free_ Hashmap *unit_ids = NULL;
1561 : 0 : _cleanup_hashmap_free_ Hashmap *unit_names = NULL;
1562 : 0 : char **patterns = strv_skip(argv, 1);
1563 : : Iterator i;
1564 : : const char *k, *dst;
1565 : : char **v;
1566 : : int r;
1567 : :
1568 : 0 : r = lookup_paths_init(&lp, arg_scope, 0, NULL);
1569 [ # # ]: 0 : if (r < 0)
1570 [ # # ]: 0 : return log_error_errno(r, "lookup_paths_init() failed: %m");
1571 : :
1572 : 0 : r = unit_file_build_name_map(&lp, NULL, &unit_ids, &unit_names, NULL);
1573 [ # # ]: 0 : if (r < 0)
1574 [ # # ]: 0 : return log_error_errno(r, "unit_file_build_name_map() failed: %m");
1575 : :
1576 [ # # ]: 0 : HASHMAP_FOREACH_KEY(dst, k, unit_ids, i) {
1577 [ # # ]: 0 : if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
1578 [ # # ]: 0 : !strv_fnmatch_or_empty(patterns, dst, FNM_NOESCAPE))
1579 : 0 : continue;
1580 : :
1581 : 0 : printf("ids: %s → %s\n", k, dst);
1582 : : }
1583 : :
1584 [ # # ]: 0 : HASHMAP_FOREACH_KEY(v, k, unit_names, i) {
1585 [ # # ]: 0 : if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
1586 [ # # ]: 0 : !strv_fnmatch_strv_or_empty(patterns, v, FNM_NOESCAPE))
1587 : 0 : continue;
1588 : :
1589 : 0 : _cleanup_free_ char *j = strv_join(v, ", ");
1590 : 0 : printf("aliases: %s ← %s\n", k, j);
1591 : : }
1592 : :
1593 : 0 : return 0;
1594 : : }
1595 : :
1596 : 0 : static int dump_unit_paths(int argc, char *argv[], void *userdata) {
1597 : 0 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
1598 : : int r;
1599 : : char **p;
1600 : :
1601 : 0 : r = lookup_paths_init(&paths, arg_scope, 0, NULL);
1602 [ # # ]: 0 : if (r < 0)
1603 [ # # ]: 0 : return log_error_errno(r, "lookup_paths_init() failed: %m");
1604 : :
1605 [ # # # # ]: 0 : STRV_FOREACH(p, paths.search_path)
1606 : 0 : puts(*p);
1607 : :
1608 : 0 : return 0;
1609 : : }
1610 : :
1611 : 0 : static int dump_exit_status(int argc, char *argv[], void *userdata) {
1612 : 0 : _cleanup_(table_unrefp) Table *table = NULL;
1613 : : int r;
1614 : :
1615 : 0 : table = table_new("name", "status", "class");
1616 [ # # ]: 0 : if (!table)
1617 : 0 : return log_oom();
1618 : :
1619 : 0 : r = table_set_align_percent(table, table_get_cell(table, 0, 1), 100);
1620 [ # # ]: 0 : if (r < 0)
1621 [ # # ]: 0 : return log_error_errno(r, "Failed to right-align status: %m");
1622 : :
1623 [ # # ]: 0 : if (strv_isempty(strv_skip(argv, 1)))
1624 [ # # ]: 0 : for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++) {
1625 [ # # ]: 0 : if (!exit_status_mappings[i].name)
1626 : 0 : continue;
1627 : :
1628 : 0 : r = table_add_many(table,
1629 : : TABLE_STRING, exit_status_mappings[i].name,
1630 : : TABLE_INT, (int) i,
1631 : : TABLE_STRING, exit_status_class(i));
1632 [ # # ]: 0 : if (r < 0)
1633 : 0 : return r;
1634 : : }
1635 : : else
1636 [ # # ]: 0 : for (int i = 1; i < argc; i++) {
1637 : : int status;
1638 : :
1639 : 0 : status = exit_status_from_string(argv[i]);
1640 [ # # ]: 0 : if (status < 0)
1641 [ # # ]: 0 : return log_error_errno(r, "Invalid exit status \"%s\": %m", argv[i]);
1642 : :
1643 [ # # # # ]: 0 : assert(status >= 0 && (size_t) status < ELEMENTSOF(exit_status_mappings));
1644 [ # # # # ]: 0 : r = table_add_many(table,
1645 : : TABLE_STRING, exit_status_mappings[status].name ?: "-",
1646 : : TABLE_INT, status,
1647 : : TABLE_STRING, exit_status_class(status) ?: "-");
1648 [ # # ]: 0 : if (r < 0)
1649 : 0 : return r;
1650 : : }
1651 : :
1652 : 0 : (void) pager_open(arg_pager_flags);
1653 : :
1654 : 0 : return table_print(table, NULL);
1655 : : }
1656 : :
1657 : : #if HAVE_SECCOMP
1658 : :
1659 : 0 : static int load_kernel_syscalls(Set **ret) {
1660 : 0 : _cleanup_(set_free_freep) Set *syscalls = NULL;
1661 : 0 : _cleanup_fclose_ FILE *f = NULL;
1662 : : int r;
1663 : :
1664 : : /* Let's read the available system calls from the list of available tracing events. Slightly dirty,
1665 : : * but good enough for analysis purposes. */
1666 : :
1667 : 0 : f = fopen("/sys/kernel/tracing/available_events", "re");
1668 [ # # ]: 0 : if (!f) {
1669 : : /* We tried the non-debugfs mount point and that didn't work. If it wasn't mounted, maybe the
1670 : : * old debugfs mount point works? */
1671 : 0 : f = fopen("/sys/kernel/debug/tracing/available_events", "re");
1672 [ # # ]: 0 : if (!f)
1673 [ # # # # : 0 : return log_full_errno(IN_SET(errno, EPERM, EACCES, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
# # ]
1674 : : "Can't read open tracefs' available_events file: %m");
1675 : : }
1676 : :
1677 : 0 : for (;;) {
1678 [ # # # # ]: 0 : _cleanup_free_ char *line = NULL;
1679 : : const char *e;
1680 : :
1681 : 0 : r = read_line(f, LONG_LINE_MAX, &line);
1682 [ # # ]: 0 : if (r < 0)
1683 [ # # ]: 0 : return log_error_errno(r, "Failed to read system call list: %m");
1684 [ # # ]: 0 : if (r == 0)
1685 : 0 : break;
1686 : :
1687 : 0 : e = startswith(line, "syscalls:sys_enter_");
1688 [ # # ]: 0 : if (!e)
1689 : 0 : continue;
1690 : :
1691 : : /* These are named differently inside the kernel than their external name for historical
1692 : : * reasons. Let's hide them here. */
1693 [ # # ]: 0 : if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl"))
1694 : 0 : continue;
1695 : :
1696 : 0 : r = set_ensure_allocated(&syscalls, &string_hash_ops);
1697 [ # # ]: 0 : if (r < 0)
1698 : 0 : return log_oom();
1699 : :
1700 : 0 : r = set_put_strdup(syscalls, e);
1701 [ # # ]: 0 : if (r < 0)
1702 [ # # ]: 0 : return log_error_errno(r, "Failed to add system call to list: %m");
1703 : : }
1704 : :
1705 : 0 : *ret = TAKE_PTR(syscalls);
1706 : 0 : return 0;
1707 : : }
1708 : :
1709 : 0 : static void kernel_syscalls_remove(Set *s, const SyscallFilterSet *set) {
1710 : : const char *syscall;
1711 : :
1712 [ # # # # ]: 0 : NULSTR_FOREACH(syscall, set->value) {
1713 [ # # ]: 0 : if (syscall[0] == '@')
1714 : 0 : continue;
1715 : :
1716 : 0 : (void) set_remove(s, syscall);
1717 : : }
1718 : 0 : }
1719 : :
1720 : 0 : static void dump_syscall_filter(const SyscallFilterSet *set) {
1721 : : const char *syscall;
1722 : :
1723 : 0 : printf("%s%s%s\n"
1724 : : " # %s\n",
1725 : : ansi_highlight(),
1726 : : set->name,
1727 : : ansi_normal(),
1728 : : set->help);
1729 : :
1730 [ # # # # ]: 0 : NULSTR_FOREACH(syscall, set->value)
1731 [ # # ]: 0 : printf(" %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal());
1732 : 0 : }
1733 : :
1734 : 0 : static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
1735 : 0 : bool first = true;
1736 : :
1737 : 0 : (void) pager_open(arg_pager_flags);
1738 : :
1739 [ # # ]: 0 : if (strv_isempty(strv_skip(argv, 1))) {
1740 : 0 : _cleanup_(set_free_freep) Set *kernel = NULL;
1741 : : int i, k;
1742 : :
1743 : 0 : k = load_kernel_syscalls(&kernel);
1744 : :
1745 [ # # ]: 0 : for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
1746 : 0 : const SyscallFilterSet *set = syscall_filter_sets + i;
1747 [ # # ]: 0 : if (!first)
1748 : 0 : puts("");
1749 : :
1750 : 0 : dump_syscall_filter(set);
1751 : 0 : kernel_syscalls_remove(kernel, set);
1752 : 0 : first = false;
1753 : : }
1754 : :
1755 [ # # ]: 0 : if (k < 0) {
1756 : 0 : fputc('\n', stdout);
1757 : 0 : fflush(stdout);
1758 [ # # ]: 0 : log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
1759 [ # # ]: 0 : } else if (!set_isempty(kernel)) {
1760 : : const char *syscall;
1761 : : Iterator j;
1762 : :
1763 : 0 : printf("\n"
1764 : : "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n",
1765 : : ansi_highlight(), ansi_normal());
1766 : :
1767 [ # # ]: 0 : SET_FOREACH(syscall, kernel, j)
1768 : 0 : printf("# %s\n", syscall);
1769 : : }
1770 : : } else {
1771 : : char **name;
1772 : :
1773 [ # # # # ]: 0 : STRV_FOREACH(name, strv_skip(argv, 1)) {
1774 : : const SyscallFilterSet *set;
1775 : :
1776 [ # # ]: 0 : if (!first)
1777 : 0 : puts("");
1778 : :
1779 : 0 : set = syscall_filter_set_find(*name);
1780 [ # # ]: 0 : if (!set) {
1781 : : /* make sure the error appears below normal output */
1782 : 0 : fflush(stdout);
1783 : :
1784 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
1785 : : "Filter set \"%s\" not found.", *name);
1786 : : }
1787 : :
1788 : 0 : dump_syscall_filter(set);
1789 : 0 : first = false;
1790 : : }
1791 : : }
1792 : :
1793 : 0 : return 0;
1794 : : }
1795 : :
1796 : : #else
1797 : : static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
1798 : : return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with syscall filters, sorry.");
1799 : : }
1800 : : #endif
1801 : :
1802 : 0 : static void parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) {
1803 [ # # # # ]: 0 : if (calendar && calendar_spec_from_string(p, NULL) >= 0)
1804 [ # # ]: 0 : log_notice("Hint: this expression is a valid calendar specification. "
1805 : : "Use 'systemd-analyze calendar \"%s\"' instead?", p);
1806 [ # # # # ]: 0 : if (timestamp && parse_timestamp(p, NULL) >= 0)
1807 [ # # ]: 0 : log_notice("Hint: this expression is a valid timestamp. "
1808 : : "Use 'systemd-analyze timestamp \"%s\"' instead?", p);
1809 [ # # # # ]: 0 : if (timespan && parse_time(p, NULL, USEC_PER_SEC) >= 0)
1810 [ # # ]: 0 : log_notice("Hint: this expression is a valid timespan. "
1811 : : "Use 'systemd-analyze timespan \"%s\"' instead?", p);
1812 : 0 : }
1813 : :
1814 : 0 : static int dump_timespan(int argc, char *argv[], void *userdata) {
1815 : : char **input_timespan;
1816 : :
1817 [ # # # # ]: 0 : STRV_FOREACH(input_timespan, strv_skip(argv, 1)) {
1818 [ # # ]: 0 : _cleanup_(table_unrefp) Table *table = NULL;
1819 : : usec_t output_usecs;
1820 : : TableCell *cell;
1821 : : int r;
1822 : :
1823 : 0 : r = parse_time(*input_timespan, &output_usecs, USEC_PER_SEC);
1824 [ # # ]: 0 : if (r < 0) {
1825 [ # # ]: 0 : log_error_errno(r, "Failed to parse time span '%s': %m", *input_timespan);
1826 : 0 : parsing_hint(*input_timespan, true, true, false);
1827 : 0 : return r;
1828 : : }
1829 : :
1830 : 0 : table = table_new("name", "value");
1831 [ # # ]: 0 : if (!table)
1832 : 0 : return log_oom();
1833 : :
1834 : 0 : table_set_header(table, false);
1835 : :
1836 [ # # ]: 0 : assert_se(cell = table_get_cell(table, 0, 0));
1837 : 0 : r = table_set_ellipsize_percent(table, cell, 100);
1838 [ # # ]: 0 : if (r < 0)
1839 : 0 : return r;
1840 : :
1841 : 0 : r = table_set_align_percent(table, cell, 100);
1842 [ # # ]: 0 : if (r < 0)
1843 : 0 : return r;
1844 : :
1845 [ # # ]: 0 : assert_se(cell = table_get_cell(table, 0, 1));
1846 : 0 : r = table_set_ellipsize_percent(table, cell, 100);
1847 [ # # ]: 0 : if (r < 0)
1848 : 0 : return r;
1849 : :
1850 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Original:");
1851 [ # # ]: 0 : if (r < 0)
1852 : 0 : return r;
1853 : :
1854 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, *input_timespan);
1855 [ # # ]: 0 : if (r < 0)
1856 : 0 : return r;
1857 : :
1858 : 0 : r = table_add_cell_stringf(table, NULL, "%ss:", special_glyph(SPECIAL_GLYPH_MU));
1859 [ # # ]: 0 : if (r < 0)
1860 : 0 : return r;
1861 : :
1862 : 0 : r = table_add_cell(table, NULL, TABLE_UINT64, &output_usecs);
1863 [ # # ]: 0 : if (r < 0)
1864 : 0 : return r;
1865 : :
1866 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Human:");
1867 [ # # ]: 0 : if (r < 0)
1868 : 0 : return r;
1869 : :
1870 : 0 : r = table_add_cell(table, &cell, TABLE_TIMESPAN, &output_usecs);
1871 [ # # ]: 0 : if (r < 0)
1872 : 0 : return r;
1873 : :
1874 : 0 : r = table_set_color(table, cell, ansi_highlight());
1875 [ # # ]: 0 : if (r < 0)
1876 : 0 : return r;
1877 : :
1878 : 0 : r = table_print(table, NULL);
1879 [ # # ]: 0 : if (r < 0)
1880 : 0 : return r;
1881 : :
1882 [ # # ]: 0 : if (input_timespan[1])
1883 : 0 : putchar('\n');
1884 : : }
1885 : :
1886 : 0 : return EXIT_SUCCESS;
1887 : : }
1888 : :
1889 : 0 : static int test_timestamp_one(const char *p) {
1890 : 0 : _cleanup_(table_unrefp) Table *table = NULL;
1891 : : TableCell *cell;
1892 : : usec_t usec;
1893 : : int r;
1894 : :
1895 : 0 : r = parse_timestamp(p, &usec);
1896 [ # # ]: 0 : if (r < 0) {
1897 [ # # ]: 0 : log_error_errno(r, "Failed to parse \"%s\": %m", p);
1898 : 0 : parsing_hint(p, true, false, true);
1899 : 0 : return r;
1900 : : }
1901 : :
1902 : 0 : table = table_new("name", "value");
1903 [ # # ]: 0 : if (!table)
1904 : 0 : return log_oom();
1905 : :
1906 : 0 : table_set_header(table, false);
1907 : :
1908 [ # # ]: 0 : assert_se(cell = table_get_cell(table, 0, 0));
1909 : 0 : r = table_set_ellipsize_percent(table, cell, 100);
1910 [ # # ]: 0 : if (r < 0)
1911 : 0 : return r;
1912 : :
1913 : 0 : r = table_set_align_percent(table, cell, 100);
1914 [ # # ]: 0 : if (r < 0)
1915 : 0 : return r;
1916 : :
1917 [ # # ]: 0 : assert_se(cell = table_get_cell(table, 0, 1));
1918 : 0 : r = table_set_ellipsize_percent(table, cell, 100);
1919 [ # # ]: 0 : if (r < 0)
1920 : 0 : return r;
1921 : :
1922 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Original form:");
1923 [ # # ]: 0 : if (r < 0)
1924 : 0 : return r;
1925 : :
1926 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, p);
1927 [ # # ]: 0 : if (r < 0)
1928 : 0 : return r;
1929 : :
1930 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Normalized form:");
1931 [ # # ]: 0 : if (r < 0)
1932 : 0 : return r;
1933 : :
1934 : 0 : r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &usec);
1935 [ # # ]: 0 : if (r < 0)
1936 : 0 : return r;
1937 : :
1938 : 0 : r = table_set_color(table, cell, ansi_highlight_blue());
1939 [ # # ]: 0 : if (r < 0)
1940 : 0 : return r;
1941 : :
1942 [ # # ]: 0 : if (!in_utc_timezone()) {
1943 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "(in UTC):");
1944 [ # # ]: 0 : if (r < 0)
1945 : 0 : return r;
1946 : :
1947 : 0 : r = table_add_cell(table, &cell, TABLE_TIMESTAMP_UTC, &usec);
1948 [ # # ]: 0 : if (r < 0)
1949 : 0 : return r;
1950 : : }
1951 : :
1952 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "UNIX seconds:");
1953 [ # # ]: 0 : if (r < 0)
1954 : 0 : return r;
1955 : :
1956 [ # # ]: 0 : if (usec % USEC_PER_SEC == 0)
1957 : 0 : r = table_add_cell_stringf(table, &cell, "@%"PRI_USEC,
1958 : : usec / USEC_PER_SEC);
1959 : : else
1960 : 0 : r = table_add_cell_stringf(table, &cell, "@%"PRI_USEC".%06"PRI_USEC"",
1961 : : usec / USEC_PER_SEC,
1962 : : usec % USEC_PER_SEC);
1963 [ # # ]: 0 : if (r < 0)
1964 : 0 : return r;
1965 : :
1966 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "From now:");
1967 [ # # ]: 0 : if (r < 0)
1968 : 0 : return r;
1969 : :
1970 : 0 : r = table_add_cell(table, &cell, TABLE_TIMESTAMP_RELATIVE, &usec);
1971 [ # # ]: 0 : if (r < 0)
1972 : 0 : return r;
1973 : :
1974 : 0 : return table_print(table, NULL);
1975 : : }
1976 : :
1977 : 0 : static int test_timestamp(int argc, char *argv[], void *userdata) {
1978 : 0 : int ret = 0, r;
1979 : : char **p;
1980 : :
1981 [ # # # # ]: 0 : STRV_FOREACH(p, strv_skip(argv, 1)) {
1982 : 0 : r = test_timestamp_one(*p);
1983 [ # # # # ]: 0 : if (ret == 0 && r < 0)
1984 : 0 : ret = r;
1985 : :
1986 [ # # ]: 0 : if (*(p + 1))
1987 : 0 : putchar('\n');
1988 : : }
1989 : :
1990 : 0 : return ret;
1991 : : }
1992 : :
1993 : 0 : static int test_calendar_one(usec_t n, const char *p) {
1994 : 0 : _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
1995 : 0 : _cleanup_(table_unrefp) Table *table = NULL;
1996 : 0 : _cleanup_free_ char *t = NULL;
1997 : : TableCell *cell;
1998 : : int r;
1999 : :
2000 : 0 : r = calendar_spec_from_string(p, &spec);
2001 [ # # ]: 0 : if (r < 0) {
2002 [ # # ]: 0 : log_error_errno(r, "Failed to parse calendar specification '%s': %m", p);
2003 : 0 : parsing_hint(p, false, true, true);
2004 : 0 : return r;
2005 : : }
2006 : :
2007 : 0 : r = calendar_spec_to_string(spec, &t);
2008 [ # # ]: 0 : if (r < 0)
2009 [ # # ]: 0 : return log_error_errno(r, "Failed to format calendar specification '%s': %m", p);
2010 : :
2011 : 0 : table = table_new("name", "value");
2012 [ # # ]: 0 : if (!table)
2013 : 0 : return log_oom();
2014 : :
2015 : 0 : table_set_header(table, false);
2016 : :
2017 [ # # ]: 0 : assert_se(cell = table_get_cell(table, 0, 0));
2018 : 0 : r = table_set_ellipsize_percent(table, cell, 100);
2019 [ # # ]: 0 : if (r < 0)
2020 : 0 : return r;
2021 : :
2022 : 0 : r = table_set_align_percent(table, cell, 100);
2023 [ # # ]: 0 : if (r < 0)
2024 : 0 : return r;
2025 : :
2026 [ # # ]: 0 : assert_se(cell = table_get_cell(table, 0, 1));
2027 : 0 : r = table_set_ellipsize_percent(table, cell, 100);
2028 [ # # ]: 0 : if (r < 0)
2029 : 0 : return r;
2030 : :
2031 [ # # ]: 0 : if (!streq(t, p)) {
2032 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Original form:");
2033 [ # # ]: 0 : if (r < 0)
2034 : 0 : return r;
2035 : :
2036 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, p);
2037 [ # # ]: 0 : if (r < 0)
2038 : 0 : return r;
2039 : : }
2040 : :
2041 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Normalized form:");
2042 [ # # ]: 0 : if (r < 0)
2043 : 0 : return r;
2044 : :
2045 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, t);
2046 [ # # ]: 0 : if (r < 0)
2047 : 0 : return r;
2048 : :
2049 [ # # ]: 0 : for (unsigned i = 0; i < arg_iterations; i++) {
2050 : : usec_t next;
2051 : :
2052 : 0 : r = calendar_spec_next_usec(spec, n, &next);
2053 [ # # ]: 0 : if (r == -ENOENT) {
2054 [ # # ]: 0 : if (i == 0) {
2055 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Next elapse:");
2056 [ # # ]: 0 : if (r < 0)
2057 : 0 : return r;
2058 : :
2059 : 0 : r = table_add_cell(table, &cell, TABLE_STRING, "never");
2060 [ # # ]: 0 : if (r < 0)
2061 : 0 : return r;
2062 : :
2063 : 0 : r = table_set_color(table, cell, ansi_highlight_yellow());
2064 [ # # ]: 0 : if (r < 0)
2065 : 0 : return r;
2066 : : }
2067 : 0 : break;
2068 : : }
2069 [ # # ]: 0 : if (r < 0)
2070 [ # # ]: 0 : return log_error_errno(r, "Failed to determine next elapse for '%s': %m", p);
2071 : :
2072 [ # # ]: 0 : if (i == 0) {
2073 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Next elapse:");
2074 [ # # ]: 0 : if (r < 0)
2075 : 0 : return r;
2076 : :
2077 : 0 : r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &next);
2078 [ # # ]: 0 : if (r < 0)
2079 : 0 : return r;
2080 : :
2081 : 0 : r = table_set_color(table, cell, ansi_highlight_blue());
2082 [ # # ]: 0 : if (r < 0)
2083 : 0 : return r;
2084 : : } else {
2085 [ # # ]: 0 : int k = DECIMAL_STR_WIDTH(i + 1);
2086 : :
2087 [ # # ]: 0 : if (k < 8)
2088 : 0 : k = 8 - k;
2089 : : else
2090 : 0 : k = 0;
2091 : :
2092 : 0 : r = table_add_cell_stringf(table, NULL, "Iter. #%u:", i+1);
2093 [ # # ]: 0 : if (r < 0)
2094 : 0 : return r;
2095 : :
2096 : 0 : r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &next);
2097 [ # # ]: 0 : if (r < 0)
2098 : 0 : return r;
2099 : :
2100 : 0 : r = table_set_color(table, cell, ansi_highlight_blue());
2101 [ # # ]: 0 : if (r < 0)
2102 : 0 : return r;
2103 : : }
2104 : :
2105 [ # # ]: 0 : if (!in_utc_timezone()) {
2106 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "(in UTC):");
2107 [ # # ]: 0 : if (r < 0)
2108 : 0 : return r;
2109 : :
2110 : 0 : r = table_add_cell(table, NULL, TABLE_TIMESTAMP_UTC, &next);
2111 [ # # ]: 0 : if (r < 0)
2112 : 0 : return r;
2113 : : }
2114 : :
2115 : 0 : r = table_add_cell(table, NULL, TABLE_STRING, "From now:");
2116 [ # # ]: 0 : if (r < 0)
2117 : 0 : return r;
2118 : :
2119 : 0 : r = table_add_cell(table, NULL, TABLE_TIMESTAMP_RELATIVE, &next);
2120 [ # # ]: 0 : if (r < 0)
2121 : 0 : return r;
2122 : :
2123 : 0 : n = next;
2124 : : }
2125 : :
2126 : 0 : return table_print(table, NULL);
2127 : : }
2128 : :
2129 : 0 : static int test_calendar(int argc, char *argv[], void *userdata) {
2130 : 0 : int ret = 0, r;
2131 : : char **p;
2132 : : usec_t n;
2133 : :
2134 : 0 : n = now(CLOCK_REALTIME); /* We want to use the same "base" for all expressions */
2135 : :
2136 [ # # # # ]: 0 : STRV_FOREACH(p, strv_skip(argv, 1)) {
2137 : 0 : r = test_calendar_one(n, *p);
2138 [ # # # # ]: 0 : if (ret == 0 && r < 0)
2139 : 0 : ret = r;
2140 : :
2141 [ # # ]: 0 : if (*(p + 1))
2142 : 0 : putchar('\n');
2143 : : }
2144 : :
2145 : 0 : return ret;
2146 : : }
2147 : :
2148 : 0 : static int service_watchdogs(int argc, char *argv[], void *userdata) {
2149 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2150 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2151 : : int b, r;
2152 : :
2153 [ # # # # ]: 0 : assert(IN_SET(argc, 1, 2));
2154 [ # # ]: 0 : assert(argv);
2155 : :
2156 : 0 : r = acquire_bus(&bus, NULL);
2157 [ # # ]: 0 : if (r < 0)
2158 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
2159 : :
2160 : : /* get ServiceWatchdogs */
2161 [ # # ]: 0 : if (argc == 1) {
2162 : 0 : r = sd_bus_get_property_trivial(
2163 : : bus,
2164 : : "org.freedesktop.systemd1",
2165 : : "/org/freedesktop/systemd1",
2166 : : "org.freedesktop.systemd1.Manager",
2167 : : "ServiceWatchdogs",
2168 : : &error,
2169 : : 'b',
2170 : : &b);
2171 [ # # ]: 0 : if (r < 0)
2172 [ # # ]: 0 : return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
2173 : :
2174 : 0 : printf("%s\n", yes_no(!!b));
2175 : :
2176 : 0 : return 0;
2177 : : }
2178 : :
2179 : : /* set ServiceWatchdogs */
2180 : 0 : b = parse_boolean(argv[1]);
2181 [ # # ]: 0 : if (b < 0) {
2182 [ # # ]: 0 : log_error("Failed to parse service-watchdogs argument.");
2183 : 0 : return -EINVAL;
2184 : : }
2185 : :
2186 : 0 : r = sd_bus_set_property(
2187 : : bus,
2188 : : "org.freedesktop.systemd1",
2189 : : "/org/freedesktop/systemd1",
2190 : : "org.freedesktop.systemd1.Manager",
2191 : : "ServiceWatchdogs",
2192 : : &error,
2193 : : "b",
2194 : : b);
2195 [ # # ]: 0 : if (r < 0)
2196 [ # # ]: 0 : return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
2197 : :
2198 : 0 : return 0;
2199 : : }
2200 : :
2201 : 0 : static int do_condition(int argc, char *argv[], void *userdata) {
2202 : 0 : return verify_conditions(strv_skip(argv, 1), arg_scope);
2203 : : }
2204 : :
2205 : 0 : static int do_verify(int argc, char *argv[], void *userdata) {
2206 : 0 : return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
2207 : : }
2208 : :
2209 : 0 : static int do_security(int argc, char *argv[], void *userdata) {
2210 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2211 : : int r;
2212 : :
2213 : 0 : r = acquire_bus(&bus, NULL);
2214 [ # # ]: 0 : if (r < 0)
2215 [ # # ]: 0 : return log_error_errno(r, "Failed to create bus connection: %m");
2216 : :
2217 : 0 : (void) pager_open(arg_pager_flags);
2218 : :
2219 : 0 : return analyze_security(bus, strv_skip(argv, 1), 0);
2220 : : }
2221 : :
2222 : 12 : static int help(int argc, char *argv[], void *userdata) {
2223 : 12 : _cleanup_free_ char *link = NULL, *dot_link = NULL;
2224 : : int r;
2225 : :
2226 : 12 : (void) pager_open(arg_pager_flags);
2227 : :
2228 : 12 : r = terminal_urlify_man("systemd-analyze", "1", &link);
2229 [ - + ]: 12 : if (r < 0)
2230 : 0 : return log_oom();
2231 : :
2232 : : /* Not using terminal_urlify_man() for this, since we don't want the "man page" text suffix in this case. */
2233 : 12 : r = terminal_urlify("man:dot(1)", "dot(1)", &dot_link);
2234 [ - + ]: 12 : if (r < 0)
2235 : 0 : return log_oom();
2236 : :
2237 : 12 : printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2238 : : "Profile systemd, show unit dependencies, check unit files.\n\n"
2239 : : " -h --help Show this help\n"
2240 : : " --version Show package version\n"
2241 : : " --no-pager Do not pipe output into a pager\n"
2242 : : " --system Operate on system systemd instance\n"
2243 : : " --user Operate on user systemd instance\n"
2244 : : " --global Operate on global user configuration\n"
2245 : : " -H --host=[USER@]HOST Operate on remote host\n"
2246 : : " -M --machine=CONTAINER Operate on local container\n"
2247 : : " --order Show only order in the graph\n"
2248 : : " --require Show only requirement in the graph\n"
2249 : : " --from-pattern=GLOB Show only origins in the graph\n"
2250 : : " --to-pattern=GLOB Show only destinations in the graph\n"
2251 : : " --fuzz=SECONDS Also print services which finished SECONDS earlier\n"
2252 : : " than the latest in the branch\n"
2253 : : " --man[=BOOL] Do [not] check for existence of man pages\n"
2254 : : " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n"
2255 : : " --iterations=N Show the specified number of iterations\n"
2256 : : "\nCommands:\n"
2257 : : " time Print time spent in the kernel\n"
2258 : : " blame Print list of running units ordered by time to init\n"
2259 : : " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
2260 : : " plot Output SVG graphic showing service initialization\n"
2261 : : " dot [UNIT...] Output dependency graph in %s format\n"
2262 : : " log-level [LEVEL] Get/set logging threshold for manager\n"
2263 : : " log-target [TARGET] Get/set logging target for manager\n"
2264 : : " dump Output state serialization of service manager\n"
2265 : : " cat-config Show configuration file and drop-ins\n"
2266 : : " unit-files List files and symlinks for units\n"
2267 : : " unit-paths List load directories for units\n"
2268 : : " exit-status [STATUS...] List exit status definitions\n"
2269 : : " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
2270 : : " condition CONDITION... Evaluate conditions and asserts\n"
2271 : : " verify FILE... Check unit files for correctness\n"
2272 : : " service-watchdogs [BOOL] Get/set service watchdog state\n"
2273 : : " calendar SPEC... Validate repetitive calendar time events\n"
2274 : : " timestamp TIMESTAMP... Validate a timestamp\n"
2275 : : " timespan SPAN... Validate a time span\n"
2276 : : " security [UNIT...] Analyze security of unit\n"
2277 : : "\nSee the %s for details.\n"
2278 : : , program_invocation_short_name
2279 : : , dot_link
2280 : : , link
2281 : : );
2282 : :
2283 : : /* When updating this list, including descriptions, apply changes to
2284 : : * shell-completion/bash/systemd-analyze and shell-completion/zsh/_systemd-analyze too. */
2285 : :
2286 : 12 : return 0;
2287 : : }
2288 : :
2289 : 16 : static int parse_argv(int argc, char *argv[]) {
2290 : : enum {
2291 : : ARG_VERSION = 0x100,
2292 : : ARG_ORDER,
2293 : : ARG_REQUIRE,
2294 : : ARG_ROOT,
2295 : : ARG_SYSTEM,
2296 : : ARG_USER,
2297 : : ARG_GLOBAL,
2298 : : ARG_DOT_FROM_PATTERN,
2299 : : ARG_DOT_TO_PATTERN,
2300 : : ARG_FUZZ,
2301 : : ARG_NO_PAGER,
2302 : : ARG_MAN,
2303 : : ARG_GENERATORS,
2304 : : ARG_ITERATIONS,
2305 : : };
2306 : :
2307 : : static const struct option options[] = {
2308 : : { "help", no_argument, NULL, 'h' },
2309 : : { "version", no_argument, NULL, ARG_VERSION },
2310 : : { "order", no_argument, NULL, ARG_ORDER },
2311 : : { "require", no_argument, NULL, ARG_REQUIRE },
2312 : : { "root", required_argument, NULL, ARG_ROOT },
2313 : : { "system", no_argument, NULL, ARG_SYSTEM },
2314 : : { "user", no_argument, NULL, ARG_USER },
2315 : : { "global", no_argument, NULL, ARG_GLOBAL },
2316 : : { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
2317 : : { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
2318 : : { "fuzz", required_argument, NULL, ARG_FUZZ },
2319 : : { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2320 : : { "man", optional_argument, NULL, ARG_MAN },
2321 : : { "generators", optional_argument, NULL, ARG_GENERATORS },
2322 : : { "host", required_argument, NULL, 'H' },
2323 : : { "machine", required_argument, NULL, 'M' },
2324 : : { "iterations", required_argument, NULL, ARG_ITERATIONS },
2325 : : {}
2326 : : };
2327 : :
2328 : : int r, c;
2329 : :
2330 [ - + ]: 16 : assert(argc >= 0);
2331 [ - + ]: 16 : assert(argv);
2332 : :
2333 [ + - ]: 16 : while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
2334 [ + - - - : 16 : switch (c) {
- - - - -
- - - - -
- - - +
- ]
2335 : :
2336 : 12 : case 'h':
2337 : 12 : return help(0, NULL, NULL);
2338 : :
2339 : 0 : case ARG_VERSION:
2340 : 0 : return version();
2341 : :
2342 : 0 : case ARG_ROOT:
2343 : 0 : arg_root = optarg;
2344 : 0 : break;
2345 : :
2346 : 0 : case ARG_SYSTEM:
2347 : 0 : arg_scope = UNIT_FILE_SYSTEM;
2348 : 0 : break;
2349 : :
2350 : 0 : case ARG_USER:
2351 : 0 : arg_scope = UNIT_FILE_USER;
2352 : 0 : break;
2353 : :
2354 : 0 : case ARG_GLOBAL:
2355 : 0 : arg_scope = UNIT_FILE_GLOBAL;
2356 : 0 : break;
2357 : :
2358 : 0 : case ARG_ORDER:
2359 : 0 : arg_dot = DEP_ORDER;
2360 : 0 : break;
2361 : :
2362 : 0 : case ARG_REQUIRE:
2363 : 0 : arg_dot = DEP_REQUIRE;
2364 : 0 : break;
2365 : :
2366 : 0 : case ARG_DOT_FROM_PATTERN:
2367 [ # # ]: 0 : if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
2368 : 0 : return log_oom();
2369 : :
2370 : 0 : break;
2371 : :
2372 : 0 : case ARG_DOT_TO_PATTERN:
2373 [ # # ]: 0 : if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
2374 : 0 : return log_oom();
2375 : :
2376 : 0 : break;
2377 : :
2378 : 0 : case ARG_FUZZ:
2379 : 0 : r = parse_sec(optarg, &arg_fuzz);
2380 [ # # ]: 0 : if (r < 0)
2381 : 0 : return r;
2382 : 0 : break;
2383 : :
2384 : 0 : case ARG_NO_PAGER:
2385 : 0 : arg_pager_flags |= PAGER_DISABLE;
2386 : 0 : break;
2387 : :
2388 : 0 : case 'H':
2389 : 0 : arg_transport = BUS_TRANSPORT_REMOTE;
2390 : 0 : arg_host = optarg;
2391 : 0 : break;
2392 : :
2393 : 0 : case 'M':
2394 : 0 : arg_transport = BUS_TRANSPORT_MACHINE;
2395 : 0 : arg_host = optarg;
2396 : 0 : break;
2397 : :
2398 : 0 : case ARG_MAN:
2399 [ # # ]: 0 : if (optarg) {
2400 : 0 : r = parse_boolean(optarg);
2401 [ # # ]: 0 : if (r < 0)
2402 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2403 : : "Failed to parse --man= argument.");
2404 : :
2405 : 0 : arg_man = r;
2406 : : } else
2407 : 0 : arg_man = true;
2408 : :
2409 : 0 : break;
2410 : :
2411 : 0 : case ARG_GENERATORS:
2412 [ # # ]: 0 : if (optarg) {
2413 : 0 : r = parse_boolean(optarg);
2414 [ # # ]: 0 : if (r < 0)
2415 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2416 : : "Failed to parse --generators= argument.");
2417 : :
2418 : 0 : arg_generators = r;
2419 : : } else
2420 : 0 : arg_generators = true;
2421 : :
2422 : 0 : break;
2423 : :
2424 : 0 : case ARG_ITERATIONS:
2425 : 0 : r = safe_atou(optarg, &arg_iterations);
2426 [ # # ]: 0 : if (r < 0)
2427 [ # # ]: 0 : return log_error_errno(r, "Failed to parse iterations: %s", optarg);
2428 : :
2429 : 0 : break;
2430 : :
2431 : 4 : case '?':
2432 : 4 : return -EINVAL;
2433 : :
2434 : 0 : default:
2435 : 0 : assert_not_reached("Unhandled option code.");
2436 : : }
2437 : :
2438 [ # # ]: 0 : if (arg_scope == UNIT_FILE_GLOBAL &&
2439 [ # # # # ]: 0 : !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
2440 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2441 : : "Option --global only makes sense with verbs dot, unit-paths, verify.");
2442 : :
2443 [ # # # # ]: 0 : if (streq_ptr(argv[optind], "cat-config") && arg_scope == UNIT_FILE_USER)
2444 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2445 : : "Option --user is not supported for cat-config right now.");
2446 : :
2447 [ # # # # ]: 0 : if (arg_root && !streq_ptr(argv[optind], "cat-config"))
2448 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2449 : : "Option --root is only supported for cat-config right now.");
2450 : :
2451 : 0 : return 1; /* work to do */
2452 : : }
2453 : :
2454 : 16 : static int run(int argc, char *argv[]) {
2455 : :
2456 : : static const Verb verbs[] = {
2457 : : { "help", VERB_ANY, VERB_ANY, 0, help },
2458 : : { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time },
2459 : : { "blame", VERB_ANY, 1, 0, analyze_blame },
2460 : : { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain },
2461 : : { "plot", VERB_ANY, 1, 0, analyze_plot },
2462 : : { "dot", VERB_ANY, VERB_ANY, 0, dot },
2463 : : { "log-level", VERB_ANY, 2, 0, get_or_set_log_level },
2464 : : { "log-target", VERB_ANY, 2, 0, get_or_set_log_target },
2465 : : /* The following four verbs are deprecated aliases */
2466 : : { "set-log-level", 2, 2, 0, set_log_level },
2467 : : { "get-log-level", VERB_ANY, 1, 0, get_log_level },
2468 : : { "set-log-target", 2, 2, 0, set_log_target },
2469 : : { "get-log-target", VERB_ANY, 1, 0, get_log_target },
2470 : :
2471 : : { "dump", VERB_ANY, 1, 0, dump },
2472 : : { "cat-config", 2, VERB_ANY, 0, cat_config },
2473 : : { "unit-files", VERB_ANY, VERB_ANY, 0, do_unit_files },
2474 : : { "unit-paths", 1, 1, 0, dump_unit_paths },
2475 : : { "exit-status", VERB_ANY, VERB_ANY, 0, dump_exit_status },
2476 : : { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
2477 : : { "condition", 2, VERB_ANY, 0, do_condition },
2478 : : { "verify", 2, VERB_ANY, 0, do_verify },
2479 : : { "calendar", 2, VERB_ANY, 0, test_calendar },
2480 : : { "timestamp", 2, VERB_ANY, 0, test_timestamp },
2481 : : { "timespan", 2, VERB_ANY, 0, dump_timespan },
2482 : : { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
2483 : : { "security", VERB_ANY, VERB_ANY, 0, do_security },
2484 : : {}
2485 : : };
2486 : :
2487 : : int r;
2488 : :
2489 : 16 : setlocale(LC_ALL, "");
2490 : 16 : setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
2491 : :
2492 : 16 : log_show_color(true);
2493 : 16 : log_parse_environment();
2494 : 16 : log_open();
2495 : :
2496 : 16 : r = parse_argv(argc, argv);
2497 [ + - ]: 16 : if (r <= 0)
2498 : 16 : return r;
2499 : :
2500 : 0 : return dispatch_verb(argc, argv, verbs, NULL);
2501 : : }
2502 : :
2503 : 16 : DEFINE_MAIN_FUNCTION(run);
|