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
);
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];
494 usec_t activated_time
= USEC_INFINITY
;
495 _cleanup_free_
char* path
= NULL
, *unit_id
= NULL
;
496 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
498 r
= acquire_boot_times(bus
, &t
);
502 path
= unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET
);
506 r
= sd_bus_get_property_string(
508 "org.freedesktop.systemd1",
510 "org.freedesktop.systemd1.Unit",
515 log_error_errno(r
, "default.target doesn't seem to exist: %s", bus_error_message(&error
, r
));
519 r
= bus_get_uint64_property(bus
, path
,
520 "org.freedesktop.systemd1.Unit",
521 "ActiveEnterTimestampMonotonic",
524 log_info_errno(r
, "default.target seems not to be started. Continuing...");
525 activated_time
= USEC_INFINITY
;
531 size
= strpcpyf(&ptr
, size
, "Startup finished in ");
532 if (t
->firmware_time
)
533 size
= strpcpyf(&ptr
, size
, "%s (firmware) + ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
- t
->loader_time
, USEC_PER_MSEC
));
535 size
= strpcpyf(&ptr
, size
, "%s (loader) + ", format_timespan(ts
, sizeof(ts
), t
->loader_time
, USEC_PER_MSEC
));
537 size
= strpcpyf(&ptr
, size
, "%s (kernel) + ", format_timespan(ts
, sizeof(ts
), t
->kernel_done_time
, USEC_PER_MSEC
));
538 if (t
->initrd_time
> 0)
539 size
= strpcpyf(&ptr
, size
, "%s (initrd) + ", format_timespan(ts
, sizeof(ts
), t
->userspace_time
- t
->initrd_time
, USEC_PER_MSEC
));
541 size
= strpcpyf(&ptr
, size
, "%s (userspace) ", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
542 strpcpyf(&ptr
, size
, "= %s", format_timespan(ts
, sizeof(ts
), t
->firmware_time
+ t
->finish_time
, USEC_PER_MSEC
));
544 if (unit_id
&& activated_time
!= USEC_INFINITY
)
545 size
= strpcpyf(&ptr
, size
, "\n%s reached after %s in userspace", unit_id
, format_timespan(ts
, sizeof(ts
), activated_time
- t
->userspace_time
, USEC_PER_MSEC
));
555 static void svg_graph_box(double height
, double begin
, double end
) {
558 /* outside box, fill */
559 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
560 SCALE_X
* (end
- begin
), SCALE_Y
* height
);
562 for (i
= ((long long) (begin
/ 100000)) * 100000; i
<= end
; i
+=100000) {
563 /* lines for each second */
564 if (i
% 5000000 == 0)
565 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
566 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
567 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
568 else if (i
% 1000000 == 0)
569 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
570 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
571 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
573 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
574 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
);
578 static int analyze_plot(sd_bus
*bus
) {
579 _cleanup_(free_host_infop
) struct host_info
*host
= NULL
;
580 struct unit_times
*times
;
581 struct boot_times
*boot
;
584 _cleanup_free_
char *pretty_times
= NULL
;
585 struct unit_times
*u
;
587 n
= acquire_boot_times(bus
, &boot
);
591 n
= pretty_boot_time(bus
, &pretty_times
);
595 n
= acquire_host_info(bus
, &host
);
599 n
= acquire_time_data(bus
, ×
);
603 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_start
);
605 width
= SCALE_X
* (boot
->firmware_time
+ boot
->finish_time
);
609 if (boot
->firmware_time
> boot
->loader_time
)
611 if (boot
->loader_time
) {
616 if (boot
->initrd_time
)
618 if (boot
->kernel_time
)
621 for (u
= times
; u
< times
+ n
; u
++) {
622 double text_start
, text_width
;
624 if (u
->activating
< boot
->userspace_time
||
625 u
->activating
> boot
->finish_time
) {
626 u
->name
= mfree(u
->name
);
630 /* If the text cannot fit on the left side then
631 * increase the svg width so it fits on the right.
632 * TODO: calculate the text width more accurately */
633 text_width
= 8.0 * strlen(u
->name
);
634 text_start
= (boot
->firmware_time
+ u
->activating
) * SCALE_X
;
635 if (text_width
> text_start
&& text_width
+ text_start
> width
)
636 width
= text_width
+ text_start
;
638 if (u
->deactivated
> u
->activating
&& u
->deactivated
<= boot
->finish_time
639 && u
->activated
== 0 && u
->deactivating
== 0)
640 u
->activated
= u
->deactivating
= u
->deactivated
;
641 if (u
->activated
< u
->activating
|| u
->activated
> boot
->finish_time
)
642 u
->activated
= boot
->finish_time
;
643 if (u
->deactivating
< u
->activated
|| u
->activated
> boot
->finish_time
)
644 u
->deactivating
= boot
->finish_time
;
645 if (u
->deactivated
< u
->deactivating
|| u
->deactivated
> boot
->finish_time
)
646 u
->deactivated
= boot
->finish_time
;
650 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
651 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
652 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
654 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
655 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
656 80.0 + width
, 150.0 + (m
* SCALE_Y
) +
657 5 * SCALE_Y
/* legend */);
659 /* write some basic info as a comment, including some help */
660 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
661 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
662 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
663 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
664 "<!-- point your browser to this file. -->\n\n"
665 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION
);
668 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
669 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
670 " rect.background { fill: rgb(255,255,255); }\n"
671 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
672 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
673 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
674 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
675 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
676 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
677 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
678 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
679 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
680 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
681 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
682 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
683 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
685 " line.sec5 { stroke-width: 2; }\n"
686 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
687 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
688 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
689 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
690 " text.sec { font-size: 10px; }\n"
691 " ]]>\n </style>\n</defs>\n\n");
693 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
694 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times
);
695 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
696 isempty(host
->os_pretty_name
) ? "Linux" : host
->os_pretty_name
,
697 strempty(host
->hostname
),
698 strempty(host
->kernel_name
),
699 strempty(host
->kernel_release
),
700 strempty(host
->kernel_version
),
701 strempty(host
->architecture
),
702 strempty(host
->virtualization
));
704 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X
* boot
->firmware_time
));
705 svg_graph_box(m
, -(double) boot
->firmware_time
, boot
->finish_time
);
707 if (boot
->firmware_time
) {
708 svg_bar("firmware", -(double) boot
->firmware_time
, -(double) boot
->loader_time
, y
);
709 svg_text(true, -(double) boot
->firmware_time
, y
, "firmware");
712 if (boot
->loader_time
) {
713 svg_bar("loader", -(double) boot
->loader_time
, 0, y
);
714 svg_text(true, -(double) boot
->loader_time
, y
, "loader");
717 if (boot
->kernel_time
) {
718 svg_bar("kernel", 0, boot
->kernel_done_time
, y
);
719 svg_text(true, 0, y
, "kernel");
722 if (boot
->initrd_time
) {
723 svg_bar("initrd", boot
->initrd_time
, boot
->userspace_time
, y
);
724 svg_text(true, boot
->initrd_time
, y
, "initrd");
727 svg_bar("active", boot
->userspace_time
, boot
->finish_time
, y
);
728 svg_bar("security", boot
->security_start_time
, boot
->security_finish_time
, y
);
729 svg_bar("generators", boot
->generators_start_time
, boot
->generators_finish_time
, y
);
730 svg_bar("unitsload", boot
->unitsload_start_time
, boot
->unitsload_finish_time
, y
);
731 svg_text(true, boot
->userspace_time
, y
, "systemd");
734 for (u
= times
; u
< times
+ n
; u
++) {
735 char ts
[FORMAT_TIMESPAN_MAX
];
741 svg_bar("activating", u
->activating
, u
->activated
, y
);
742 svg_bar("active", u
->activated
, u
->deactivating
, y
);
743 svg_bar("deactivating", u
->deactivating
, u
->deactivated
, y
);
745 /* place the text on the left if we have passed the half of the svg width */
746 b
= u
->activating
* SCALE_X
< width
/ 2;
748 svg_text(b
, u
->activating
, y
, "%s (%s)",
749 u
->name
, format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
));
751 svg_text(b
, u
->activating
, y
, "%s", u
->name
);
758 svg("<g transform=\"translate(20,100)\">\n");
760 svg_bar("activating", 0, 300000, y
);
761 svg_text(true, 400000, y
, "Activating");
763 svg_bar("active", 0, 300000, y
);
764 svg_text(true, 400000, y
, "Active");
766 svg_bar("deactivating", 0, 300000, y
);
767 svg_text(true, 400000, y
, "Deactivating");
769 svg_bar("security", 0, 300000, y
);
770 svg_text(true, 400000, y
, "Setting up security module");
772 svg_bar("generators", 0, 300000, y
);
773 svg_text(true, 400000, y
, "Generators");
775 svg_bar("unitsload", 0, 300000, y
);
776 svg_text(true, 400000, y
, "Loading unit files");
783 free_unit_times(times
, (unsigned) n
);
789 static int list_dependencies_print(const char *name
, unsigned int level
, unsigned int branches
,
790 bool last
, struct unit_times
*times
, struct boot_times
*boot
) {
792 char ts
[FORMAT_TIMESPAN_MAX
], ts2
[FORMAT_TIMESPAN_MAX
];
794 for (i
= level
; i
!= 0; i
--)
795 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? TREE_VERTICAL
: TREE_SPACE
));
797 printf("%s", special_glyph(last
? TREE_RIGHT
: TREE_BRANCH
));
801 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
802 format_timespan(ts
, sizeof(ts
), times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
803 format_timespan(ts2
, sizeof(ts2
), times
->time
, USEC_PER_MSEC
), ansi_normal());
804 else if (times
->activated
> boot
->userspace_time
)
805 printf("%s @%s", name
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
815 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
816 _cleanup_free_
char *path
= NULL
;
822 path
= unit_dbus_path_from_name(name
);
826 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
829 static Hashmap
*unit_times_hashmap
;
831 static int list_dependencies_compare(const void *_a
, const void *_b
) {
832 const char **a
= (const char**) _a
, **b
= (const char**) _b
;
833 usec_t usa
= 0, usb
= 0;
834 struct unit_times
*times
;
836 times
= hashmap_get(unit_times_hashmap
, *a
);
838 usa
= times
->activated
;
839 times
= hashmap_get(unit_times_hashmap
, *b
);
841 usb
= times
->activated
;
846 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned int level
, char ***units
,
847 unsigned int branches
) {
848 _cleanup_strv_free_
char **deps
= NULL
;
851 usec_t service_longest
= 0;
853 struct unit_times
*times
;
854 struct boot_times
*boot
;
856 if (strv_extend(units
, name
))
859 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
863 qsort_safe(deps
, strv_length(deps
), sizeof (char*), list_dependencies_compare
);
865 r
= acquire_boot_times(bus
, &boot
);
869 STRV_FOREACH(c
, deps
) {
870 times
= hashmap_get(unit_times_hashmap
, *c
);
873 && times
->activated
<= boot
->finish_time
874 && (times
->activated
>= service_longest
875 || service_longest
== 0)) {
876 service_longest
= times
->activated
;
881 if (service_longest
== 0)
884 STRV_FOREACH(c
, deps
) {
885 times
= hashmap_get(unit_times_hashmap
, *c
);
886 if (times
&& times
->activated
&& times
->activated
<= boot
->finish_time
&& (service_longest
- times
->activated
) <= arg_fuzz
)
893 STRV_FOREACH(c
, deps
) {
894 times
= hashmap_get(unit_times_hashmap
, *c
);
897 || times
->activated
> boot
->finish_time
898 || service_longest
- times
->activated
> arg_fuzz
)
903 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
907 if (strv_contains(*units
, *c
)) {
908 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
915 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
,
916 (branches
<< 1) | (to_print
? 1 : 0));
926 static int list_dependencies(sd_bus
*bus
, const char *name
) {
927 _cleanup_strv_free_
char **units
= NULL
;
928 char ts
[FORMAT_TIMESPAN_MAX
];
929 struct unit_times
*times
;
932 _cleanup_free_
char *path
= NULL
;
933 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
934 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
935 struct boot_times
*boot
;
939 path
= unit_dbus_path_from_name(name
);
943 r
= sd_bus_get_property(
945 "org.freedesktop.systemd1",
947 "org.freedesktop.systemd1.Unit",
953 log_error("Failed to get ID: %s", bus_error_message(&error
, -r
));
957 r
= sd_bus_message_read(reply
, "s", &id
);
959 return bus_log_parse_error(r
);
961 times
= hashmap_get(unit_times_hashmap
, id
);
963 r
= acquire_boot_times(bus
, &boot
);
969 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
970 format_timespan(ts
, sizeof(ts
), times
->time
, USEC_PER_MSEC
), ansi_normal());
971 else if (times
->activated
> boot
->userspace_time
)
972 printf("%s @%s\n", id
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
977 return list_dependencies_one(bus
, name
, 0, &units
, 0);
980 static int analyze_critical_chain(sd_bus
*bus
, char *names
[]) {
981 struct unit_times
*times
;
986 n
= acquire_time_data(bus
, ×
);
990 h
= hashmap_new(&string_hash_ops
);
994 for (i
= 0; i
< (unsigned)n
; i
++) {
995 r
= hashmap_put(h
, times
[i
].name
, ×
[i
]);
999 unit_times_hashmap
= h
;
1001 pager_open(arg_no_pager
, false);
1003 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1004 "The time the unit takes to start is printed after the \"+\" character.\n");
1006 if (!strv_isempty(names
)) {
1008 STRV_FOREACH(name
, names
)
1009 list_dependencies(bus
, *name
);
1011 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);
1014 free_unit_times(times
, (unsigned) n
);
1018 static int analyze_blame(sd_bus
*bus
) {
1019 struct unit_times
*times
;
1023 n
= acquire_time_data(bus
, ×
);
1027 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_time
);
1029 pager_open(arg_no_pager
, false);
1031 for (i
= 0; i
< (unsigned) n
; i
++) {
1032 char ts
[FORMAT_TIMESPAN_MAX
];
1034 if (times
[i
].time
> 0)
1035 printf("%16s %s\n", format_timespan(ts
, sizeof(ts
), times
[i
].time
, USEC_PER_MSEC
), times
[i
].name
);
1038 free_unit_times(times
, (unsigned) n
);
1042 static int analyze_time(sd_bus
*bus
) {
1043 _cleanup_free_
char *buf
= NULL
;
1046 r
= pretty_boot_time(bus
, &buf
);
1054 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
[]) {
1055 _cleanup_strv_free_
char **units
= NULL
;
1058 bool match_patterns
;
1064 match_patterns
= strv_fnmatch(patterns
, u
->id
, 0);
1066 if (!strv_isempty(from_patterns
) &&
1068 !strv_fnmatch(from_patterns
, u
->id
, 0))
1071 r
= bus_get_unit_property_strv(bus
, u
->unit_path
, prop
, &units
);
1075 STRV_FOREACH(unit
, units
) {
1076 bool match_patterns2
;
1078 match_patterns2
= strv_fnmatch(patterns
, *unit
, 0);
1080 if (!strv_isempty(to_patterns
) &&
1082 !strv_fnmatch(to_patterns
, *unit
, 0))
1085 if (!strv_isempty(patterns
) && !match_patterns
&& !match_patterns2
)
1088 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u
->id
, *unit
, color
);
1094 static int graph_one(sd_bus
*bus
, const UnitInfo
*u
, char *patterns
[], char *from_patterns
[], char *to_patterns
[]) {
1100 if (IN_SET(arg_dot
, DEP_ORDER
, DEP_ALL
)) {
1101 r
= graph_one_property(bus
, u
, "After", "green", patterns
, from_patterns
, to_patterns
);
1106 if (IN_SET(arg_dot
, DEP_REQUIRE
, DEP_ALL
)) {
1107 r
= graph_one_property(bus
, u
, "Requires", "black", patterns
, from_patterns
, to_patterns
);
1110 r
= graph_one_property(bus
, u
, "Requisite", "darkblue", patterns
, from_patterns
, to_patterns
);
1113 r
= graph_one_property(bus
, u
, "Wants", "grey66", patterns
, from_patterns
, to_patterns
);
1116 r
= graph_one_property(bus
, u
, "Conflicts", "red", patterns
, from_patterns
, to_patterns
);
1124 static int expand_patterns(sd_bus
*bus
, char **patterns
, char ***ret
) {
1125 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1129 STRV_FOREACH(pattern
, patterns
) {
1130 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1131 _cleanup_free_
char *unit
= NULL
, *unit_id
= NULL
;
1133 if (strv_extend(&expanded_patterns
, *pattern
) < 0)
1136 if (string_is_glob(*pattern
))
1139 unit
= unit_dbus_path_from_name(*pattern
);
1143 r
= sd_bus_get_property_string(
1145 "org.freedesktop.systemd1",
1147 "org.freedesktop.systemd1.Unit",
1152 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
1154 if (!streq(*pattern
, unit_id
)) {
1155 if (strv_extend(&expanded_patterns
, unit_id
) < 0)
1160 *ret
= expanded_patterns
;
1161 expanded_patterns
= NULL
; /* do not free */
1166 static int dot(sd_bus
*bus
, char* patterns
[]) {
1167 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1168 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1169 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1170 _cleanup_strv_free_
char **expanded_from_patterns
= NULL
;
1171 _cleanup_strv_free_
char **expanded_to_patterns
= NULL
;
1175 r
= expand_patterns(bus
, patterns
, &expanded_patterns
);
1179 r
= expand_patterns(bus
, arg_dot_from_patterns
, &expanded_from_patterns
);
1183 r
= expand_patterns(bus
, arg_dot_to_patterns
, &expanded_to_patterns
);
1187 r
= sd_bus_call_method(
1189 "org.freedesktop.systemd1",
1190 "/org/freedesktop/systemd1",
1191 "org.freedesktop.systemd1.Manager",
1197 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
1201 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1203 return bus_log_parse_error(r
);
1205 printf("digraph systemd {\n");
1207 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
1209 r
= graph_one(bus
, &u
, expanded_patterns
, expanded_from_patterns
, expanded_to_patterns
);
1214 return bus_log_parse_error(r
);
1218 log_info(" Color legend: black = Requires\n"
1219 " dark blue = Requisite\n"
1220 " dark grey = Wants\n"
1221 " red = Conflicts\n"
1222 " green = After\n");
1225 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1226 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1231 static int dump(sd_bus
*bus
, char **args
) {
1232 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1233 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1234 const char *text
= NULL
;
1237 if (!strv_isempty(args
)) {
1238 log_error("Too many arguments.");
1242 pager_open(arg_no_pager
, false);
1244 r
= sd_bus_call_method(
1246 "org.freedesktop.systemd1",
1247 "/org/freedesktop/systemd1",
1248 "org.freedesktop.systemd1.Manager",
1254 return log_error_errno(r
, "Failed issue method call: %s", bus_error_message(&error
, r
));
1256 r
= sd_bus_message_read(reply
, "s", &text
);
1258 return bus_log_parse_error(r
);
1260 fputs(text
, stdout
);
1264 static int set_log_level(sd_bus
*bus
, char **args
) {
1265 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1271 if (strv_length(args
) != 1) {
1272 log_error("This command expects one argument only.");
1276 r
= sd_bus_set_property(
1278 "org.freedesktop.systemd1",
1279 "/org/freedesktop/systemd1",
1280 "org.freedesktop.systemd1.Manager",
1286 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1291 static int get_log_level(sd_bus
*bus
, char **args
) {
1292 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1294 _cleanup_free_
char *level
= NULL
;
1299 if (!strv_isempty(args
)) {
1300 log_error("Too many arguments.");
1304 r
= sd_bus_get_property_string(
1306 "org.freedesktop.systemd1",
1307 "/org/freedesktop/systemd1",
1308 "org.freedesktop.systemd1.Manager",
1313 return log_error_errno(r
, "Failed to get log level: %s", bus_error_message(&error
, r
));
1319 static int set_log_target(sd_bus
*bus
, char **args
) {
1320 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1326 if (strv_length(args
) != 1) {
1327 log_error("This command expects one argument only.");
1331 r
= sd_bus_set_property(
1333 "org.freedesktop.systemd1",
1334 "/org/freedesktop/systemd1",
1335 "org.freedesktop.systemd1.Manager",
1341 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1346 static int get_log_target(sd_bus
*bus
, char **args
) {
1347 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1349 _cleanup_free_
char *target
= NULL
;
1354 if (!strv_isempty(args
)) {
1355 log_error("Too many arguments.");
1359 r
= sd_bus_get_property_string(
1361 "org.freedesktop.systemd1",
1362 "/org/freedesktop/systemd1",
1363 "org.freedesktop.systemd1.Manager",
1368 return log_error_errno(r
, "Failed to get log target: %s", bus_error_message(&error
, r
));
1375 static void dump_syscall_filter(const SyscallFilterSet
*set
) {
1376 const char *syscall
;
1378 printf("%s\n", set
->name
);
1379 printf(" # %s\n", set
->help
);
1380 NULSTR_FOREACH(syscall
, set
->value
)
1381 printf(" %s\n", syscall
);
1384 static int dump_syscall_filters(char** names
) {
1387 pager_open(arg_no_pager
, false);
1389 if (strv_isempty(names
)) {
1392 for (i
= 0; i
< _SYSCALL_FILTER_SET_MAX
; i
++) {
1395 dump_syscall_filter(syscall_filter_sets
+ i
);
1401 STRV_FOREACH(name
, names
) {
1402 const SyscallFilterSet
*set
;
1407 set
= syscall_filter_set_find(*name
);
1409 /* make sure the error appears below normal output */
1412 log_error("Filter set \"%s\" not found.", *name
);
1416 dump_syscall_filter(set
);
1425 static int dump_syscall_filters(char** names
) {
1426 log_error("Not compiled with syscall filters, sorry.");
1431 static int test_calendar(char **args
) {
1436 if (strv_isempty(args
)) {
1437 log_error("Expected at least one calendar specification string as argument.");
1441 n
= now(CLOCK_REALTIME
);
1443 STRV_FOREACH(p
, args
) {
1444 _cleanup_(calendar_spec_freep
) CalendarSpec
*spec
= NULL
;
1445 _cleanup_free_
char *t
= NULL
;
1448 r
= calendar_spec_from_string(*p
, &spec
);
1450 ret
= log_error_errno(r
, "Failed to parse calendar specification '%s': %m", *p
);
1454 r
= calendar_spec_normalize(spec
);
1456 ret
= log_error_errno(r
, "Failed to normalize calendar specification '%s': %m", *p
);
1460 r
= calendar_spec_to_string(spec
, &t
);
1462 ret
= log_error_errno(r
, "Failed to fomat calendar specification '%s': %m", *p
);
1467 printf(" Original form: %s\n", *p
);
1469 printf("Normalized form: %s\n", t
);
1471 r
= calendar_spec_next_usec(spec
, n
, &next
);
1473 printf(" Next elapse: never\n");
1475 ret
= log_error_errno(r
, "Failed to determine next elapse for '%s': %m", *p
);
1478 char buffer
[CONST_MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_TIMESTAMP_RELATIVE_MAX
)];
1480 printf(" Next elapse: %s\n", format_timestamp(buffer
, sizeof(buffer
), next
));
1482 if (!in_utc_timezone())
1483 printf(" (in UTC): %s\n", format_timestamp_utc(buffer
, sizeof(buffer
), next
));
1485 printf(" From now: %s\n", format_timestamp_relative(buffer
, sizeof(buffer
), next
));
1495 static void help(void) {
1497 pager_open(arg_no_pager
, false);
1499 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1500 "Profile systemd, show unit dependencies, check unit files.\n\n"
1501 " -h --help Show this help\n"
1502 " --version Show package version\n"
1503 " --no-pager Do not pipe output into a pager\n"
1504 " --system Operate on system systemd instance\n"
1505 " --user Operate on user systemd instance\n"
1506 " -H --host=[USER@]HOST Operate on remote host\n"
1507 " -M --machine=CONTAINER Operate on local container\n"
1508 " --order Show only order in the graph\n"
1509 " --require Show only requirement in the graph\n"
1510 " --from-pattern=GLOB Show only origins in the graph\n"
1511 " --to-pattern=GLOB Show only destinations in the graph\n"
1512 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1513 " earlier than the latest in the branch\n"
1514 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1515 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
1517 " time Print time spent in the kernel\n"
1518 " blame Print list of running units ordered by time to init\n"
1519 " critical-chain Print a tree of the time critical chain of units\n"
1520 " plot Output SVG graphic showing service initialization\n"
1521 " dot Output dependency graph in man:dot(1) format\n"
1522 " set-log-level LEVEL Set logging threshold for manager\n"
1523 " set-log-target TARGET Set logging target for manager\n"
1524 " get-log-level Get logging threshold for manager\n"
1525 " get-log-target Get logging target for manager\n"
1526 " dump Output state serialization of service manager\n"
1527 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1528 " verify FILE... Check unit files for correctness\n"
1529 " calendar SPEC... Validate repetitive calendar time events\n"
1530 , program_invocation_short_name
);
1532 /* When updating this list, including descriptions, apply
1533 * changes to shell-completion/bash/systemd-analyze and
1534 * shell-completion/zsh/_systemd-analyze too. */
1537 static int parse_argv(int argc
, char *argv
[]) {
1539 ARG_VERSION
= 0x100,
1544 ARG_DOT_FROM_PATTERN
,
1552 static const struct option options
[] = {
1553 { "help", no_argument
, NULL
, 'h' },
1554 { "version", no_argument
, NULL
, ARG_VERSION
},
1555 { "order", no_argument
, NULL
, ARG_ORDER
},
1556 { "require", no_argument
, NULL
, ARG_REQUIRE
},
1557 { "user", no_argument
, NULL
, ARG_USER
},
1558 { "system", no_argument
, NULL
, ARG_SYSTEM
},
1559 { "from-pattern", required_argument
, NULL
, ARG_DOT_FROM_PATTERN
},
1560 { "to-pattern", required_argument
, NULL
, ARG_DOT_TO_PATTERN
},
1561 { "fuzz", required_argument
, NULL
, ARG_FUZZ
},
1562 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1563 { "man", optional_argument
, NULL
, ARG_MAN
},
1564 { "generators", optional_argument
, NULL
, ARG_GENERATORS
},
1565 { "host", required_argument
, NULL
, 'H' },
1566 { "machine", required_argument
, NULL
, 'M' },
1575 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
1594 arg_dot
= DEP_ORDER
;
1598 arg_dot
= DEP_REQUIRE
;
1601 case ARG_DOT_FROM_PATTERN
:
1602 if (strv_extend(&arg_dot_from_patterns
, optarg
) < 0)
1607 case ARG_DOT_TO_PATTERN
:
1608 if (strv_extend(&arg_dot_to_patterns
, optarg
) < 0)
1614 r
= parse_sec(optarg
, &arg_fuzz
);
1620 arg_no_pager
= true;
1624 arg_transport
= BUS_TRANSPORT_REMOTE
;
1629 arg_transport
= BUS_TRANSPORT_MACHINE
;
1635 r
= parse_boolean(optarg
);
1637 log_error("Failed to parse --man= argument.");
1647 case ARG_GENERATORS
:
1649 r
= parse_boolean(optarg
);
1651 log_error("Failed to parse --generators= argument.");
1655 arg_generators
= !!r
;
1657 arg_generators
= true;
1665 assert_not_reached("Unhandled option code.");
1668 return 1; /* work to do */
1671 int main(int argc
, char *argv
[]) {
1674 setlocale(LC_ALL
, "");
1675 setlocale(LC_NUMERIC
, "C"); /* we want to format/parse floats in C style */
1676 log_parse_environment();
1679 r
= parse_argv(argc
, argv
);
1683 if (streq_ptr(argv
[optind
], "verify"))
1684 r
= verify_units(argv
+optind
+1,
1685 arg_user
? UNIT_FILE_USER
: UNIT_FILE_SYSTEM
,
1689 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1691 r
= bus_connect_transport_systemd(arg_transport
, arg_host
, arg_user
, &bus
);
1693 log_error_errno(r
, "Failed to create bus connection: %m");
1697 if (!argv
[optind
] || streq(argv
[optind
], "time"))
1698 r
= analyze_time(bus
);
1699 else if (streq(argv
[optind
], "blame"))
1700 r
= analyze_blame(bus
);
1701 else if (streq(argv
[optind
], "critical-chain"))
1702 r
= analyze_critical_chain(bus
, argv
+optind
+1);
1703 else if (streq(argv
[optind
], "plot"))
1704 r
= analyze_plot(bus
);
1705 else if (streq(argv
[optind
], "dot"))
1706 r
= dot(bus
, argv
+optind
+1);
1707 else if (streq(argv
[optind
], "dump"))
1708 r
= dump(bus
, argv
+optind
+1);
1709 else if (streq(argv
[optind
], "set-log-level"))
1710 r
= set_log_level(bus
, argv
+optind
+1);
1711 else if (streq(argv
[optind
], "get-log-level"))
1712 r
= get_log_level(bus
, argv
+optind
+1);
1713 else if (streq(argv
[optind
], "set-log-target"))
1714 r
= set_log_target(bus
, argv
+optind
+1);
1715 else if (streq(argv
[optind
], "get-log-target"))
1716 r
= get_log_target(bus
, argv
+optind
+1);
1717 else if (streq(argv
[optind
], "syscall-filter"))
1718 r
= dump_syscall_filters(argv
+optind
+1);
1719 else if (streq(argv
[optind
], "calendar"))
1720 r
= test_calendar(argv
+optind
+1);
1722 log_error("Unknown operation '%s'.", argv
[optind
]);
1728 strv_free(arg_dot_from_patterns
);
1729 strv_free(arg_dot_to_patterns
);
1731 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;