1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010-2013 Lennart Poettering
6 Copyright 2013 Simon Peeters
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include "alloc-util.h"
30 #include "analyze-verify.h"
31 #include "bus-error.h"
32 #include "bus-unit-util.h"
34 #include "calendarspec.h"
35 #include "glob-util.h"
37 #include "locale-util.h"
40 #include "parse-util.h"
42 #include "seccomp-util.h"
47 #include "terminal-util.h"
48 #include "unit-name.h"
51 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
52 #define SCALE_Y (20.0)
54 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
56 #define svg(...) printf(__VA_ARGS__)
58 #define svg_bar(class, x1, x2, y) \
59 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
61 SCALE_X * (x1), SCALE_Y * (y), \
62 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
64 #define svg_text(b, x, y, format, ...) \
66 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
67 svg(format, ## __VA_ARGS__); \
76 static char** arg_dot_from_patterns
= NULL
;
77 static char** arg_dot_to_patterns
= NULL
;
78 static usec_t arg_fuzz
= 0;
79 static bool arg_no_pager
= false;
80 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
81 static char *arg_host
= NULL
;
82 static bool arg_user
= false;
83 static bool arg_man
= true;
84 static bool arg_generators
= false;
90 usec_t kernel_done_time
;
92 usec_t userspace_time
;
94 usec_t security_start_time
;
95 usec_t security_finish_time
;
96 usec_t generators_start_time
;
97 usec_t generators_finish_time
;
98 usec_t unitsload_start_time
;
99 usec_t unitsload_finish_time
;
102 * If we're analyzing the user instance, all timestamps will be offset
103 * by its own start-up timestamp, which may be arbitrarily big.
104 * With "plot", this causes arbitrarily wide output SVG files which almost
105 * completely consist of empty space. Thus we cancel out this offset.
107 * This offset is subtracted from times above by acquire_boot_times(),
108 * but it still needs to be subtracted from unit-specific timestamps
109 * (so it is stored here for reference).
111 usec_t reverse_offset
;
126 char *kernel_release
;
127 char *kernel_version
;
128 char *os_pretty_name
;
129 char *virtualization
;
133 static int bus_get_uint64_property(sd_bus
*bus
, const char *path
, const char *interface
, const char *property
, uint64_t *val
) {
134 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
143 r
= sd_bus_get_property_trivial(
145 "org.freedesktop.systemd1",
153 log_error("Failed to parse reply: %s", bus_error_message(&error
, -r
));
160 static int bus_get_unit_property_strv(sd_bus
*bus
, const char *path
, const char *property
, char ***strv
) {
161 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
169 r
= sd_bus_get_property_strv(
171 "org.freedesktop.systemd1",
173 "org.freedesktop.systemd1.Unit",
178 log_error("Failed to get unit property %s: %s", property
, bus_error_message(&error
, -r
));
185 static int compare_unit_time(const void *a
, const void *b
) {
186 return compare(((struct unit_times
*)b
)->time
,
187 ((struct unit_times
*)a
)->time
);
190 static int compare_unit_start(const void *a
, const void *b
) {
191 return compare(((struct unit_times
*)a
)->activating
,
192 ((struct unit_times
*)b
)->activating
);
195 static void free_unit_times(struct unit_times
*t
, unsigned n
) {
196 struct unit_times
*p
;
198 for (p
= t
; p
< t
+ n
; p
++)
204 static void subtract_timestamp(usec_t
*a
, usec_t b
) {
213 static int acquire_boot_times(sd_bus
*bus
, struct boot_times
**bt
) {
214 static struct boot_times times
;
215 static bool cached
= false;
220 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
222 if (bus_get_uint64_property(bus
,
223 "/org/freedesktop/systemd1",
224 "org.freedesktop.systemd1.Manager",
225 "FirmwareTimestampMonotonic",
226 ×
.firmware_time
) < 0 ||
227 bus_get_uint64_property(bus
,
228 "/org/freedesktop/systemd1",
229 "org.freedesktop.systemd1.Manager",
230 "LoaderTimestampMonotonic",
231 ×
.loader_time
) < 0 ||
232 bus_get_uint64_property(bus
,
233 "/org/freedesktop/systemd1",
234 "org.freedesktop.systemd1.Manager",
236 ×
.kernel_time
) < 0 ||
237 bus_get_uint64_property(bus
,
238 "/org/freedesktop/systemd1",
239 "org.freedesktop.systemd1.Manager",
240 "InitRDTimestampMonotonic",
241 ×
.initrd_time
) < 0 ||
242 bus_get_uint64_property(bus
,
243 "/org/freedesktop/systemd1",
244 "org.freedesktop.systemd1.Manager",
245 "UserspaceTimestampMonotonic",
246 ×
.userspace_time
) < 0 ||
247 bus_get_uint64_property(bus
,
248 "/org/freedesktop/systemd1",
249 "org.freedesktop.systemd1.Manager",
250 "FinishTimestampMonotonic",
251 ×
.finish_time
) < 0 ||
252 bus_get_uint64_property(bus
,
253 "/org/freedesktop/systemd1",
254 "org.freedesktop.systemd1.Manager",
255 "SecurityStartTimestampMonotonic",
256 ×
.security_start_time
) < 0 ||
257 bus_get_uint64_property(bus
,
258 "/org/freedesktop/systemd1",
259 "org.freedesktop.systemd1.Manager",
260 "SecurityFinishTimestampMonotonic",
261 ×
.security_finish_time
) < 0 ||
262 bus_get_uint64_property(bus
,
263 "/org/freedesktop/systemd1",
264 "org.freedesktop.systemd1.Manager",
265 "GeneratorsStartTimestampMonotonic",
266 ×
.generators_start_time
) < 0 ||
267 bus_get_uint64_property(bus
,
268 "/org/freedesktop/systemd1",
269 "org.freedesktop.systemd1.Manager",
270 "GeneratorsFinishTimestampMonotonic",
271 ×
.generators_finish_time
) < 0 ||
272 bus_get_uint64_property(bus
,
273 "/org/freedesktop/systemd1",
274 "org.freedesktop.systemd1.Manager",
275 "UnitsLoadStartTimestampMonotonic",
276 ×
.unitsload_start_time
) < 0 ||
277 bus_get_uint64_property(bus
,
278 "/org/freedesktop/systemd1",
279 "org.freedesktop.systemd1.Manager",
280 "UnitsLoadFinishTimestampMonotonic",
281 ×
.unitsload_finish_time
) < 0)
284 if (times
.finish_time
<= 0) {
285 log_error("Bootup is not yet finished. Please try again later.");
291 * User-instance-specific timestamps processing
292 * (see comment to reverse_offset in struct boot_times).
294 times
.reverse_offset
= times
.userspace_time
;
296 times
.firmware_time
= times
.loader_time
= times
.kernel_time
= times
.initrd_time
= times
.userspace_time
= 0;
297 subtract_timestamp(×
.finish_time
, times
.reverse_offset
);
299 subtract_timestamp(×
.security_start_time
, times
.reverse_offset
);
300 subtract_timestamp(×
.security_finish_time
, times
.reverse_offset
);
302 subtract_timestamp(×
.generators_start_time
, times
.reverse_offset
);
303 subtract_timestamp(×
.generators_finish_time
, times
.reverse_offset
);
305 subtract_timestamp(×
.unitsload_start_time
, times
.reverse_offset
);
306 subtract_timestamp(×
.unitsload_finish_time
, times
.reverse_offset
);
308 if (times
.initrd_time
)
309 times
.kernel_done_time
= times
.initrd_time
;
311 times
.kernel_done_time
= times
.userspace_time
;
321 static void free_host_info(struct host_info
*hi
) {
327 free(hi
->kernel_name
);
328 free(hi
->kernel_release
);
329 free(hi
->kernel_version
);
330 free(hi
->os_pretty_name
);
331 free(hi
->virtualization
);
332 free(hi
->architecture
);
336 DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info
*, free_host_info
);
338 static int acquire_time_data(sd_bus
*bus
, struct unit_times
**out
) {
339 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
340 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
342 struct boot_times
*boot_times
= NULL
;
343 struct unit_times
*unit_times
= NULL
;
347 r
= acquire_boot_times(bus
, &boot_times
);
351 r
= sd_bus_call_method(
353 "org.freedesktop.systemd1",
354 "/org/freedesktop/systemd1",
355 "org.freedesktop.systemd1.Manager",
360 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
364 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
366 bus_log_parse_error(r
);
370 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
371 struct unit_times
*t
;
373 if (!GREEDY_REALLOC(unit_times
, size
, c
+1)) {
381 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
383 if (bus_get_uint64_property(bus
, u
.unit_path
,
384 "org.freedesktop.systemd1.Unit",
385 "InactiveExitTimestampMonotonic",
386 &t
->activating
) < 0 ||
387 bus_get_uint64_property(bus
, u
.unit_path
,
388 "org.freedesktop.systemd1.Unit",
389 "ActiveEnterTimestampMonotonic",
390 &t
->activated
) < 0 ||
391 bus_get_uint64_property(bus
, u
.unit_path
,
392 "org.freedesktop.systemd1.Unit",
393 "ActiveExitTimestampMonotonic",
394 &t
->deactivating
) < 0 ||
395 bus_get_uint64_property(bus
, u
.unit_path
,
396 "org.freedesktop.systemd1.Unit",
397 "InactiveEnterTimestampMonotonic",
398 &t
->deactivated
) < 0) {
403 subtract_timestamp(&t
->activating
, boot_times
->reverse_offset
);
404 subtract_timestamp(&t
->activated
, boot_times
->reverse_offset
);
405 subtract_timestamp(&t
->deactivating
, boot_times
->reverse_offset
);
406 subtract_timestamp(&t
->deactivated
, boot_times
->reverse_offset
);
408 if (t
->activated
>= t
->activating
)
409 t
->time
= t
->activated
- t
->activating
;
410 else if (t
->deactivated
>= t
->activating
)
411 t
->time
= t
->deactivated
- t
->activating
;
415 if (t
->activating
== 0)
418 t
->name
= strdup(u
.id
);
419 if (t
->name
== NULL
) {
426 bus_log_parse_error(r
);
435 free_unit_times(unit_times
, (unsigned) c
);
439 static int acquire_host_info(sd_bus
*bus
, struct host_info
**hi
) {
440 static const struct bus_properties_map hostname_map
[] = {
441 { "Hostname", "s", NULL
, offsetof(struct host_info
, hostname
) },
442 { "KernelName", "s", NULL
, offsetof(struct host_info
, kernel_name
) },
443 { "KernelRelease", "s", NULL
, offsetof(struct host_info
, kernel_release
) },
444 { "KernelVersion", "s", NULL
, offsetof(struct host_info
, kernel_version
) },
445 { "OperatingSystemPrettyName", "s", NULL
, offsetof(struct host_info
, os_pretty_name
) },
449 static const struct bus_properties_map manager_map
[] = {
450 { "Virtualization", "s", NULL
, offsetof(struct host_info
, virtualization
) },
451 { "Architecture", "s", NULL
, offsetof(struct host_info
, architecture
) },
455 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
456 _cleanup_(free_host_infop
) struct host_info
*host
;
459 host
= new0(struct host_info
, 1);
463 r
= bus_map_all_properties(bus
,
464 "org.freedesktop.hostname1",
465 "/org/freedesktop/hostname1",
470 log_debug_errno(r
, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error
, r
));
472 r
= bus_map_all_properties(bus
,
473 "org.freedesktop.systemd1",
474 "/org/freedesktop/systemd1",
479 return log_error_errno(r
, "Failed to get host information from systemd: %s", bus_error_message(&error
, r
));
487 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];
495 r
= acquire_boot_times(bus
, &t
);
502 size
= strpcpyf(&ptr
, size
, "Startup finished in ");
503 if (t
->firmware_time
)
504 size
= strpcpyf(&ptr
, size
, "%s (firmware) + ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
- t
->loader_time
, USEC_PER_MSEC
));
506 size
= strpcpyf(&ptr
, size
, "%s (loader) + ", format_timespan(ts
, sizeof(ts
), t
->loader_time
, USEC_PER_MSEC
));
508 size
= strpcpyf(&ptr
, size
, "%s (kernel) + ", format_timespan(ts
, sizeof(ts
), t
->kernel_done_time
, USEC_PER_MSEC
));
509 if (t
->initrd_time
> 0)
510 size
= strpcpyf(&ptr
, size
, "%s (initrd) + ", format_timespan(ts
, sizeof(ts
), t
->userspace_time
- t
->initrd_time
, USEC_PER_MSEC
));
512 size
= strpcpyf(&ptr
, size
, "%s (userspace) ", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
513 strpcpyf(&ptr
, size
, "= %s", format_timespan(ts
, sizeof(ts
), t
->firmware_time
+ t
->finish_time
, USEC_PER_MSEC
));
523 static void svg_graph_box(double height
, double begin
, double end
) {
526 /* outside box, fill */
527 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
528 SCALE_X
* (end
- begin
), SCALE_Y
* height
);
530 for (i
= ((long long) (begin
/ 100000)) * 100000; i
<= end
; i
+=100000) {
531 /* lines for each second */
532 if (i
% 5000000 == 0)
533 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
534 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
535 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
536 else if (i
% 1000000 == 0)
537 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
538 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
539 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
541 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
542 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
);
546 static int analyze_plot(sd_bus
*bus
) {
547 _cleanup_(free_host_infop
) struct host_info
*host
= NULL
;
548 struct unit_times
*times
;
549 struct boot_times
*boot
;
552 _cleanup_free_
char *pretty_times
= NULL
;
553 struct unit_times
*u
;
555 n
= acquire_boot_times(bus
, &boot
);
559 n
= pretty_boot_time(bus
, &pretty_times
);
563 n
= acquire_host_info(bus
, &host
);
567 n
= acquire_time_data(bus
, ×
);
571 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_start
);
573 width
= SCALE_X
* (boot
->firmware_time
+ boot
->finish_time
);
577 if (boot
->firmware_time
> boot
->loader_time
)
579 if (boot
->loader_time
) {
584 if (boot
->initrd_time
)
586 if (boot
->kernel_time
)
589 for (u
= times
; u
< times
+ n
; u
++) {
590 double text_start
, text_width
;
592 if (u
->activating
< boot
->userspace_time
||
593 u
->activating
> boot
->finish_time
) {
594 u
->name
= mfree(u
->name
);
598 /* If the text cannot fit on the left side then
599 * increase the svg width so it fits on the right.
600 * TODO: calculate the text width more accurately */
601 text_width
= 8.0 * strlen(u
->name
);
602 text_start
= (boot
->firmware_time
+ u
->activating
) * SCALE_X
;
603 if (text_width
> text_start
&& text_width
+ text_start
> width
)
604 width
= text_width
+ text_start
;
606 if (u
->deactivated
> u
->activating
&& u
->deactivated
<= boot
->finish_time
607 && u
->activated
== 0 && u
->deactivating
== 0)
608 u
->activated
= u
->deactivating
= u
->deactivated
;
609 if (u
->activated
< u
->activating
|| u
->activated
> boot
->finish_time
)
610 u
->activated
= boot
->finish_time
;
611 if (u
->deactivating
< u
->activated
|| u
->activated
> boot
->finish_time
)
612 u
->deactivating
= boot
->finish_time
;
613 if (u
->deactivated
< u
->deactivating
|| u
->deactivated
> boot
->finish_time
)
614 u
->deactivated
= boot
->finish_time
;
618 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
619 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
620 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
622 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
623 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
624 80.0 + width
, 150.0 + (m
* SCALE_Y
) +
625 5 * SCALE_Y
/* legend */);
627 /* write some basic info as a comment, including some help */
628 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
629 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
630 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
631 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
632 "<!-- point your browser to this file. -->\n\n"
633 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION
);
636 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
637 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
638 " rect.background { fill: rgb(255,255,255); }\n"
639 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
640 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
641 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
642 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
643 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
644 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
645 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
646 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
647 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
648 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
649 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
650 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
651 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
653 " line.sec5 { stroke-width: 2; }\n"
654 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
655 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
656 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
657 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
658 " text.sec { font-size: 10px; }\n"
659 " ]]>\n </style>\n</defs>\n\n");
661 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
662 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times
);
663 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
664 isempty(host
->os_pretty_name
) ? "Linux" : host
->os_pretty_name
,
665 strempty(host
->hostname
),
666 strempty(host
->kernel_name
),
667 strempty(host
->kernel_release
),
668 strempty(host
->kernel_version
),
669 strempty(host
->architecture
),
670 strempty(host
->virtualization
));
672 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X
* boot
->firmware_time
));
673 svg_graph_box(m
, -(double) boot
->firmware_time
, boot
->finish_time
);
675 if (boot
->firmware_time
) {
676 svg_bar("firmware", -(double) boot
->firmware_time
, -(double) boot
->loader_time
, y
);
677 svg_text(true, -(double) boot
->firmware_time
, y
, "firmware");
680 if (boot
->loader_time
) {
681 svg_bar("loader", -(double) boot
->loader_time
, 0, y
);
682 svg_text(true, -(double) boot
->loader_time
, y
, "loader");
685 if (boot
->kernel_time
) {
686 svg_bar("kernel", 0, boot
->kernel_done_time
, y
);
687 svg_text(true, 0, y
, "kernel");
690 if (boot
->initrd_time
) {
691 svg_bar("initrd", boot
->initrd_time
, boot
->userspace_time
, y
);
692 svg_text(true, boot
->initrd_time
, y
, "initrd");
695 svg_bar("active", boot
->userspace_time
, boot
->finish_time
, y
);
696 svg_bar("security", boot
->security_start_time
, boot
->security_finish_time
, y
);
697 svg_bar("generators", boot
->generators_start_time
, boot
->generators_finish_time
, y
);
698 svg_bar("unitsload", boot
->unitsload_start_time
, boot
->unitsload_finish_time
, y
);
699 svg_text(true, boot
->userspace_time
, y
, "systemd");
702 for (u
= times
; u
< times
+ n
; u
++) {
703 char ts
[FORMAT_TIMESPAN_MAX
];
709 svg_bar("activating", u
->activating
, u
->activated
, y
);
710 svg_bar("active", u
->activated
, u
->deactivating
, y
);
711 svg_bar("deactivating", u
->deactivating
, u
->deactivated
, y
);
713 /* place the text on the left if we have passed the half of the svg width */
714 b
= u
->activating
* SCALE_X
< width
/ 2;
716 svg_text(b
, u
->activating
, y
, "%s (%s)",
717 u
->name
, format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
));
719 svg_text(b
, u
->activating
, y
, "%s", u
->name
);
726 svg("<g transform=\"translate(20,100)\">\n");
728 svg_bar("activating", 0, 300000, y
);
729 svg_text(true, 400000, y
, "Activating");
731 svg_bar("active", 0, 300000, y
);
732 svg_text(true, 400000, y
, "Active");
734 svg_bar("deactivating", 0, 300000, y
);
735 svg_text(true, 400000, y
, "Deactivating");
737 svg_bar("security", 0, 300000, y
);
738 svg_text(true, 400000, y
, "Setting up security module");
740 svg_bar("generators", 0, 300000, y
);
741 svg_text(true, 400000, y
, "Generators");
743 svg_bar("unitsload", 0, 300000, y
);
744 svg_text(true, 400000, y
, "Loading unit files");
751 free_unit_times(times
, (unsigned) n
);
757 static int list_dependencies_print(const char *name
, unsigned int level
, unsigned int branches
,
758 bool last
, struct unit_times
*times
, struct boot_times
*boot
) {
760 char ts
[FORMAT_TIMESPAN_MAX
], ts2
[FORMAT_TIMESPAN_MAX
];
762 for (i
= level
; i
!= 0; i
--)
763 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? TREE_VERTICAL
: TREE_SPACE
));
765 printf("%s", special_glyph(last
? TREE_RIGHT
: TREE_BRANCH
));
769 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
770 format_timespan(ts
, sizeof(ts
), times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
771 format_timespan(ts2
, sizeof(ts2
), times
->time
, USEC_PER_MSEC
), ansi_normal());
772 else if (times
->activated
> boot
->userspace_time
)
773 printf("%s @%s", name
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
783 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
784 _cleanup_free_
char *path
= NULL
;
790 path
= unit_dbus_path_from_name(name
);
794 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
797 static Hashmap
*unit_times_hashmap
;
799 static int list_dependencies_compare(const void *_a
, const void *_b
) {
800 const char **a
= (const char**) _a
, **b
= (const char**) _b
;
801 usec_t usa
= 0, usb
= 0;
802 struct unit_times
*times
;
804 times
= hashmap_get(unit_times_hashmap
, *a
);
806 usa
= times
->activated
;
807 times
= hashmap_get(unit_times_hashmap
, *b
);
809 usb
= times
->activated
;
814 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned int level
, char ***units
,
815 unsigned int branches
) {
816 _cleanup_strv_free_
char **deps
= NULL
;
819 usec_t service_longest
= 0;
821 struct unit_times
*times
;
822 struct boot_times
*boot
;
824 if (strv_extend(units
, name
))
827 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
831 qsort_safe(deps
, strv_length(deps
), sizeof (char*), list_dependencies_compare
);
833 r
= acquire_boot_times(bus
, &boot
);
837 STRV_FOREACH(c
, deps
) {
838 times
= hashmap_get(unit_times_hashmap
, *c
);
841 && times
->activated
<= boot
->finish_time
842 && (times
->activated
>= service_longest
843 || service_longest
== 0)) {
844 service_longest
= times
->activated
;
849 if (service_longest
== 0 )
852 STRV_FOREACH(c
, deps
) {
853 times
= hashmap_get(unit_times_hashmap
, *c
);
854 if (times
&& times
->activated
&& times
->activated
<= boot
->finish_time
&& (service_longest
- times
->activated
) <= arg_fuzz
)
861 STRV_FOREACH(c
, deps
) {
862 times
= hashmap_get(unit_times_hashmap
, *c
);
865 || times
->activated
> boot
->finish_time
866 || service_longest
- times
->activated
> arg_fuzz
)
871 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
875 if (strv_contains(*units
, *c
)) {
876 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
883 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
,
884 (branches
<< 1) | (to_print
? 1 : 0));
894 static int list_dependencies(sd_bus
*bus
, const char *name
) {
895 _cleanup_strv_free_
char **units
= NULL
;
896 char ts
[FORMAT_TIMESPAN_MAX
];
897 struct unit_times
*times
;
900 _cleanup_free_
char *path
= NULL
;
901 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
902 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
903 struct boot_times
*boot
;
907 path
= unit_dbus_path_from_name(name
);
911 r
= sd_bus_get_property(
913 "org.freedesktop.systemd1",
915 "org.freedesktop.systemd1.Unit",
921 log_error("Failed to get ID: %s", bus_error_message(&error
, -r
));
925 r
= sd_bus_message_read(reply
, "s", &id
);
927 return bus_log_parse_error(r
);
929 times
= hashmap_get(unit_times_hashmap
, id
);
931 r
= acquire_boot_times(bus
, &boot
);
937 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
938 format_timespan(ts
, sizeof(ts
), times
->time
, USEC_PER_MSEC
), ansi_normal());
939 else if (times
->activated
> boot
->userspace_time
)
940 printf("%s @%s\n", id
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
945 return list_dependencies_one(bus
, name
, 0, &units
, 0);
948 static int analyze_critical_chain(sd_bus
*bus
, char *names
[]) {
949 struct unit_times
*times
;
954 n
= acquire_time_data(bus
, ×
);
958 h
= hashmap_new(&string_hash_ops
);
962 for (i
= 0; i
< (unsigned)n
; i
++) {
963 r
= hashmap_put(h
, times
[i
].name
, ×
[i
]);
967 unit_times_hashmap
= h
;
969 pager_open(arg_no_pager
, false);
971 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
972 "The time the unit takes to start is printed after the \"+\" character.\n");
974 if (!strv_isempty(names
)) {
976 STRV_FOREACH(name
, names
)
977 list_dependencies(bus
, *name
);
979 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);
982 free_unit_times(times
, (unsigned) n
);
986 static int analyze_blame(sd_bus
*bus
) {
987 struct unit_times
*times
;
991 n
= acquire_time_data(bus
, ×
);
995 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_time
);
997 pager_open(arg_no_pager
, false);
999 for (i
= 0; i
< (unsigned) n
; i
++) {
1000 char ts
[FORMAT_TIMESPAN_MAX
];
1002 if (times
[i
].time
> 0)
1003 printf("%16s %s\n", format_timespan(ts
, sizeof(ts
), times
[i
].time
, USEC_PER_MSEC
), times
[i
].name
);
1006 free_unit_times(times
, (unsigned) n
);
1010 static int analyze_time(sd_bus
*bus
) {
1011 _cleanup_free_
char *buf
= NULL
;
1014 r
= pretty_boot_time(bus
, &buf
);
1022 static int graph_one_property(sd_bus
*bus
, const UnitInfo
*u
, const char* prop
, const char *color
, char* patterns
[], char* from_patterns
[], char* to_patterns
[]) {
1023 _cleanup_strv_free_
char **units
= NULL
;
1026 bool match_patterns
;
1032 match_patterns
= strv_fnmatch(patterns
, u
->id
, 0);
1034 if (!strv_isempty(from_patterns
) &&
1036 !strv_fnmatch(from_patterns
, u
->id
, 0))
1039 r
= bus_get_unit_property_strv(bus
, u
->unit_path
, prop
, &units
);
1043 STRV_FOREACH(unit
, units
) {
1044 bool match_patterns2
;
1046 match_patterns2
= strv_fnmatch(patterns
, *unit
, 0);
1048 if (!strv_isempty(to_patterns
) &&
1050 !strv_fnmatch(to_patterns
, *unit
, 0))
1053 if (!strv_isempty(patterns
) && !match_patterns
&& !match_patterns2
)
1056 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u
->id
, *unit
, color
);
1062 static int graph_one(sd_bus
*bus
, const UnitInfo
*u
, char *patterns
[], char *from_patterns
[], char *to_patterns
[]) {
1068 if (IN_SET(arg_dot
, DEP_ORDER
, DEP_ALL
)) {
1069 r
= graph_one_property(bus
, u
, "After", "green", patterns
, from_patterns
, to_patterns
);
1074 if (IN_SET(arg_dot
, DEP_REQUIRE
, DEP_ALL
)) {
1075 r
= graph_one_property(bus
, u
, "Requires", "black", patterns
, from_patterns
, to_patterns
);
1078 r
= graph_one_property(bus
, u
, "Requisite", "darkblue", patterns
, from_patterns
, to_patterns
);
1081 r
= graph_one_property(bus
, u
, "Wants", "grey66", patterns
, from_patterns
, to_patterns
);
1084 r
= graph_one_property(bus
, u
, "Conflicts", "red", patterns
, from_patterns
, to_patterns
);
1092 static int expand_patterns(sd_bus
*bus
, char **patterns
, char ***ret
) {
1093 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1097 STRV_FOREACH(pattern
, patterns
) {
1098 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1099 _cleanup_free_
char *unit
= NULL
, *unit_id
= NULL
;
1101 if (strv_extend(&expanded_patterns
, *pattern
) < 0)
1104 if (string_is_glob(*pattern
))
1107 unit
= unit_dbus_path_from_name(*pattern
);
1111 r
= sd_bus_get_property_string(
1113 "org.freedesktop.systemd1",
1115 "org.freedesktop.systemd1.Unit",
1120 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
1122 if (!streq(*pattern
, unit_id
)) {
1123 if (strv_extend(&expanded_patterns
, unit_id
) < 0)
1128 *ret
= expanded_patterns
;
1129 expanded_patterns
= NULL
; /* do not free */
1134 static int dot(sd_bus
*bus
, char* patterns
[]) {
1135 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1136 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1137 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1138 _cleanup_strv_free_
char **expanded_from_patterns
= NULL
;
1139 _cleanup_strv_free_
char **expanded_to_patterns
= NULL
;
1143 r
= expand_patterns(bus
, patterns
, &expanded_patterns
);
1147 r
= expand_patterns(bus
, arg_dot_from_patterns
, &expanded_from_patterns
);
1151 r
= expand_patterns(bus
, arg_dot_to_patterns
, &expanded_to_patterns
);
1155 r
= sd_bus_call_method(
1157 "org.freedesktop.systemd1",
1158 "/org/freedesktop/systemd1",
1159 "org.freedesktop.systemd1.Manager",
1165 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
1169 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1171 return bus_log_parse_error(r
);
1173 printf("digraph systemd {\n");
1175 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
1177 r
= graph_one(bus
, &u
, expanded_patterns
, expanded_from_patterns
, expanded_to_patterns
);
1182 return bus_log_parse_error(r
);
1186 log_info(" Color legend: black = Requires\n"
1187 " dark blue = Requisite\n"
1188 " dark grey = Wants\n"
1189 " red = Conflicts\n"
1190 " green = After\n");
1193 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1194 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1199 static int dump(sd_bus
*bus
, char **args
) {
1200 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1201 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1202 const char *text
= NULL
;
1205 if (!strv_isempty(args
)) {
1206 log_error("Too many arguments.");
1210 pager_open(arg_no_pager
, false);
1212 r
= sd_bus_call_method(
1214 "org.freedesktop.systemd1",
1215 "/org/freedesktop/systemd1",
1216 "org.freedesktop.systemd1.Manager",
1222 return log_error_errno(r
, "Failed issue method call: %s", bus_error_message(&error
, r
));
1224 r
= sd_bus_message_read(reply
, "s", &text
);
1226 return bus_log_parse_error(r
);
1228 fputs(text
, stdout
);
1232 static int set_log_level(sd_bus
*bus
, char **args
) {
1233 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1239 if (strv_length(args
) != 1) {
1240 log_error("This command expects one argument only.");
1244 r
= sd_bus_set_property(
1246 "org.freedesktop.systemd1",
1247 "/org/freedesktop/systemd1",
1248 "org.freedesktop.systemd1.Manager",
1254 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1259 static int get_log_level(sd_bus
*bus
, char **args
) {
1260 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1262 _cleanup_free_
char *level
= NULL
;
1267 if (!strv_isempty(args
)) {
1268 log_error("Too many arguments.");
1272 r
= sd_bus_get_property_string(
1274 "org.freedesktop.systemd1",
1275 "/org/freedesktop/systemd1",
1276 "org.freedesktop.systemd1.Manager",
1281 return log_error_errno(r
, "Failed to get log level: %s", bus_error_message(&error
, r
));
1287 static int set_log_target(sd_bus
*bus
, char **args
) {
1288 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1294 if (strv_length(args
) != 1) {
1295 log_error("This command expects one argument only.");
1299 r
= sd_bus_set_property(
1301 "org.freedesktop.systemd1",
1302 "/org/freedesktop/systemd1",
1303 "org.freedesktop.systemd1.Manager",
1309 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1314 static int get_log_target(sd_bus
*bus
, char **args
) {
1315 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1317 _cleanup_free_
char *target
= NULL
;
1322 if (!strv_isempty(args
)) {
1323 log_error("Too many arguments.");
1327 r
= sd_bus_get_property_string(
1329 "org.freedesktop.systemd1",
1330 "/org/freedesktop/systemd1",
1331 "org.freedesktop.systemd1.Manager",
1336 return log_error_errno(r
, "Failed to get log target: %s", bus_error_message(&error
, r
));
1343 static void dump_syscall_filter(const SyscallFilterSet
*set
) {
1344 const char *syscall
;
1346 printf("%s\n", set
->name
);
1347 printf(" # %s\n", set
->help
);
1348 NULSTR_FOREACH(syscall
, set
->value
)
1349 printf(" %s\n", syscall
);
1352 static int dump_syscall_filters(char** names
) {
1355 pager_open(arg_no_pager
, false);
1357 if (strv_isempty(names
)) {
1360 for (i
= 0; i
< _SYSCALL_FILTER_SET_MAX
; i
++) {
1363 dump_syscall_filter(syscall_filter_sets
+ i
);
1369 STRV_FOREACH(name
, names
) {
1370 const SyscallFilterSet
*set
;
1375 set
= syscall_filter_set_find(*name
);
1377 /* make sure the error appears below normal output */
1380 log_error("Filter set \"%s\" not found.", *name
);
1384 dump_syscall_filter(set
);
1393 static int dump_syscall_filters(char** names
) {
1394 log_error("Not compiled with syscall filters, sorry.");
1399 static int test_calendar(char **args
) {
1404 if (strv_isempty(args
)) {
1405 log_error("Expected at least one calendar specification string as argument.");
1409 n
= now(CLOCK_REALTIME
);
1411 STRV_FOREACH(p
, args
) {
1412 _cleanup_(calendar_spec_freep
) CalendarSpec
*spec
= NULL
;
1413 _cleanup_free_
char *t
= NULL
;
1416 r
= calendar_spec_from_string(*p
, &spec
);
1418 ret
= log_error_errno(r
, "Failed to parse calendar specification '%s': %m", *p
);
1422 r
= calendar_spec_normalize(spec
);
1424 ret
= log_error_errno(r
, "Failed to normalize calendar specification '%s': %m", *p
);
1428 r
= calendar_spec_to_string(spec
, &t
);
1430 ret
= log_error_errno(r
, "Failed to fomat calendar specification '%s': %m", *p
);
1435 printf(" Original form: %s\n", *p
);
1437 printf("Normalized form: %s\n", t
);
1439 r
= calendar_spec_next_usec(spec
, n
, &next
);
1441 printf(" Next elapse: never\n");
1443 ret
= log_error_errno(r
, "Failed to determine next elapse for '%s': %m", *p
);
1446 char buffer
[CONST_MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_TIMESTAMP_RELATIVE_MAX
)];
1448 printf(" Next elapse: %s\n", format_timestamp(buffer
, sizeof(buffer
), next
));
1450 if (!in_utc_timezone())
1451 printf(" (in UTC): %s\n", format_timestamp_utc(buffer
, sizeof(buffer
), next
));
1453 printf(" From now: %s\n", format_timestamp_relative(buffer
, sizeof(buffer
), next
));
1463 static void help(void) {
1465 pager_open(arg_no_pager
, false);
1467 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1468 "Profile systemd, show unit dependencies, check unit files.\n\n"
1469 " -h --help Show this help\n"
1470 " --version Show package version\n"
1471 " --no-pager Do not pipe output into a pager\n"
1472 " --system Operate on system systemd instance\n"
1473 " --user Operate on user systemd instance\n"
1474 " -H --host=[USER@]HOST Operate on remote host\n"
1475 " -M --machine=CONTAINER Operate on local container\n"
1476 " --order Show only order in the graph\n"
1477 " --require Show only requirement in the graph\n"
1478 " --from-pattern=GLOB Show only origins in the graph\n"
1479 " --to-pattern=GLOB Show only destinations in the graph\n"
1480 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1481 " earlier than the latest in the branch\n"
1482 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1483 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
1485 " time Print time spent in the kernel\n"
1486 " blame Print list of running units ordered by time to init\n"
1487 " critical-chain Print a tree of the time critical chain of units\n"
1488 " plot Output SVG graphic showing service initialization\n"
1489 " dot Output dependency graph in man:dot(1) format\n"
1490 " set-log-level LEVEL Set logging threshold for manager\n"
1491 " set-log-target TARGET Set logging target for manager\n"
1492 " get-log-level Get logging threshold for manager\n"
1493 " get-log-target Get logging target for manager\n"
1494 " dump Output state serialization of service manager\n"
1495 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1496 " verify FILE... Check unit files for correctness\n"
1497 " calendar SPEC... Validate repetitive calendar time events\n"
1498 , program_invocation_short_name
);
1500 /* When updating this list, including descriptions, apply
1501 * changes to shell-completion/bash/systemd-analyze and
1502 * shell-completion/zsh/_systemd-analyze too. */
1505 static int parse_argv(int argc
, char *argv
[]) {
1507 ARG_VERSION
= 0x100,
1512 ARG_DOT_FROM_PATTERN
,
1520 static const struct option options
[] = {
1521 { "help", no_argument
, NULL
, 'h' },
1522 { "version", no_argument
, NULL
, ARG_VERSION
},
1523 { "order", no_argument
, NULL
, ARG_ORDER
},
1524 { "require", no_argument
, NULL
, ARG_REQUIRE
},
1525 { "user", no_argument
, NULL
, ARG_USER
},
1526 { "system", no_argument
, NULL
, ARG_SYSTEM
},
1527 { "from-pattern", required_argument
, NULL
, ARG_DOT_FROM_PATTERN
},
1528 { "to-pattern", required_argument
, NULL
, ARG_DOT_TO_PATTERN
},
1529 { "fuzz", required_argument
, NULL
, ARG_FUZZ
},
1530 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1531 { "man", optional_argument
, NULL
, ARG_MAN
},
1532 { "generators", optional_argument
, NULL
, ARG_GENERATORS
},
1533 { "host", required_argument
, NULL
, 'H' },
1534 { "machine", required_argument
, NULL
, 'M' },
1543 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
1562 arg_dot
= DEP_ORDER
;
1566 arg_dot
= DEP_REQUIRE
;
1569 case ARG_DOT_FROM_PATTERN
:
1570 if (strv_extend(&arg_dot_from_patterns
, optarg
) < 0)
1575 case ARG_DOT_TO_PATTERN
:
1576 if (strv_extend(&arg_dot_to_patterns
, optarg
) < 0)
1582 r
= parse_sec(optarg
, &arg_fuzz
);
1588 arg_no_pager
= true;
1592 arg_transport
= BUS_TRANSPORT_REMOTE
;
1597 arg_transport
= BUS_TRANSPORT_MACHINE
;
1603 r
= parse_boolean(optarg
);
1605 log_error("Failed to parse --man= argument.");
1615 case ARG_GENERATORS
:
1617 r
= parse_boolean(optarg
);
1619 log_error("Failed to parse --generators= argument.");
1623 arg_generators
= !!r
;
1625 arg_generators
= true;
1633 assert_not_reached("Unhandled option code.");
1636 return 1; /* work to do */
1639 int main(int argc
, char *argv
[]) {
1642 setlocale(LC_ALL
, "");
1643 setlocale(LC_NUMERIC
, "C"); /* we want to format/parse floats in C style */
1644 log_parse_environment();
1647 r
= parse_argv(argc
, argv
);
1651 if (streq_ptr(argv
[optind
], "verify"))
1652 r
= verify_units(argv
+optind
+1,
1653 arg_user
? UNIT_FILE_USER
: UNIT_FILE_SYSTEM
,
1657 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1659 r
= bus_connect_transport_systemd(arg_transport
, arg_host
, arg_user
, &bus
);
1661 log_error_errno(r
, "Failed to create bus connection: %m");
1665 if (!argv
[optind
] || streq(argv
[optind
], "time"))
1666 r
= analyze_time(bus
);
1667 else if (streq(argv
[optind
], "blame"))
1668 r
= analyze_blame(bus
);
1669 else if (streq(argv
[optind
], "critical-chain"))
1670 r
= analyze_critical_chain(bus
, argv
+optind
+1);
1671 else if (streq(argv
[optind
], "plot"))
1672 r
= analyze_plot(bus
);
1673 else if (streq(argv
[optind
], "dot"))
1674 r
= dot(bus
, argv
+optind
+1);
1675 else if (streq(argv
[optind
], "dump"))
1676 r
= dump(bus
, argv
+optind
+1);
1677 else if (streq(argv
[optind
], "set-log-level"))
1678 r
= set_log_level(bus
, argv
+optind
+1);
1679 else if (streq(argv
[optind
], "get-log-level"))
1680 r
= get_log_level(bus
, argv
+optind
+1);
1681 else if (streq(argv
[optind
], "set-log-target"))
1682 r
= set_log_target(bus
, argv
+optind
+1);
1683 else if (streq(argv
[optind
], "get-log-target"))
1684 r
= get_log_target(bus
, argv
+optind
+1);
1685 else if (streq(argv
[optind
], "syscall-filter"))
1686 r
= dump_syscall_filters(argv
+optind
+1);
1687 else if (streq(argv
[optind
], "calendar"))
1688 r
= test_calendar(argv
+optind
+1);
1690 log_error("Unknown operation '%s'.", argv
[optind
]);
1696 strv_free(arg_dot_from_patterns
);
1697 strv_free(arg_dot_to_patterns
);
1699 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;