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