1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2013 Simon Peeters
14 #include "alloc-util.h"
15 #include "analyze-verify.h"
16 #include "bus-error.h"
17 #include "bus-unit-util.h"
19 #include "calendarspec.h"
21 #include "conf-files.h"
25 #include "glob-util.h"
27 #include "locale-util.h"
29 #include "main-func.h"
31 #include "parse-util.h"
32 #include "path-util.h"
33 #include "pretty-print.h"
35 # include "seccomp-util.h"
40 #include "time-util.h"
41 #include "terminal-util.h"
42 #include "unit-name.h"
46 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
47 #define SCALE_Y (20.0)
49 #define svg(...) printf(__VA_ARGS__)
51 #define svg_bar(class, x1, x2, y) \
52 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
54 SCALE_X * (x1), SCALE_Y * (y), \
55 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
57 #define svg_text(b, x, y, format, ...) \
59 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
60 svg(format, ## __VA_ARGS__); \
69 static char** arg_dot_from_patterns
= NULL
;
70 static char** arg_dot_to_patterns
= NULL
;
71 static usec_t arg_fuzz
= 0;
72 static PagerFlags arg_pager_flags
= 0;
73 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
74 static const char *arg_host
= NULL
;
75 static UnitFileScope arg_scope
= UNIT_FILE_SYSTEM
;
76 static bool arg_man
= true;
77 static bool arg_generators
= false;
78 static const char *arg_root
= NULL
;
80 STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns
, strv_freep
);
81 STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns
, strv_freep
);
87 usec_t kernel_done_time
;
89 usec_t userspace_time
;
91 usec_t security_start_time
;
92 usec_t security_finish_time
;
93 usec_t generators_start_time
;
94 usec_t generators_finish_time
;
95 usec_t unitsload_start_time
;
96 usec_t unitsload_finish_time
;
97 usec_t initrd_security_start_time
;
98 usec_t initrd_security_finish_time
;
99 usec_t initrd_generators_start_time
;
100 usec_t initrd_generators_finish_time
;
101 usec_t initrd_unitsload_start_time
;
102 usec_t initrd_unitsload_finish_time
;
105 * If we're analyzing the user instance, all timestamps will be offset
106 * by its own start-up timestamp, which may be arbitrarily big.
107 * With "plot", this causes arbitrarily wide output SVG files which almost
108 * completely consist of empty space. Thus we cancel out this offset.
110 * This offset is subtracted from times above by acquire_boot_times(),
111 * but it still needs to be subtracted from unit-specific timestamps
112 * (so it is stored here for reference).
114 usec_t reverse_offset
;
130 char *kernel_release
;
131 char *kernel_version
;
132 char *os_pretty_name
;
133 char *virtualization
;
137 static int acquire_bus(sd_bus
**bus
, bool *use_full_bus
) {
138 bool user
= arg_scope
!= UNIT_FILE_SYSTEM
;
141 if (use_full_bus
&& *use_full_bus
) {
142 r
= bus_connect_transport(arg_transport
, arg_host
, user
, bus
);
143 if (IN_SET(r
, 0, -EHOSTDOWN
))
146 *use_full_bus
= false;
149 return bus_connect_transport_systemd(arg_transport
, arg_host
, user
, bus
);
152 static int bus_get_uint64_property(sd_bus
*bus
, const char *path
, const char *interface
, const char *property
, uint64_t *val
) {
153 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
162 r
= sd_bus_get_property_trivial(
164 "org.freedesktop.systemd1",
172 return log_error_errno(r
, "Failed to parse reply: %s", bus_error_message(&error
, -r
));
177 static int bus_get_unit_property_strv(sd_bus
*bus
, const char *path
, const char *property
, char ***strv
) {
178 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
186 r
= sd_bus_get_property_strv(
188 "org.freedesktop.systemd1",
190 "org.freedesktop.systemd1.Unit",
195 return log_error_errno(r
, "Failed to get unit property %s: %s", property
, bus_error_message(&error
, -r
));
200 static int compare_unit_time(const struct unit_times
*a
, const struct unit_times
*b
) {
201 return CMP(b
->time
, a
->time
);
204 static int compare_unit_start(const struct unit_times
*a
, const struct unit_times
*b
) {
205 return CMP(a
->activating
, b
->activating
);
208 static void unit_times_free(struct unit_times
*t
) {
209 struct unit_times
*p
;
211 for (p
= t
; p
->has_data
; p
++)
216 DEFINE_TRIVIAL_CLEANUP_FUNC(struct unit_times
*, unit_times_free
);
218 static void subtract_timestamp(usec_t
*a
, usec_t b
) {
227 static int acquire_boot_times(sd_bus
*bus
, struct boot_times
**bt
) {
228 static const struct bus_properties_map property_map
[] = {
229 { "FirmwareTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, firmware_time
) },
230 { "LoaderTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, loader_time
) },
231 { "KernelTimestamp", "t", NULL
, offsetof(struct boot_times
, kernel_time
) },
232 { "InitRDTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_time
) },
233 { "UserspaceTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, userspace_time
) },
234 { "FinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, finish_time
) },
235 { "SecurityStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, security_start_time
) },
236 { "SecurityFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, security_finish_time
) },
237 { "GeneratorsStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, generators_start_time
) },
238 { "GeneratorsFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, generators_finish_time
) },
239 { "UnitsLoadStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, unitsload_start_time
) },
240 { "UnitsLoadFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, unitsload_finish_time
) },
241 { "InitRDSecurityStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_security_start_time
) },
242 { "InitRDSecurityFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_security_finish_time
) },
243 { "InitRDGeneratorsStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_generators_start_time
) },
244 { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_generators_finish_time
) },
245 { "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_unitsload_start_time
) },
246 { "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_unitsload_finish_time
) },
249 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
250 static struct boot_times times
;
251 static bool cached
= false;
257 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
259 r
= bus_map_all_properties(
261 "org.freedesktop.systemd1",
262 "/org/freedesktop/systemd1",
269 return log_error_errno(r
, "Failed to get timestamp properties: %s", bus_error_message(&error
, r
));
271 if (times
.finish_time
<= 0) {
272 log_error("Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64
").\n"
273 "Please try again later.\n"
274 "Hint: Use 'systemctl%s list-jobs' to see active jobs",
276 arg_scope
== UNIT_FILE_SYSTEM
? "" : " --user");
280 if (arg_scope
== UNIT_FILE_SYSTEM
&& times
.security_start_time
> 0) {
281 /* security_start_time is set when systemd is not running under container environment. */
282 if (times
.initrd_time
> 0)
283 times
.kernel_done_time
= times
.initrd_time
;
285 times
.kernel_done_time
= times
.userspace_time
;
288 * User-instance-specific or container-system-specific timestamps processing
289 * (see comment to reverse_offset in struct boot_times).
291 times
.reverse_offset
= times
.userspace_time
;
293 times
.firmware_time
= times
.loader_time
= times
.kernel_time
= times
.initrd_time
= times
.userspace_time
=
294 times
.security_start_time
= times
.security_finish_time
= 0;
296 subtract_timestamp(×
.finish_time
, times
.reverse_offset
);
298 subtract_timestamp(×
.generators_start_time
, times
.reverse_offset
);
299 subtract_timestamp(×
.generators_finish_time
, times
.reverse_offset
);
301 subtract_timestamp(×
.unitsload_start_time
, times
.reverse_offset
);
302 subtract_timestamp(×
.unitsload_finish_time
, times
.reverse_offset
);
312 static void free_host_info(struct host_info
*hi
) {
318 free(hi
->kernel_name
);
319 free(hi
->kernel_release
);
320 free(hi
->kernel_version
);
321 free(hi
->os_pretty_name
);
322 free(hi
->virtualization
);
323 free(hi
->architecture
);
327 DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info
*, free_host_info
);
329 static int acquire_time_data(sd_bus
*bus
, struct unit_times
**out
) {
330 static const struct bus_properties_map property_map
[] = {
331 { "InactiveExitTimestampMonotonic", "t", NULL
, offsetof(struct unit_times
, activating
) },
332 { "ActiveEnterTimestampMonotonic", "t", NULL
, offsetof(struct unit_times
, activated
) },
333 { "ActiveExitTimestampMonotonic", "t", NULL
, offsetof(struct unit_times
, deactivating
) },
334 { "InactiveEnterTimestampMonotonic", "t", NULL
, offsetof(struct unit_times
, deactivated
) },
337 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
338 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
339 _cleanup_(unit_times_freep
) struct unit_times
*unit_times
= NULL
;
340 struct boot_times
*boot_times
= NULL
;
341 size_t allocated
= 0, c
= 0;
345 r
= acquire_boot_times(bus
, &boot_times
);
349 r
= sd_bus_call_method(
351 "org.freedesktop.systemd1",
352 "/org/freedesktop/systemd1",
353 "org.freedesktop.systemd1.Manager",
358 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, -r
));
360 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
362 return bus_log_parse_error(r
);
364 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
365 struct unit_times
*t
;
367 if (!GREEDY_REALLOC(unit_times
, allocated
, c
+2))
370 unit_times
[c
+1].has_data
= false;
374 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
376 r
= bus_map_all_properties(
378 "org.freedesktop.systemd1",
386 return log_error_errno(r
, "Failed to get timestamp properties of unit %s: %s", u
.id
, bus_error_message(&error
, r
));
388 subtract_timestamp(&t
->activating
, boot_times
->reverse_offset
);
389 subtract_timestamp(&t
->activated
, boot_times
->reverse_offset
);
390 subtract_timestamp(&t
->deactivating
, boot_times
->reverse_offset
);
391 subtract_timestamp(&t
->deactivated
, boot_times
->reverse_offset
);
393 if (t
->activated
>= t
->activating
)
394 t
->time
= t
->activated
- t
->activating
;
395 else if (t
->deactivated
>= t
->activating
)
396 t
->time
= t
->deactivated
- t
->activating
;
400 if (t
->activating
== 0)
403 t
->name
= strdup(u
.id
);
411 return bus_log_parse_error(r
);
413 *out
= TAKE_PTR(unit_times
);
417 static int acquire_host_info(sd_bus
*bus
, struct host_info
**hi
) {
418 static const struct bus_properties_map hostname_map
[] = {
419 { "Hostname", "s", NULL
, offsetof(struct host_info
, hostname
) },
420 { "KernelName", "s", NULL
, offsetof(struct host_info
, kernel_name
) },
421 { "KernelRelease", "s", NULL
, offsetof(struct host_info
, kernel_release
) },
422 { "KernelVersion", "s", NULL
, offsetof(struct host_info
, kernel_version
) },
423 { "OperatingSystemPrettyName", "s", NULL
, offsetof(struct host_info
, os_pretty_name
) },
427 static const struct bus_properties_map manager_map
[] = {
428 { "Virtualization", "s", NULL
, offsetof(struct host_info
, virtualization
) },
429 { "Architecture", "s", NULL
, offsetof(struct host_info
, architecture
) },
433 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
434 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*system_bus
= NULL
;
435 _cleanup_(free_host_infop
) struct host_info
*host
;
438 host
= new0(struct host_info
, 1);
442 if (arg_scope
!= UNIT_FILE_SYSTEM
) {
443 r
= bus_connect_transport(arg_transport
, arg_host
, false, &system_bus
);
445 log_debug_errno(r
, "Failed to connect to system bus, ignoring: %m");
450 r
= bus_map_all_properties(system_bus
?: bus
,
451 "org.freedesktop.hostname1",
452 "/org/freedesktop/hostname1",
459 log_debug_errno(r
, "Failed to get host information from systemd-hostnamed, ignoring: %s", bus_error_message(&error
, r
));
460 sd_bus_error_free(&error
);
464 r
= bus_map_all_properties(bus
,
465 "org.freedesktop.systemd1",
466 "/org/freedesktop/systemd1",
473 return log_error_errno(r
, "Failed to get host information from systemd: %s", bus_error_message(&error
, r
));
475 *hi
= TAKE_PTR(host
);
480 static int pretty_boot_time(sd_bus
*bus
, char **_buf
) {
481 char ts
[FORMAT_TIMESPAN_MAX
];
482 struct boot_times
*t
;
483 static char buf
[4096];
487 usec_t activated_time
= USEC_INFINITY
;
488 _cleanup_free_
char* path
= NULL
, *unit_id
= NULL
;
489 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
491 r
= acquire_boot_times(bus
, &t
);
495 path
= unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET
);
499 r
= sd_bus_get_property_string(
501 "org.freedesktop.systemd1",
503 "org.freedesktop.systemd1.Unit",
508 log_error_errno(r
, "default.target doesn't seem to exist: %s", bus_error_message(&error
, r
));
512 r
= bus_get_uint64_property(bus
, path
,
513 "org.freedesktop.systemd1.Unit",
514 "ActiveEnterTimestampMonotonic",
517 log_info_errno(r
, "Could not get time to reach default.target, ignoring: %m");
518 activated_time
= USEC_INFINITY
;
524 size
= strpcpyf(&ptr
, size
, "Startup finished in ");
525 if (t
->firmware_time
> 0)
526 size
= strpcpyf(&ptr
, size
, "%s (firmware) + ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
- t
->loader_time
, USEC_PER_MSEC
));
527 if (t
->loader_time
> 0)
528 size
= strpcpyf(&ptr
, size
, "%s (loader) + ", format_timespan(ts
, sizeof(ts
), t
->loader_time
, USEC_PER_MSEC
));
529 if (t
->kernel_done_time
> 0)
530 size
= strpcpyf(&ptr
, size
, "%s (kernel) + ", format_timespan(ts
, sizeof(ts
), t
->kernel_done_time
, USEC_PER_MSEC
));
531 if (t
->initrd_time
> 0)
532 size
= strpcpyf(&ptr
, size
, "%s (initrd) + ", format_timespan(ts
, sizeof(ts
), t
->userspace_time
- t
->initrd_time
, USEC_PER_MSEC
));
534 size
= strpcpyf(&ptr
, size
, "%s (userspace) ", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
535 if (t
->kernel_done_time
> 0)
536 strpcpyf(&ptr
, size
, "= %s ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
+ t
->finish_time
, USEC_PER_MSEC
));
538 if (unit_id
&& activated_time
> 0 && activated_time
!= USEC_INFINITY
) {
539 usec_t base
= t
->userspace_time
> 0 ? t
->userspace_time
: t
->reverse_offset
;
541 size
= strpcpyf(&ptr
, size
, "\n%s reached after %s in userspace", unit_id
,
542 format_timespan(ts
, sizeof(ts
), activated_time
- base
, USEC_PER_MSEC
));
543 } else if (unit_id
&& activated_time
== 0)
544 size
= strpcpyf(&ptr
, size
, "\n%s was never reached", unit_id
);
545 else if (unit_id
&& activated_time
== USEC_INFINITY
)
546 size
= strpcpyf(&ptr
, size
, "\nCould not get time to reach %s.", unit_id
);
548 size
= strpcpyf(&ptr
, size
, "\ncould not find default.target");
558 static void svg_graph_box(double height
, double begin
, double end
) {
561 /* outside box, fill */
562 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
563 SCALE_X
* (end
- begin
), SCALE_Y
* height
);
565 for (i
= ((long long) (begin
/ 100000)) * 100000; i
<= end
; i
+=100000) {
566 /* lines for each second */
567 if (i
% 5000000 == 0)
568 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
569 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
570 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
571 else if (i
% 1000000 == 0)
572 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
573 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
574 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
576 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
577 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
);
581 static int plot_unit_times(struct unit_times
*u
, double width
, int y
) {
582 char ts
[FORMAT_TIMESPAN_MAX
];
588 svg_bar("activating", u
->activating
, u
->activated
, y
);
589 svg_bar("active", u
->activated
, u
->deactivating
, y
);
590 svg_bar("deactivating", u
->deactivating
, u
->deactivated
, y
);
592 /* place the text on the left if we have passed the half of the svg width */
593 b
= u
->activating
* SCALE_X
< width
/ 2;
595 svg_text(b
, u
->activating
, y
, "%s (%s)",
596 u
->name
, format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
));
598 svg_text(b
, u
->activating
, y
, "%s", u
->name
);
603 static int analyze_plot(int argc
, char *argv
[], void *userdata
) {
604 _cleanup_(free_host_infop
) struct host_info
*host
= NULL
;
605 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
606 _cleanup_(unit_times_freep
) struct unit_times
*times
= NULL
;
607 _cleanup_free_
char *pretty_times
= NULL
;
608 bool use_full_bus
= arg_scope
== UNIT_FILE_SYSTEM
;
609 struct boot_times
*boot
;
610 struct unit_times
*u
;
611 int n
, m
= 1, y
= 0, r
;
614 r
= acquire_bus(&bus
, &use_full_bus
);
616 return log_error_errno(r
, "Failed to create bus connection: %m");
618 n
= acquire_boot_times(bus
, &boot
);
622 n
= pretty_boot_time(bus
, &pretty_times
);
626 if (use_full_bus
|| arg_scope
!= UNIT_FILE_SYSTEM
) {
627 n
= acquire_host_info(bus
, &host
);
632 n
= acquire_time_data(bus
, ×
);
636 typesafe_qsort(times
, n
, compare_unit_start
);
638 width
= SCALE_X
* (boot
->firmware_time
+ boot
->finish_time
);
642 if (boot
->firmware_time
> boot
->loader_time
)
644 if (boot
->loader_time
> 0) {
649 if (boot
->initrd_time
> 0)
651 if (boot
->kernel_done_time
> 0)
654 for (u
= times
; u
->has_data
; u
++) {
655 double text_start
, text_width
;
657 if (u
->activating
> boot
->finish_time
) {
658 u
->name
= mfree(u
->name
);
662 /* If the text cannot fit on the left side then
663 * increase the svg width so it fits on the right.
664 * TODO: calculate the text width more accurately */
665 text_width
= 8.0 * strlen(u
->name
);
666 text_start
= (boot
->firmware_time
+ u
->activating
) * SCALE_X
;
667 if (text_width
> text_start
&& text_width
+ text_start
> width
)
668 width
= text_width
+ text_start
;
670 if (u
->deactivated
> u
->activating
&&
671 u
->deactivated
<= boot
->finish_time
&&
672 u
->activated
== 0 && u
->deactivating
== 0)
673 u
->activated
= u
->deactivating
= u
->deactivated
;
674 if (u
->activated
< u
->activating
|| u
->activated
> boot
->finish_time
)
675 u
->activated
= boot
->finish_time
;
676 if (u
->deactivating
< u
->activated
|| u
->deactivating
> boot
->finish_time
)
677 u
->deactivating
= boot
->finish_time
;
678 if (u
->deactivated
< u
->deactivating
|| u
->deactivated
> boot
->finish_time
)
679 u
->deactivated
= boot
->finish_time
;
683 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
684 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
685 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
687 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
688 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
689 80.0 + width
, 150.0 + (m
* SCALE_Y
) +
690 5 * SCALE_Y
/* legend */);
692 /* write some basic info as a comment, including some help */
693 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
694 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
695 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
696 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
697 "<!-- point your browser to this file. -->\n\n"
698 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION
);
701 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
702 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
703 " rect.background { fill: rgb(255,255,255); }\n"
704 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
705 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
706 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
707 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
708 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
709 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
710 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
711 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
712 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
713 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
714 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
715 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
716 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
718 " line.sec5 { stroke-width: 2; }\n"
719 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
720 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
721 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
722 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
723 " text.sec { font-size: 10px; }\n"
724 " ]]>\n </style>\n</defs>\n\n");
726 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
727 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times
);
729 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
730 isempty(host
->os_pretty_name
) ? "Linux" : host
->os_pretty_name
,
731 strempty(host
->hostname
),
732 strempty(host
->kernel_name
),
733 strempty(host
->kernel_release
),
734 strempty(host
->kernel_version
),
735 strempty(host
->architecture
),
736 strempty(host
->virtualization
));
738 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X
* boot
->firmware_time
));
739 svg_graph_box(m
, -(double) boot
->firmware_time
, boot
->finish_time
);
741 if (boot
->firmware_time
> 0) {
742 svg_bar("firmware", -(double) boot
->firmware_time
, -(double) boot
->loader_time
, y
);
743 svg_text(true, -(double) boot
->firmware_time
, y
, "firmware");
746 if (boot
->loader_time
> 0) {
747 svg_bar("loader", -(double) boot
->loader_time
, 0, y
);
748 svg_text(true, -(double) boot
->loader_time
, y
, "loader");
751 if (boot
->kernel_done_time
> 0) {
752 svg_bar("kernel", 0, boot
->kernel_done_time
, y
);
753 svg_text(true, 0, y
, "kernel");
756 if (boot
->initrd_time
> 0) {
757 svg_bar("initrd", boot
->initrd_time
, boot
->userspace_time
, y
);
758 if (boot
->initrd_security_start_time
< boot
->initrd_security_finish_time
)
759 svg_bar("security", boot
->initrd_security_start_time
, boot
->initrd_security_finish_time
, y
);
760 if (boot
->initrd_generators_start_time
< boot
->initrd_generators_finish_time
)
761 svg_bar("generators", boot
->initrd_generators_start_time
, boot
->initrd_generators_finish_time
, y
);
762 if (boot
->initrd_unitsload_start_time
< boot
->initrd_unitsload_finish_time
)
763 svg_bar("unitsload", boot
->initrd_unitsload_start_time
, boot
->initrd_unitsload_finish_time
, y
);
764 svg_text(true, boot
->initrd_time
, y
, "initrd");
768 for (u
= times
; u
->has_data
; u
++) {
769 if (u
->activating
>= boot
->userspace_time
)
772 y
+= plot_unit_times(u
, width
, y
);
775 svg_bar("active", boot
->userspace_time
, boot
->finish_time
, y
);
776 if (boot
->security_start_time
> 0)
777 svg_bar("security", boot
->security_start_time
, boot
->security_finish_time
, y
);
778 svg_bar("generators", boot
->generators_start_time
, boot
->generators_finish_time
, y
);
779 svg_bar("unitsload", boot
->unitsload_start_time
, boot
->unitsload_finish_time
, y
);
780 svg_text(true, boot
->userspace_time
, y
, "systemd");
783 for (; u
->has_data
; u
++)
784 y
+= plot_unit_times(u
, width
, y
);
789 svg("<g transform=\"translate(20,100)\">\n");
791 svg_bar("activating", 0, 300000, y
);
792 svg_text(true, 400000, y
, "Activating");
794 svg_bar("active", 0, 300000, y
);
795 svg_text(true, 400000, y
, "Active");
797 svg_bar("deactivating", 0, 300000, y
);
798 svg_text(true, 400000, y
, "Deactivating");
800 if (boot
->security_start_time
> 0) {
801 svg_bar("security", 0, 300000, y
);
802 svg_text(true, 400000, y
, "Setting up security module");
805 svg_bar("generators", 0, 300000, y
);
806 svg_text(true, 400000, y
, "Generators");
808 svg_bar("unitsload", 0, 300000, y
);
809 svg_text(true, 400000, y
, "Loading unit files");
819 static int list_dependencies_print(const char *name
, unsigned level
, unsigned branches
,
820 bool last
, struct unit_times
*times
, struct boot_times
*boot
) {
822 char ts
[FORMAT_TIMESPAN_MAX
], ts2
[FORMAT_TIMESPAN_MAX
];
824 for (i
= level
; i
!= 0; i
--)
825 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? TREE_VERTICAL
: TREE_SPACE
));
827 printf("%s", special_glyph(last
? TREE_RIGHT
: TREE_BRANCH
));
831 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
832 format_timespan(ts
, sizeof(ts
), times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
833 format_timespan(ts2
, sizeof(ts2
), times
->time
, USEC_PER_MSEC
), ansi_normal());
834 else if (times
->activated
> boot
->userspace_time
)
835 printf("%s @%s", name
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
845 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
846 _cleanup_free_
char *path
= NULL
;
852 path
= unit_dbus_path_from_name(name
);
856 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
859 static Hashmap
*unit_times_hashmap
;
861 static int list_dependencies_compare(char * const *a
, char * const *b
) {
862 usec_t usa
= 0, usb
= 0;
863 struct unit_times
*times
;
865 times
= hashmap_get(unit_times_hashmap
, *a
);
867 usa
= times
->activated
;
868 times
= hashmap_get(unit_times_hashmap
, *b
);
870 usb
= times
->activated
;
872 return CMP(usb
, usa
);
875 static bool times_in_range(const struct unit_times
*times
, const struct boot_times
*boot
) {
877 times
->activated
> 0 && times
->activated
<= boot
->finish_time
;
880 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned level
, char ***units
,
882 _cleanup_strv_free_
char **deps
= NULL
;
885 usec_t service_longest
= 0;
887 struct unit_times
*times
;
888 struct boot_times
*boot
;
890 if (strv_extend(units
, name
))
893 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
897 typesafe_qsort(deps
, strv_length(deps
), list_dependencies_compare
);
899 r
= acquire_boot_times(bus
, &boot
);
903 STRV_FOREACH(c
, deps
) {
904 times
= hashmap_get(unit_times_hashmap
, *c
);
905 if (times_in_range(times
, boot
) &&
906 times
->activated
>= service_longest
)
907 service_longest
= times
->activated
;
910 if (service_longest
== 0)
913 STRV_FOREACH(c
, deps
) {
914 times
= hashmap_get(unit_times_hashmap
, *c
);
915 if (times_in_range(times
, boot
) &&
916 service_longest
- times
->activated
<= arg_fuzz
)
923 STRV_FOREACH(c
, deps
) {
924 times
= hashmap_get(unit_times_hashmap
, *c
);
925 if (!times_in_range(times
, boot
) ||
926 service_longest
- times
->activated
> arg_fuzz
)
931 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
935 if (strv_contains(*units
, *c
)) {
936 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
943 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
,
944 (branches
<< 1) | (to_print
? 1 : 0));
954 static int list_dependencies(sd_bus
*bus
, const char *name
) {
955 _cleanup_strv_free_
char **units
= NULL
;
956 char ts
[FORMAT_TIMESPAN_MAX
];
957 struct unit_times
*times
;
960 _cleanup_free_
char *path
= NULL
;
961 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
962 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
963 struct boot_times
*boot
;
967 path
= unit_dbus_path_from_name(name
);
971 r
= sd_bus_get_property(
973 "org.freedesktop.systemd1",
975 "org.freedesktop.systemd1.Unit",
981 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, -r
));
983 r
= sd_bus_message_read(reply
, "s", &id
);
985 return bus_log_parse_error(r
);
987 times
= hashmap_get(unit_times_hashmap
, id
);
989 r
= acquire_boot_times(bus
, &boot
);
995 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
996 format_timespan(ts
, sizeof(ts
), times
->time
, USEC_PER_MSEC
), ansi_normal());
997 else if (times
->activated
> boot
->userspace_time
)
998 printf("%s @%s\n", id
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
1003 return list_dependencies_one(bus
, name
, 0, &units
, 0);
1006 static int analyze_critical_chain(int argc
, char *argv
[], void *userdata
) {
1007 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1008 _cleanup_(unit_times_freep
) struct unit_times
*times
= NULL
;
1009 struct unit_times
*u
;
1013 r
= acquire_bus(&bus
, NULL
);
1015 return log_error_errno(r
, "Failed to create bus connection: %m");
1017 n
= acquire_time_data(bus
, ×
);
1021 h
= hashmap_new(&string_hash_ops
);
1025 for (u
= times
; u
->has_data
; u
++) {
1026 r
= hashmap_put(h
, u
->name
, u
);
1028 return log_error_errno(r
, "Failed to add entry to hashmap: %m");
1030 unit_times_hashmap
= h
;
1032 (void) pager_open(arg_pager_flags
);
1034 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1035 "The time the unit takes to start is printed after the \"+\" character.\n");
1039 STRV_FOREACH(name
, strv_skip(argv
, 1))
1040 list_dependencies(bus
, *name
);
1042 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);
1044 h
= hashmap_free(h
);
1048 static int analyze_blame(int argc
, char *argv
[], void *userdata
) {
1049 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1050 _cleanup_(unit_times_freep
) struct unit_times
*times
= NULL
;
1051 struct unit_times
*u
;
1054 r
= acquire_bus(&bus
, NULL
);
1056 return log_error_errno(r
, "Failed to create bus connection: %m");
1058 n
= acquire_time_data(bus
, ×
);
1062 typesafe_qsort(times
, n
, compare_unit_time
);
1064 (void) pager_open(arg_pager_flags
);
1066 for (u
= times
; u
->has_data
; u
++) {
1067 char ts
[FORMAT_TIMESPAN_MAX
];
1070 printf("%16s %s\n", format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
), u
->name
);
1076 static int analyze_time(int argc
, char *argv
[], void *userdata
) {
1077 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1078 _cleanup_free_
char *buf
= NULL
;
1081 r
= acquire_bus(&bus
, NULL
);
1083 return log_error_errno(r
, "Failed to create bus connection: %m");
1085 r
= pretty_boot_time(bus
, &buf
);
1093 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
[]) {
1094 _cleanup_strv_free_
char **units
= NULL
;
1097 bool match_patterns
;
1103 match_patterns
= strv_fnmatch(patterns
, u
->id
, 0);
1105 if (!strv_isempty(from_patterns
) &&
1107 !strv_fnmatch(from_patterns
, u
->id
, 0))
1110 r
= bus_get_unit_property_strv(bus
, u
->unit_path
, prop
, &units
);
1114 STRV_FOREACH(unit
, units
) {
1115 bool match_patterns2
;
1117 match_patterns2
= strv_fnmatch(patterns
, *unit
, 0);
1119 if (!strv_isempty(to_patterns
) &&
1121 !strv_fnmatch(to_patterns
, *unit
, 0))
1124 if (!strv_isempty(patterns
) && !match_patterns
&& !match_patterns2
)
1127 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u
->id
, *unit
, color
);
1133 static int graph_one(sd_bus
*bus
, const UnitInfo
*u
, char *patterns
[], char *from_patterns
[], char *to_patterns
[]) {
1139 if (IN_SET(arg_dot
, DEP_ORDER
, DEP_ALL
)) {
1140 r
= graph_one_property(bus
, u
, "After", "green", patterns
, from_patterns
, to_patterns
);
1145 if (IN_SET(arg_dot
, DEP_REQUIRE
, DEP_ALL
)) {
1146 r
= graph_one_property(bus
, u
, "Requires", "black", patterns
, from_patterns
, to_patterns
);
1149 r
= graph_one_property(bus
, u
, "Requisite", "darkblue", patterns
, from_patterns
, to_patterns
);
1152 r
= graph_one_property(bus
, u
, "Wants", "grey66", patterns
, from_patterns
, to_patterns
);
1155 r
= graph_one_property(bus
, u
, "Conflicts", "red", patterns
, from_patterns
, to_patterns
);
1163 static int expand_patterns(sd_bus
*bus
, char **patterns
, char ***ret
) {
1164 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1168 STRV_FOREACH(pattern
, patterns
) {
1169 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1170 _cleanup_free_
char *unit
= NULL
, *unit_id
= NULL
;
1172 if (strv_extend(&expanded_patterns
, *pattern
) < 0)
1175 if (string_is_glob(*pattern
))
1178 unit
= unit_dbus_path_from_name(*pattern
);
1182 r
= sd_bus_get_property_string(
1184 "org.freedesktop.systemd1",
1186 "org.freedesktop.systemd1.Unit",
1191 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
1193 if (!streq(*pattern
, unit_id
)) {
1194 if (strv_extend(&expanded_patterns
, unit_id
) < 0)
1199 *ret
= expanded_patterns
;
1200 expanded_patterns
= NULL
; /* do not free */
1205 static int dot(int argc
, char *argv
[], void *userdata
) {
1206 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1207 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1208 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1209 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1210 _cleanup_strv_free_
char **expanded_from_patterns
= NULL
;
1211 _cleanup_strv_free_
char **expanded_to_patterns
= NULL
;
1215 r
= acquire_bus(&bus
, NULL
);
1217 return log_error_errno(r
, "Failed to create bus connection: %m");
1219 r
= expand_patterns(bus
, strv_skip(argv
, 1), &expanded_patterns
);
1223 r
= expand_patterns(bus
, arg_dot_from_patterns
, &expanded_from_patterns
);
1227 r
= expand_patterns(bus
, arg_dot_to_patterns
, &expanded_to_patterns
);
1231 r
= sd_bus_call_method(
1233 "org.freedesktop.systemd1",
1234 "/org/freedesktop/systemd1",
1235 "org.freedesktop.systemd1.Manager",
1241 log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, -r
));
1243 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1245 return bus_log_parse_error(r
);
1247 printf("digraph systemd {\n");
1249 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
1251 r
= graph_one(bus
, &u
, expanded_patterns
, expanded_from_patterns
, expanded_to_patterns
);
1256 return bus_log_parse_error(r
);
1260 log_info(" Color legend: black = Requires\n"
1261 " dark blue = Requisite\n"
1262 " dark grey = Wants\n"
1263 " red = Conflicts\n"
1264 " green = After\n");
1267 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1268 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1273 static int dump_fallback(sd_bus
*bus
) {
1274 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1275 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1276 const char *text
= NULL
;
1281 r
= sd_bus_call_method(
1283 "org.freedesktop.systemd1",
1284 "/org/freedesktop/systemd1",
1285 "org.freedesktop.systemd1.Manager",
1291 return log_error_errno(r
, "Failed to issue method call Dump: %s", bus_error_message(&error
, r
));
1293 r
= sd_bus_message_read(reply
, "s", &text
);
1295 return bus_log_parse_error(r
);
1297 fputs(text
, stdout
);
1301 static int dump(int argc
, char *argv
[], void *userdata
) {
1302 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1303 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1304 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1308 r
= acquire_bus(&bus
, NULL
);
1310 return log_error_errno(r
, "Failed to create bus connection: %m");
1312 (void) pager_open(arg_pager_flags
);
1314 if (!sd_bus_can_send(bus
, SD_BUS_TYPE_UNIX_FD
))
1315 return dump_fallback(bus
);
1317 r
= sd_bus_call_method(
1319 "org.freedesktop.systemd1",
1320 "/org/freedesktop/systemd1",
1321 "org.freedesktop.systemd1.Manager",
1322 "DumpByFileDescriptor",
1327 /* fall back to Dump if DumpByFileDescriptor is not supported */
1328 if (!IN_SET(r
, -EACCES
, -EBADR
))
1329 return log_error_errno(r
, "Failed to issue method call DumpByFileDescriptor: %s", bus_error_message(&error
, r
));
1331 return dump_fallback(bus
);
1334 r
= sd_bus_message_read(reply
, "h", &fd
);
1336 return bus_log_parse_error(r
);
1339 return copy_bytes(fd
, STDOUT_FILENO
, (uint64_t) -1, 0);
1342 static int cat_config(int argc
, char *argv
[], void *userdata
) {
1346 (void) pager_open(arg_pager_flags
);
1348 STRV_FOREACH(arg
, argv
+ 1) {
1349 const char *t
= NULL
;
1351 if (arg
!= argv
+ 1)
1354 if (path_is_absolute(*arg
)) {
1357 NULSTR_FOREACH(dir
, CONF_PATHS_NULSTR("")) {
1358 t
= path_startswith(*arg
, dir
);
1364 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1365 "Path %s does not start with any known prefix.",
1370 r
= conf_files_cat(arg_root
, t
);
1378 static int set_log_level(int argc
, char *argv
[], void *userdata
) {
1379 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1380 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1386 r
= acquire_bus(&bus
, NULL
);
1388 return log_error_errno(r
, "Failed to create bus connection: %m");
1390 r
= sd_bus_set_property(
1392 "org.freedesktop.systemd1",
1393 "/org/freedesktop/systemd1",
1394 "org.freedesktop.systemd1.Manager",
1400 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1405 static int get_log_level(int argc
, char *argv
[], void *userdata
) {
1406 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1407 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1408 _cleanup_free_
char *level
= NULL
;
1411 r
= acquire_bus(&bus
, NULL
);
1413 return log_error_errno(r
, "Failed to create bus connection: %m");
1415 r
= sd_bus_get_property_string(
1417 "org.freedesktop.systemd1",
1418 "/org/freedesktop/systemd1",
1419 "org.freedesktop.systemd1.Manager",
1424 return log_error_errno(r
, "Failed to get log level: %s", bus_error_message(&error
, r
));
1430 static int get_or_set_log_level(int argc
, char *argv
[], void *userdata
) {
1431 return (argc
== 1) ? get_log_level(argc
, argv
, userdata
) : set_log_level(argc
, argv
, userdata
);
1434 static int set_log_target(int argc
, char *argv
[], void *userdata
) {
1435 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1436 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1442 r
= acquire_bus(&bus
, NULL
);
1444 return log_error_errno(r
, "Failed to create bus connection: %m");
1446 r
= sd_bus_set_property(
1448 "org.freedesktop.systemd1",
1449 "/org/freedesktop/systemd1",
1450 "org.freedesktop.systemd1.Manager",
1456 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1461 static int get_log_target(int argc
, char *argv
[], void *userdata
) {
1462 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1463 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1464 _cleanup_free_
char *target
= NULL
;
1467 r
= acquire_bus(&bus
, NULL
);
1469 return log_error_errno(r
, "Failed to create bus connection: %m");
1471 r
= sd_bus_get_property_string(
1473 "org.freedesktop.systemd1",
1474 "/org/freedesktop/systemd1",
1475 "org.freedesktop.systemd1.Manager",
1480 return log_error_errno(r
, "Failed to get log target: %s", bus_error_message(&error
, r
));
1486 static int get_or_set_log_target(int argc
, char *argv
[], void *userdata
) {
1487 return (argc
== 1) ? get_log_target(argc
, argv
, userdata
) : set_log_target(argc
, argv
, userdata
);
1490 static int dump_unit_paths(int argc
, char *argv
[], void *userdata
) {
1491 _cleanup_(lookup_paths_free
) LookupPaths paths
= {};
1495 r
= lookup_paths_init(&paths
, arg_scope
, 0, NULL
);
1497 return log_error_errno(r
, "lookup_paths_init() failed: %m");
1499 STRV_FOREACH(p
, paths
.search_path
)
1507 static int load_kernel_syscalls(Set
**ret
) {
1508 _cleanup_(set_free_freep
) Set
*syscalls
= NULL
;
1509 _cleanup_fclose_
FILE *f
= NULL
;
1512 /* Let's read the available system calls from the list of available tracing events. Slightly dirty, but good
1513 * enough for analysis purposes. */
1515 f
= fopen("/sys/kernel/debug/tracing/available_events", "re");
1517 return log_full_errno(IN_SET(errno
, EPERM
, EACCES
, ENOENT
) ? LOG_DEBUG
: LOG_WARNING
, errno
, "Can't read open /sys/kernel/debug/tracing/available_events: %m");
1520 _cleanup_free_
char *line
= NULL
;
1523 r
= read_line(f
, LONG_LINE_MAX
, &line
);
1525 return log_error_errno(r
, "Failed to read system call list: %m");
1529 e
= startswith(line
, "syscalls:sys_enter_");
1533 /* These are named differently inside the kernel than their external name for historical reasons. Let's hide them here. */
1534 if (STR_IN_SET(e
, "newuname", "newfstat", "newstat", "newlstat", "sysctl"))
1537 r
= set_ensure_allocated(&syscalls
, &string_hash_ops
);
1541 r
= set_put_strdup(syscalls
, e
);
1543 return log_error_errno(r
, "Failed to add system call to list: %m");
1546 *ret
= TAKE_PTR(syscalls
);
1550 static void kernel_syscalls_remove(Set
*s
, const SyscallFilterSet
*set
) {
1551 const char *syscall
;
1553 NULSTR_FOREACH(syscall
, set
->value
) {
1554 if (syscall
[0] == '@')
1557 (void) set_remove(s
, syscall
);
1561 static void dump_syscall_filter(const SyscallFilterSet
*set
) {
1562 const char *syscall
;
1571 NULSTR_FOREACH(syscall
, set
->value
)
1573 syscall
[0] == '@' ? ansi_underline() : "",
1578 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1581 (void) pager_open(arg_pager_flags
);
1583 if (strv_isempty(strv_skip(argv
, 1))) {
1584 _cleanup_(set_free_freep
) Set
*kernel
= NULL
;
1587 k
= load_kernel_syscalls(&kernel
);
1589 for (i
= 0; i
< _SYSCALL_FILTER_SET_MAX
; i
++) {
1590 const SyscallFilterSet
*set
= syscall_filter_sets
+ i
;
1594 dump_syscall_filter(set
);
1595 kernel_syscalls_remove(kernel
, set
);
1600 fputc('\n', stdout
);
1602 log_notice_errno(k
, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
1603 } else if (!set_isempty(kernel
)) {
1604 const char *syscall
;
1608 "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n",
1609 ansi_highlight(), ansi_normal());
1611 SET_FOREACH(syscall
, kernel
, j
)
1612 printf("# %s\n", syscall
);
1617 STRV_FOREACH(name
, strv_skip(argv
, 1)) {
1618 const SyscallFilterSet
*set
;
1623 set
= syscall_filter_set_find(*name
);
1625 /* make sure the error appears below normal output */
1628 log_error("Filter set \"%s\" not found.", *name
);
1632 dump_syscall_filter(set
);
1641 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
)
1642 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1643 "Not compiled with syscall filters, sorry.");
1646 static int dump_timespan(int argc
, char *argv
[], void *userdata
) {
1647 char **input_timespan
;
1649 STRV_FOREACH(input_timespan
, strv_skip(argv
, 1)) {
1651 usec_t usec_magnitude
= 1, output_usecs
;
1652 char ft_buf
[FORMAT_TIMESPAN_MAX
];
1654 r
= parse_time(*input_timespan
, &output_usecs
, USEC_PER_SEC
);
1656 return log_error_errno(r
, "Failed to parse time span '%s': %m", *input_timespan
);
1658 printf("Original: %s\n", *input_timespan
);
1659 printf(" %ss: %" PRIu64
"\n", special_glyph(MU
), output_usecs
);
1660 printf(" Human: %s\n", format_timespan(ft_buf
, sizeof(ft_buf
), output_usecs
, usec_magnitude
));
1662 if (input_timespan
[1])
1666 return EXIT_SUCCESS
;
1669 static int test_calendar(int argc
, char *argv
[], void *userdata
) {
1674 n
= now(CLOCK_REALTIME
);
1676 STRV_FOREACH(p
, strv_skip(argv
, 1)) {
1677 _cleanup_(calendar_spec_freep
) CalendarSpec
*spec
= NULL
;
1678 _cleanup_free_
char *t
= NULL
;
1681 r
= calendar_spec_from_string(*p
, &spec
);
1683 ret
= log_error_errno(r
, "Failed to parse calendar specification '%s': %m", *p
);
1687 r
= calendar_spec_normalize(spec
);
1689 ret
= log_error_errno(r
, "Failed to normalize calendar specification '%s': %m", *p
);
1693 r
= calendar_spec_to_string(spec
, &t
);
1695 ret
= log_error_errno(r
, "Failed to format calendar specification '%s': %m", *p
);
1700 printf(" Original form: %s\n", *p
);
1702 printf("Normalized form: %s\n", t
);
1704 r
= calendar_spec_next_usec(spec
, n
, &next
);
1706 printf(" Next elapse: never\n");
1708 ret
= log_error_errno(r
, "Failed to determine next elapse for '%s': %m", *p
);
1711 char buffer
[CONST_MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_TIMESTAMP_RELATIVE_MAX
)];
1713 printf(" Next elapse: %s\n", format_timestamp(buffer
, sizeof(buffer
), next
));
1715 if (!in_utc_timezone())
1716 printf(" (in UTC): %s\n", format_timestamp_utc(buffer
, sizeof(buffer
), next
));
1718 printf(" From now: %s\n", format_timestamp_relative(buffer
, sizeof(buffer
), next
));
1728 static int service_watchdogs(int argc
, char *argv
[], void *userdata
) {
1729 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1730 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1733 assert(IN_SET(argc
, 1, 2));
1736 r
= acquire_bus(&bus
, NULL
);
1738 return log_error_errno(r
, "Failed to create bus connection: %m");
1740 /* get ServiceWatchdogs */
1742 r
= sd_bus_get_property_trivial(
1744 "org.freedesktop.systemd1",
1745 "/org/freedesktop/systemd1",
1746 "org.freedesktop.systemd1.Manager",
1752 return log_error_errno(r
, "Failed to get service-watchdog state: %s", bus_error_message(&error
, r
));
1754 printf("%s\n", yes_no(!!b
));
1759 /* set ServiceWatchdogs */
1760 b
= parse_boolean(argv
[1]);
1762 log_error("Failed to parse service-watchdogs argument.");
1766 r
= sd_bus_set_property(
1768 "org.freedesktop.systemd1",
1769 "/org/freedesktop/systemd1",
1770 "org.freedesktop.systemd1.Manager",
1776 return log_error_errno(r
, "Failed to set service-watchdog state: %s", bus_error_message(&error
, r
));
1781 static int do_verify(int argc
, char *argv
[], void *userdata
) {
1782 return verify_units(strv_skip(argv
, 1), arg_scope
, arg_man
, arg_generators
);
1785 static int help(int argc
, char *argv
[], void *userdata
) {
1786 _cleanup_free_
char *link
= NULL
;
1789 (void) pager_open(arg_pager_flags
);
1791 r
= terminal_urlify_man("systemd-analyze", "1", &link
);
1795 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1796 "Profile systemd, show unit dependencies, check unit files.\n\n"
1797 " -h --help Show this help\n"
1798 " --version Show package version\n"
1799 " --no-pager Do not pipe output into a pager\n"
1800 " --system Operate on system systemd instance\n"
1801 " --user Operate on user systemd instance\n"
1802 " --global Operate on global user configuration\n"
1803 " -H --host=[USER@]HOST Operate on remote host\n"
1804 " -M --machine=CONTAINER Operate on local container\n"
1805 " --order Show only order in the graph\n"
1806 " --require Show only requirement in the graph\n"
1807 " --from-pattern=GLOB Show only origins in the graph\n"
1808 " --to-pattern=GLOB Show only destinations in the graph\n"
1809 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1810 " earlier than the latest in the branch\n"
1811 " --man[=BOOL] Do [not] check for existence of man pages\n"
1812 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n"
1814 " time Print time spent in the kernel\n"
1815 " blame Print list of running units ordered by time to init\n"
1816 " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
1817 " plot Output SVG graphic showing service initialization\n"
1818 " dot [UNIT...] Output dependency graph in man:dot(1) format\n"
1819 " log-level [LEVEL] Get/set logging threshold for manager\n"
1820 " log-target [TARGET] Get/set logging target for manager\n"
1821 " dump Output state serialization of service manager\n"
1822 " cat-config Show configuration file and drop-ins\n"
1823 " unit-paths List load directories for units\n"
1824 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1825 " verify FILE... Check unit files for correctness\n"
1826 " calendar SPEC... Validate repetitive calendar time events\n"
1827 " service-watchdogs [BOOL] Get/set service watchdog state\n"
1828 " timespan SPAN... Validate a time span\n"
1829 "\nSee the %s for details.\n"
1830 , program_invocation_short_name
1834 /* When updating this list, including descriptions, apply changes to shell-completion/bash/systemd-analyze and
1835 * shell-completion/zsh/_systemd-analyze too. */
1840 static int parse_argv(int argc
, char *argv
[]) {
1842 ARG_VERSION
= 0x100,
1849 ARG_DOT_FROM_PATTERN
,
1857 static const struct option options
[] = {
1858 { "help", no_argument
, NULL
, 'h' },
1859 { "version", no_argument
, NULL
, ARG_VERSION
},
1860 { "order", no_argument
, NULL
, ARG_ORDER
},
1861 { "require", no_argument
, NULL
, ARG_REQUIRE
},
1862 { "root", required_argument
, NULL
, ARG_ROOT
},
1863 { "system", no_argument
, NULL
, ARG_SYSTEM
},
1864 { "user", no_argument
, NULL
, ARG_USER
},
1865 { "global", no_argument
, NULL
, ARG_GLOBAL
},
1866 { "from-pattern", required_argument
, NULL
, ARG_DOT_FROM_PATTERN
},
1867 { "to-pattern", required_argument
, NULL
, ARG_DOT_TO_PATTERN
},
1868 { "fuzz", required_argument
, NULL
, ARG_FUZZ
},
1869 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1870 { "man", optional_argument
, NULL
, ARG_MAN
},
1871 { "generators", optional_argument
, NULL
, ARG_GENERATORS
},
1872 { "host", required_argument
, NULL
, 'H' },
1873 { "machine", required_argument
, NULL
, 'M' },
1882 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
1886 return help(0, NULL
, NULL
);
1896 arg_scope
= UNIT_FILE_SYSTEM
;
1900 arg_scope
= UNIT_FILE_USER
;
1904 arg_scope
= UNIT_FILE_GLOBAL
;
1908 arg_dot
= DEP_ORDER
;
1912 arg_dot
= DEP_REQUIRE
;
1915 case ARG_DOT_FROM_PATTERN
:
1916 if (strv_extend(&arg_dot_from_patterns
, optarg
) < 0)
1921 case ARG_DOT_TO_PATTERN
:
1922 if (strv_extend(&arg_dot_to_patterns
, optarg
) < 0)
1928 r
= parse_sec(optarg
, &arg_fuzz
);
1934 arg_pager_flags
|= PAGER_DISABLE
;
1938 arg_transport
= BUS_TRANSPORT_REMOTE
;
1943 arg_transport
= BUS_TRANSPORT_MACHINE
;
1949 r
= parse_boolean(optarg
);
1951 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1952 "Failed to parse --man= argument.");
1960 case ARG_GENERATORS
:
1962 r
= parse_boolean(optarg
);
1964 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1965 "Failed to parse --generators= argument.");
1969 arg_generators
= true;
1977 assert_not_reached("Unhandled option code.");
1980 if (arg_scope
== UNIT_FILE_GLOBAL
&&
1981 !STR_IN_SET(argv
[optind
] ?: "time", "dot", "unit-paths", "verify"))
1982 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1983 "Option --global only makes sense with verbs dot, unit-paths, verify.");
1985 if (arg_root
&& !streq_ptr(argv
[optind
], "cat-config"))
1986 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1987 "Option --root is only supported for cat-config right now.");
1989 return 1; /* work to do */
1992 static int run(int argc
, char *argv
[]) {
1994 static const Verb verbs
[] = {
1995 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
1996 { "time", VERB_ANY
, 1, VERB_DEFAULT
, analyze_time
},
1997 { "blame", VERB_ANY
, 1, 0, analyze_blame
},
1998 { "critical-chain", VERB_ANY
, VERB_ANY
, 0, analyze_critical_chain
},
1999 { "plot", VERB_ANY
, 1, 0, analyze_plot
},
2000 { "dot", VERB_ANY
, VERB_ANY
, 0, dot
},
2001 { "log-level", VERB_ANY
, 2, 0, get_or_set_log_level
},
2002 { "log-target", VERB_ANY
, 2, 0, get_or_set_log_target
},
2003 /* The following four verbs are deprecated aliases */
2004 { "set-log-level", 2, 2, 0, set_log_level
},
2005 { "get-log-level", VERB_ANY
, 1, 0, get_log_level
},
2006 { "set-log-target", 2, 2, 0, set_log_target
},
2007 { "get-log-target", VERB_ANY
, 1, 0, get_log_target
},
2008 { "dump", VERB_ANY
, 1, 0, dump
},
2009 { "cat-config", 2, VERB_ANY
, 0, cat_config
},
2010 { "unit-paths", 1, 1, 0, dump_unit_paths
},
2011 { "syscall-filter", VERB_ANY
, VERB_ANY
, 0, dump_syscall_filters
},
2012 { "verify", 2, VERB_ANY
, 0, do_verify
},
2013 { "calendar", 2, VERB_ANY
, 0, test_calendar
},
2014 { "service-watchdogs", VERB_ANY
, 2, 0, service_watchdogs
},
2015 { "timespan", 2, VERB_ANY
, 0, dump_timespan
},
2021 setlocale(LC_ALL
, "");
2022 setlocale(LC_NUMERIC
, "C"); /* we want to format/parse floats in C style */
2024 log_parse_environment();
2027 r
= parse_argv(argc
, argv
);
2031 return dispatch_verb(argc
, argv
, verbs
, NULL
);
2034 DEFINE_MAIN_FUNCTION(run
);