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"
52 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
53 #define SCALE_Y (20.0)
55 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
57 #define svg(...) printf(__VA_ARGS__)
59 #define svg_bar(class, x1, x2, y) \
60 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
62 SCALE_X * (x1), SCALE_Y * (y), \
63 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
65 #define svg_text(b, x, y, format, ...) \
67 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
68 svg(format, ## __VA_ARGS__); \
77 static char** arg_dot_from_patterns
= NULL
;
78 static char** arg_dot_to_patterns
= NULL
;
79 static usec_t arg_fuzz
= 0;
80 static bool arg_no_pager
= false;
81 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
82 static const char *arg_host
= NULL
;
83 static UnitFileScope arg_scope
= UNIT_FILE_SYSTEM
;
84 static bool arg_man
= true;
85 static bool arg_generators
= false;
91 usec_t kernel_done_time
;
93 usec_t userspace_time
;
95 usec_t security_start_time
;
96 usec_t security_finish_time
;
97 usec_t generators_start_time
;
98 usec_t generators_finish_time
;
99 usec_t unitsload_start_time
;
100 usec_t unitsload_finish_time
;
103 * If we're analyzing the user instance, all timestamps will be offset
104 * by its own start-up timestamp, which may be arbitrarily big.
105 * With "plot", this causes arbitrarily wide output SVG files which almost
106 * completely consist of empty space. Thus we cancel out this offset.
108 * This offset is subtracted from times above by acquire_boot_times(),
109 * but it still needs to be subtracted from unit-specific timestamps
110 * (so it is stored here for reference).
112 usec_t reverse_offset
;
127 char *kernel_release
;
128 char *kernel_version
;
129 char *os_pretty_name
;
130 char *virtualization
;
134 static int acquire_bus(bool need_full_bus
, sd_bus
**bus
) {
135 bool user
= arg_scope
!= UNIT_FILE_SYSTEM
;
138 return bus_connect_transport(arg_transport
, arg_host
, user
, bus
);
140 return bus_connect_transport_systemd(arg_transport
, arg_host
, user
, bus
);
143 static int bus_get_uint64_property(sd_bus
*bus
, const char *path
, const char *interface
, const char *property
, uint64_t *val
) {
144 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
153 r
= sd_bus_get_property_trivial(
155 "org.freedesktop.systemd1",
163 log_error("Failed to parse reply: %s", bus_error_message(&error
, -r
));
170 static int bus_get_unit_property_strv(sd_bus
*bus
, const char *path
, const char *property
, char ***strv
) {
171 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
179 r
= sd_bus_get_property_strv(
181 "org.freedesktop.systemd1",
183 "org.freedesktop.systemd1.Unit",
188 log_error("Failed to get unit property %s: %s", property
, bus_error_message(&error
, -r
));
195 static int compare_unit_time(const void *a
, const void *b
) {
196 return compare(((struct unit_times
*)b
)->time
,
197 ((struct unit_times
*)a
)->time
);
200 static int compare_unit_start(const void *a
, const void *b
) {
201 return compare(((struct unit_times
*)a
)->activating
,
202 ((struct unit_times
*)b
)->activating
);
205 static void free_unit_times(struct unit_times
*t
, unsigned n
) {
206 struct unit_times
*p
;
208 for (p
= t
; p
< t
+ n
; p
++)
214 static void subtract_timestamp(usec_t
*a
, usec_t b
) {
223 static int acquire_boot_times(sd_bus
*bus
, struct boot_times
**bt
) {
224 static struct boot_times times
;
225 static bool cached
= false;
230 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
232 if (bus_get_uint64_property(bus
,
233 "/org/freedesktop/systemd1",
234 "org.freedesktop.systemd1.Manager",
235 "FirmwareTimestampMonotonic",
236 ×
.firmware_time
) < 0 ||
237 bus_get_uint64_property(bus
,
238 "/org/freedesktop/systemd1",
239 "org.freedesktop.systemd1.Manager",
240 "LoaderTimestampMonotonic",
241 ×
.loader_time
) < 0 ||
242 bus_get_uint64_property(bus
,
243 "/org/freedesktop/systemd1",
244 "org.freedesktop.systemd1.Manager",
246 ×
.kernel_time
) < 0 ||
247 bus_get_uint64_property(bus
,
248 "/org/freedesktop/systemd1",
249 "org.freedesktop.systemd1.Manager",
250 "InitRDTimestampMonotonic",
251 ×
.initrd_time
) < 0 ||
252 bus_get_uint64_property(bus
,
253 "/org/freedesktop/systemd1",
254 "org.freedesktop.systemd1.Manager",
255 "UserspaceTimestampMonotonic",
256 ×
.userspace_time
) < 0 ||
257 bus_get_uint64_property(bus
,
258 "/org/freedesktop/systemd1",
259 "org.freedesktop.systemd1.Manager",
260 "FinishTimestampMonotonic",
261 ×
.finish_time
) < 0 ||
262 bus_get_uint64_property(bus
,
263 "/org/freedesktop/systemd1",
264 "org.freedesktop.systemd1.Manager",
265 "SecurityStartTimestampMonotonic",
266 ×
.security_start_time
) < 0 ||
267 bus_get_uint64_property(bus
,
268 "/org/freedesktop/systemd1",
269 "org.freedesktop.systemd1.Manager",
270 "SecurityFinishTimestampMonotonic",
271 ×
.security_finish_time
) < 0 ||
272 bus_get_uint64_property(bus
,
273 "/org/freedesktop/systemd1",
274 "org.freedesktop.systemd1.Manager",
275 "GeneratorsStartTimestampMonotonic",
276 ×
.generators_start_time
) < 0 ||
277 bus_get_uint64_property(bus
,
278 "/org/freedesktop/systemd1",
279 "org.freedesktop.systemd1.Manager",
280 "GeneratorsFinishTimestampMonotonic",
281 ×
.generators_finish_time
) < 0 ||
282 bus_get_uint64_property(bus
,
283 "/org/freedesktop/systemd1",
284 "org.freedesktop.systemd1.Manager",
285 "UnitsLoadStartTimestampMonotonic",
286 ×
.unitsload_start_time
) < 0 ||
287 bus_get_uint64_property(bus
,
288 "/org/freedesktop/systemd1",
289 "org.freedesktop.systemd1.Manager",
290 "UnitsLoadFinishTimestampMonotonic",
291 ×
.unitsload_finish_time
) < 0)
294 if (times
.finish_time
<= 0) {
295 log_error("Bootup is not yet finished. Please try again later.");
299 if (arg_scope
== UNIT_FILE_SYSTEM
) {
300 if (times
.initrd_time
> 0)
301 times
.kernel_done_time
= times
.initrd_time
;
303 times
.kernel_done_time
= times
.userspace_time
;
306 * User-instance-specific timestamps processing
307 * (see comment to reverse_offset in struct boot_times).
309 times
.reverse_offset
= times
.userspace_time
;
311 times
.firmware_time
= times
.loader_time
= times
.kernel_time
= times
.initrd_time
= times
.userspace_time
= 0;
312 subtract_timestamp(×
.finish_time
, times
.reverse_offset
);
314 subtract_timestamp(×
.security_start_time
, times
.reverse_offset
);
315 subtract_timestamp(×
.security_finish_time
, times
.reverse_offset
);
317 subtract_timestamp(×
.generators_start_time
, times
.reverse_offset
);
318 subtract_timestamp(×
.generators_finish_time
, times
.reverse_offset
);
320 subtract_timestamp(×
.unitsload_start_time
, times
.reverse_offset
);
321 subtract_timestamp(×
.unitsload_finish_time
, times
.reverse_offset
);
331 static void free_host_info(struct host_info
*hi
) {
337 free(hi
->kernel_name
);
338 free(hi
->kernel_release
);
339 free(hi
->kernel_version
);
340 free(hi
->os_pretty_name
);
341 free(hi
->virtualization
);
342 free(hi
->architecture
);
346 DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info
*, free_host_info
);
348 static int acquire_time_data(sd_bus
*bus
, struct unit_times
**out
) {
349 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
350 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
352 struct boot_times
*boot_times
= NULL
;
353 struct unit_times
*unit_times
= NULL
;
357 r
= acquire_boot_times(bus
, &boot_times
);
361 r
= sd_bus_call_method(
363 "org.freedesktop.systemd1",
364 "/org/freedesktop/systemd1",
365 "org.freedesktop.systemd1.Manager",
370 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
374 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
376 bus_log_parse_error(r
);
380 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
381 struct unit_times
*t
;
383 if (!GREEDY_REALLOC(unit_times
, size
, c
+1)) {
391 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
393 if (bus_get_uint64_property(bus
, u
.unit_path
,
394 "org.freedesktop.systemd1.Unit",
395 "InactiveExitTimestampMonotonic",
396 &t
->activating
) < 0 ||
397 bus_get_uint64_property(bus
, u
.unit_path
,
398 "org.freedesktop.systemd1.Unit",
399 "ActiveEnterTimestampMonotonic",
400 &t
->activated
) < 0 ||
401 bus_get_uint64_property(bus
, u
.unit_path
,
402 "org.freedesktop.systemd1.Unit",
403 "ActiveExitTimestampMonotonic",
404 &t
->deactivating
) < 0 ||
405 bus_get_uint64_property(bus
, u
.unit_path
,
406 "org.freedesktop.systemd1.Unit",
407 "InactiveEnterTimestampMonotonic",
408 &t
->deactivated
) < 0) {
413 subtract_timestamp(&t
->activating
, boot_times
->reverse_offset
);
414 subtract_timestamp(&t
->activated
, boot_times
->reverse_offset
);
415 subtract_timestamp(&t
->deactivating
, boot_times
->reverse_offset
);
416 subtract_timestamp(&t
->deactivated
, boot_times
->reverse_offset
);
418 if (t
->activated
>= t
->activating
)
419 t
->time
= t
->activated
- t
->activating
;
420 else if (t
->deactivated
>= t
->activating
)
421 t
->time
= t
->deactivated
- t
->activating
;
425 if (t
->activating
== 0)
428 t
->name
= strdup(u
.id
);
436 bus_log_parse_error(r
);
444 free_unit_times(unit_times
, (unsigned) c
);
448 static int acquire_host_info(sd_bus
*bus
, struct host_info
**hi
) {
449 static const struct bus_properties_map hostname_map
[] = {
450 { "Hostname", "s", NULL
, offsetof(struct host_info
, hostname
) },
451 { "KernelName", "s", NULL
, offsetof(struct host_info
, kernel_name
) },
452 { "KernelRelease", "s", NULL
, offsetof(struct host_info
, kernel_release
) },
453 { "KernelVersion", "s", NULL
, offsetof(struct host_info
, kernel_version
) },
454 { "OperatingSystemPrettyName", "s", NULL
, offsetof(struct host_info
, os_pretty_name
) },
458 static const struct bus_properties_map manager_map
[] = {
459 { "Virtualization", "s", NULL
, offsetof(struct host_info
, virtualization
) },
460 { "Architecture", "s", NULL
, offsetof(struct host_info
, architecture
) },
464 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
465 _cleanup_(free_host_infop
) struct host_info
*host
;
468 host
= new0(struct host_info
, 1);
472 r
= bus_map_all_properties(bus
,
473 "org.freedesktop.hostname1",
474 "/org/freedesktop/hostname1",
479 log_debug_errno(r
, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error
, r
));
481 r
= bus_map_all_properties(bus
,
482 "org.freedesktop.systemd1",
483 "/org/freedesktop/systemd1",
488 return log_error_errno(r
, "Failed to get host information from systemd: %s", bus_error_message(&error
, r
));
496 static int pretty_boot_time(sd_bus
*bus
, char **_buf
) {
497 char ts
[FORMAT_TIMESPAN_MAX
];
498 struct boot_times
*t
;
499 static char buf
[4096];
503 usec_t activated_time
= USEC_INFINITY
;
504 _cleanup_free_
char* path
= NULL
, *unit_id
= NULL
;
505 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
507 r
= acquire_boot_times(bus
, &t
);
511 path
= unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET
);
515 r
= sd_bus_get_property_string(
517 "org.freedesktop.systemd1",
519 "org.freedesktop.systemd1.Unit",
524 log_error_errno(r
, "default.target doesn't seem to exist: %s", bus_error_message(&error
, r
));
528 r
= bus_get_uint64_property(bus
, path
,
529 "org.freedesktop.systemd1.Unit",
530 "ActiveEnterTimestampMonotonic",
533 log_info_errno(r
, "Could not get time to reach default.target. Continuing...");
534 activated_time
= USEC_INFINITY
;
540 size
= strpcpyf(&ptr
, size
, "Startup finished in ");
541 if (t
->firmware_time
> 0)
542 size
= strpcpyf(&ptr
, size
, "%s (firmware) + ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
- t
->loader_time
, USEC_PER_MSEC
));
543 if (t
->loader_time
> 0)
544 size
= strpcpyf(&ptr
, size
, "%s (loader) + ", format_timespan(ts
, sizeof(ts
), t
->loader_time
, USEC_PER_MSEC
));
545 if (t
->kernel_time
> 0)
546 size
= strpcpyf(&ptr
, size
, "%s (kernel) + ", format_timespan(ts
, sizeof(ts
), t
->kernel_done_time
, USEC_PER_MSEC
));
547 if (t
->initrd_time
> 0)
548 size
= strpcpyf(&ptr
, size
, "%s (initrd) + ", format_timespan(ts
, sizeof(ts
), t
->userspace_time
- t
->initrd_time
, USEC_PER_MSEC
));
550 size
= strpcpyf(&ptr
, size
, "%s (userspace) ", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
551 if (t
->kernel_time
> 0)
552 strpcpyf(&ptr
, size
, "= %s", format_timespan(ts
, sizeof(ts
), t
->firmware_time
+ t
->finish_time
, USEC_PER_MSEC
));
554 if (unit_id
&& activated_time
> 0 && activated_time
!= USEC_INFINITY
)
555 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
));
556 else if (unit_id
&& activated_time
== 0)
557 size
= strpcpyf(&ptr
, size
, "\n%s was never reached", unit_id
);
558 else if (unit_id
&& activated_time
== USEC_INFINITY
)
559 size
= strpcpyf(&ptr
, size
, "\nCould not get time to reach %s.",unit_id
);
561 size
= strpcpyf(&ptr
, size
, "\ncould not find default.target");
572 static void svg_graph_box(double height
, double begin
, double end
) {
575 /* outside box, fill */
576 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
577 SCALE_X
* (end
- begin
), SCALE_Y
* height
);
579 for (i
= ((long long) (begin
/ 100000)) * 100000; i
<= end
; i
+=100000) {
580 /* lines for each second */
581 if (i
% 5000000 == 0)
582 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
583 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
584 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
585 else if (i
% 1000000 == 0)
586 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
587 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
588 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
590 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
591 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
);
595 static int analyze_plot(int argc
, char *argv
[], void *userdata
) {
596 _cleanup_(free_host_infop
) struct host_info
*host
= NULL
;
597 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
598 struct unit_times
*times
;
599 struct boot_times
*boot
;
600 int n
, m
= 1, y
= 0, r
;
602 _cleanup_free_
char *pretty_times
= NULL
;
603 struct unit_times
*u
;
605 r
= acquire_bus(true, &bus
);
607 return log_error_errno(r
, "Failed to create bus connection: %m");
609 n
= acquire_boot_times(bus
, &boot
);
613 n
= pretty_boot_time(bus
, &pretty_times
);
617 n
= acquire_host_info(bus
, &host
);
621 n
= acquire_time_data(bus
, ×
);
625 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_start
);
627 width
= SCALE_X
* (boot
->firmware_time
+ boot
->finish_time
);
631 if (boot
->firmware_time
> boot
->loader_time
)
633 if (boot
->loader_time
> 0) {
638 if (boot
->initrd_time
> 0)
640 if (boot
->kernel_time
> 0)
643 for (u
= times
; u
< times
+ n
; u
++) {
644 double text_start
, text_width
;
646 if (u
->activating
< boot
->userspace_time
||
647 u
->activating
> boot
->finish_time
) {
648 u
->name
= mfree(u
->name
);
652 /* If the text cannot fit on the left side then
653 * increase the svg width so it fits on the right.
654 * TODO: calculate the text width more accurately */
655 text_width
= 8.0 * strlen(u
->name
);
656 text_start
= (boot
->firmware_time
+ u
->activating
) * SCALE_X
;
657 if (text_width
> text_start
&& text_width
+ text_start
> width
)
658 width
= text_width
+ text_start
;
660 if (u
->deactivated
> u
->activating
&& u
->deactivated
<= boot
->finish_time
661 && u
->activated
== 0 && u
->deactivating
== 0)
662 u
->activated
= u
->deactivating
= u
->deactivated
;
663 if (u
->activated
< u
->activating
|| u
->activated
> boot
->finish_time
)
664 u
->activated
= boot
->finish_time
;
665 if (u
->deactivating
< u
->activated
|| u
->activated
> boot
->finish_time
)
666 u
->deactivating
= boot
->finish_time
;
667 if (u
->deactivated
< u
->deactivating
|| u
->deactivated
> boot
->finish_time
)
668 u
->deactivated
= boot
->finish_time
;
672 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
673 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
674 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
676 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
677 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
678 80.0 + width
, 150.0 + (m
* SCALE_Y
) +
679 5 * SCALE_Y
/* legend */);
681 /* write some basic info as a comment, including some help */
682 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
683 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
684 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
685 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
686 "<!-- point your browser to this file. -->\n\n"
687 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION
);
690 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
691 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
692 " rect.background { fill: rgb(255,255,255); }\n"
693 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
694 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
695 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
696 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
697 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
698 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
699 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
700 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
701 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
702 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
703 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
704 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
705 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
707 " line.sec5 { stroke-width: 2; }\n"
708 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
709 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
710 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
711 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
712 " text.sec { font-size: 10px; }\n"
713 " ]]>\n </style>\n</defs>\n\n");
715 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
716 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times
);
717 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
718 isempty(host
->os_pretty_name
) ? "Linux" : host
->os_pretty_name
,
719 strempty(host
->hostname
),
720 strempty(host
->kernel_name
),
721 strempty(host
->kernel_release
),
722 strempty(host
->kernel_version
),
723 strempty(host
->architecture
),
724 strempty(host
->virtualization
));
726 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X
* boot
->firmware_time
));
727 svg_graph_box(m
, -(double) boot
->firmware_time
, boot
->finish_time
);
729 if (boot
->firmware_time
> 0) {
730 svg_bar("firmware", -(double) boot
->firmware_time
, -(double) boot
->loader_time
, y
);
731 svg_text(true, -(double) boot
->firmware_time
, y
, "firmware");
734 if (boot
->loader_time
> 0) {
735 svg_bar("loader", -(double) boot
->loader_time
, 0, y
);
736 svg_text(true, -(double) boot
->loader_time
, y
, "loader");
739 if (boot
->kernel_time
> 0) {
740 svg_bar("kernel", 0, boot
->kernel_done_time
, y
);
741 svg_text(true, 0, y
, "kernel");
744 if (boot
->initrd_time
> 0) {
745 svg_bar("initrd", boot
->initrd_time
, boot
->userspace_time
, y
);
746 svg_text(true, boot
->initrd_time
, y
, "initrd");
749 svg_bar("active", boot
->userspace_time
, boot
->finish_time
, y
);
750 svg_bar("security", boot
->security_start_time
, boot
->security_finish_time
, y
);
751 svg_bar("generators", boot
->generators_start_time
, boot
->generators_finish_time
, y
);
752 svg_bar("unitsload", boot
->unitsload_start_time
, boot
->unitsload_finish_time
, y
);
753 svg_text(true, boot
->userspace_time
, y
, "systemd");
756 for (u
= times
; u
< times
+ n
; u
++) {
757 char ts
[FORMAT_TIMESPAN_MAX
];
763 svg_bar("activating", u
->activating
, u
->activated
, y
);
764 svg_bar("active", u
->activated
, u
->deactivating
, y
);
765 svg_bar("deactivating", u
->deactivating
, u
->deactivated
, y
);
767 /* place the text on the left if we have passed the half of the svg width */
768 b
= u
->activating
* SCALE_X
< width
/ 2;
770 svg_text(b
, u
->activating
, y
, "%s (%s)",
771 u
->name
, format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
));
773 svg_text(b
, u
->activating
, y
, "%s", u
->name
);
780 svg("<g transform=\"translate(20,100)\">\n");
782 svg_bar("activating", 0, 300000, y
);
783 svg_text(true, 400000, y
, "Activating");
785 svg_bar("active", 0, 300000, y
);
786 svg_text(true, 400000, y
, "Active");
788 svg_bar("deactivating", 0, 300000, y
);
789 svg_text(true, 400000, y
, "Deactivating");
791 svg_bar("security", 0, 300000, y
);
792 svg_text(true, 400000, y
, "Setting up security module");
794 svg_bar("generators", 0, 300000, y
);
795 svg_text(true, 400000, y
, "Generators");
797 svg_bar("unitsload", 0, 300000, y
);
798 svg_text(true, 400000, y
, "Loading unit files");
805 free_unit_times(times
, (unsigned) n
);
811 static int list_dependencies_print(const char *name
, unsigned int level
, unsigned int branches
,
812 bool last
, struct unit_times
*times
, struct boot_times
*boot
) {
814 char ts
[FORMAT_TIMESPAN_MAX
], ts2
[FORMAT_TIMESPAN_MAX
];
816 for (i
= level
; i
!= 0; i
--)
817 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? TREE_VERTICAL
: TREE_SPACE
));
819 printf("%s", special_glyph(last
? TREE_RIGHT
: TREE_BRANCH
));
823 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
824 format_timespan(ts
, sizeof(ts
), times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
825 format_timespan(ts2
, sizeof(ts2
), times
->time
, USEC_PER_MSEC
), ansi_normal());
826 else if (times
->activated
> boot
->userspace_time
)
827 printf("%s @%s", name
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
837 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
838 _cleanup_free_
char *path
= NULL
;
844 path
= unit_dbus_path_from_name(name
);
848 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
851 static Hashmap
*unit_times_hashmap
;
853 static int list_dependencies_compare(const void *_a
, const void *_b
) {
854 const char **a
= (const char**) _a
, **b
= (const char**) _b
;
855 usec_t usa
= 0, usb
= 0;
856 struct unit_times
*times
;
858 times
= hashmap_get(unit_times_hashmap
, *a
);
860 usa
= times
->activated
;
861 times
= hashmap_get(unit_times_hashmap
, *b
);
863 usb
= times
->activated
;
868 static bool times_in_range(const struct unit_times
*times
, const struct boot_times
*boot
) {
870 times
->activated
> 0 && times
->activated
<= boot
->finish_time
;
873 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned int level
, char ***units
,
874 unsigned int branches
) {
875 _cleanup_strv_free_
char **deps
= NULL
;
878 usec_t service_longest
= 0;
880 struct unit_times
*times
;
881 struct boot_times
*boot
;
883 if (strv_extend(units
, name
))
886 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
890 qsort_safe(deps
, strv_length(deps
), sizeof (char*), list_dependencies_compare
);
892 r
= acquire_boot_times(bus
, &boot
);
896 STRV_FOREACH(c
, deps
) {
897 times
= hashmap_get(unit_times_hashmap
, *c
);
898 if (times_in_range(times
, boot
) &&
899 (times
->activated
>= service_longest
900 || service_longest
== 0)) {
901 service_longest
= times
->activated
;
906 if (service_longest
== 0)
909 STRV_FOREACH(c
, deps
) {
910 times
= hashmap_get(unit_times_hashmap
, *c
);
911 if (times_in_range(times
, boot
) &&
912 service_longest
- times
->activated
<= arg_fuzz
)
919 STRV_FOREACH(c
, deps
) {
920 times
= hashmap_get(unit_times_hashmap
, *c
);
921 if (!times_in_range(times
, boot
) ||
922 service_longest
- times
->activated
> arg_fuzz
)
927 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
931 if (strv_contains(*units
, *c
)) {
932 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
939 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
,
940 (branches
<< 1) | (to_print
? 1 : 0));
950 static int list_dependencies(sd_bus
*bus
, const char *name
) {
951 _cleanup_strv_free_
char **units
= NULL
;
952 char ts
[FORMAT_TIMESPAN_MAX
];
953 struct unit_times
*times
;
956 _cleanup_free_
char *path
= NULL
;
957 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
958 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
959 struct boot_times
*boot
;
963 path
= unit_dbus_path_from_name(name
);
967 r
= sd_bus_get_property(
969 "org.freedesktop.systemd1",
971 "org.freedesktop.systemd1.Unit",
977 log_error("Failed to get ID: %s", bus_error_message(&error
, -r
));
981 r
= sd_bus_message_read(reply
, "s", &id
);
983 return bus_log_parse_error(r
);
985 times
= hashmap_get(unit_times_hashmap
, id
);
987 r
= acquire_boot_times(bus
, &boot
);
993 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
994 format_timespan(ts
, sizeof(ts
), times
->time
, USEC_PER_MSEC
), ansi_normal());
995 else if (times
->activated
> boot
->userspace_time
)
996 printf("%s @%s\n", id
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
1001 return list_dependencies_one(bus
, name
, 0, &units
, 0);
1004 static int analyze_critical_chain(int argc
, char *argv
[], void *userdata
) {
1005 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1006 struct unit_times
*times
;
1011 r
= acquire_bus(false, &bus
);
1013 return log_error_errno(r
, "Failed to create bus connection: %m");
1015 n
= acquire_time_data(bus
, ×
);
1019 h
= hashmap_new(&string_hash_ops
);
1023 for (i
= 0; i
< (unsigned) n
; i
++) {
1024 r
= hashmap_put(h
, times
[i
].name
, ×
[i
]);
1026 return log_error_errno(r
, "Failed to add entry to hashmap: %m");
1028 unit_times_hashmap
= h
;
1030 pager_open(arg_no_pager
, false);
1032 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1033 "The time the unit takes to start is printed after the \"+\" character.\n");
1037 STRV_FOREACH(name
, strv_skip(argv
, 1))
1038 list_dependencies(bus
, *name
);
1040 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);
1042 h
= hashmap_free(h
);
1043 free_unit_times(times
, (unsigned) n
);
1047 static int analyze_blame(int argc
, char *argv
[], void *userdata
) {
1048 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1049 struct unit_times
*times
;
1053 r
= acquire_bus(false, &bus
);
1055 return log_error_errno(r
, "Failed to create bus connection: %m");
1057 n
= acquire_time_data(bus
, ×
);
1061 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_time
);
1063 pager_open(arg_no_pager
, false);
1065 for (i
= 0; i
< (unsigned) n
; i
++) {
1066 char ts
[FORMAT_TIMESPAN_MAX
];
1068 if (times
[i
].time
> 0)
1069 printf("%16s %s\n", format_timespan(ts
, sizeof(ts
), times
[i
].time
, USEC_PER_MSEC
), times
[i
].name
);
1072 free_unit_times(times
, (unsigned) n
);
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(false, &bus
);
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(false, &bus
);
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("Failed to list units: %s", bus_error_message(&error
, -r
));
1245 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1247 return bus_log_parse_error(r
);
1249 printf("digraph systemd {\n");
1251 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
1253 r
= graph_one(bus
, &u
, expanded_patterns
, expanded_from_patterns
, expanded_to_patterns
);
1258 return bus_log_parse_error(r
);
1262 log_info(" Color legend: black = Requires\n"
1263 " dark blue = Requisite\n"
1264 " dark grey = Wants\n"
1265 " red = Conflicts\n"
1266 " green = After\n");
1269 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1270 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1275 static int dump(int argc
, char *argv
[], void *userdata
) {
1276 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1277 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1278 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1279 const char *text
= NULL
;
1282 r
= acquire_bus(false, &bus
);
1284 return log_error_errno(r
, "Failed to create bus connection: %m");
1286 pager_open(arg_no_pager
, false);
1288 r
= sd_bus_call_method(
1290 "org.freedesktop.systemd1",
1291 "/org/freedesktop/systemd1",
1292 "org.freedesktop.systemd1.Manager",
1298 return log_error_errno(r
, "Failed issue method call: %s", bus_error_message(&error
, r
));
1300 r
= sd_bus_message_read(reply
, "s", &text
);
1302 return bus_log_parse_error(r
);
1304 fputs(text
, stdout
);
1308 static int set_log_level(int argc
, char *argv
[], void *userdata
) {
1309 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1310 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1316 r
= acquire_bus(false, &bus
);
1318 return log_error_errno(r
, "Failed to create bus connection: %m");
1320 r
= sd_bus_set_property(
1322 "org.freedesktop.systemd1",
1323 "/org/freedesktop/systemd1",
1324 "org.freedesktop.systemd1.Manager",
1330 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1335 static int get_log_level(int argc
, char *argv
[], void *userdata
) {
1336 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1337 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1338 _cleanup_free_
char *level
= NULL
;
1341 r
= acquire_bus(false, &bus
);
1343 return log_error_errno(r
, "Failed to create bus connection: %m");
1345 r
= sd_bus_get_property_string(
1347 "org.freedesktop.systemd1",
1348 "/org/freedesktop/systemd1",
1349 "org.freedesktop.systemd1.Manager",
1354 return log_error_errno(r
, "Failed to get log level: %s", bus_error_message(&error
, r
));
1360 static int get_or_set_log_level(int argc
, char *argv
[], void *userdata
) {
1361 return (argc
== 1) ? get_log_level(argc
, argv
, userdata
) : set_log_level(argc
, argv
, userdata
);
1364 static int set_log_target(int argc
, char *argv
[], void *userdata
) {
1365 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1366 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1372 r
= acquire_bus(false, &bus
);
1374 return log_error_errno(r
, "Failed to create bus connection: %m");
1376 r
= sd_bus_set_property(
1378 "org.freedesktop.systemd1",
1379 "/org/freedesktop/systemd1",
1380 "org.freedesktop.systemd1.Manager",
1386 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1391 static int get_log_target(int argc
, char *argv
[], void *userdata
) {
1392 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1393 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1394 _cleanup_free_
char *target
= NULL
;
1397 r
= acquire_bus(false, &bus
);
1399 return log_error_errno(r
, "Failed to create bus connection: %m");
1401 r
= sd_bus_get_property_string(
1403 "org.freedesktop.systemd1",
1404 "/org/freedesktop/systemd1",
1405 "org.freedesktop.systemd1.Manager",
1410 return log_error_errno(r
, "Failed to get log target: %s", bus_error_message(&error
, r
));
1416 static int get_or_set_log_target(int argc
, char *argv
[], void *userdata
) {
1417 return (argc
== 1) ? get_log_target(argc
, argv
, userdata
) : set_log_target(argc
, argv
, userdata
);
1420 static int dump_unit_paths(int argc
, char *argv
[], void *userdata
) {
1421 _cleanup_lookup_paths_free_ LookupPaths paths
= {};
1425 r
= lookup_paths_init(&paths
, arg_scope
, 0, NULL
);
1427 return log_error_errno(r
, "lookup_paths_init() failed: %m");
1429 STRV_FOREACH(p
, paths
.search_path
)
1436 static void dump_syscall_filter(const SyscallFilterSet
*set
) {
1437 const char *syscall
;
1439 printf("%s\n", set
->name
);
1440 printf(" # %s\n", set
->help
);
1441 NULSTR_FOREACH(syscall
, set
->value
)
1442 printf(" %s\n", syscall
);
1445 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1448 pager_open(arg_no_pager
, false);
1450 if (strv_isempty(strv_skip(argv
, 1))) {
1453 for (i
= 0; i
< _SYSCALL_FILTER_SET_MAX
; i
++) {
1456 dump_syscall_filter(syscall_filter_sets
+ i
);
1462 STRV_FOREACH(name
, strv_skip(argv
, 1)) {
1463 const SyscallFilterSet
*set
;
1468 set
= syscall_filter_set_find(*name
);
1470 /* make sure the error appears below normal output */
1473 log_error("Filter set \"%s\" not found.", *name
);
1477 dump_syscall_filter(set
);
1486 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1487 log_error("Not compiled with syscall filters, sorry.");
1492 static int test_calendar(int argc
, char *argv
[], void *userdata
) {
1497 n
= now(CLOCK_REALTIME
);
1499 STRV_FOREACH(p
, strv_skip(argv
, 1)) {
1500 _cleanup_(calendar_spec_freep
) CalendarSpec
*spec
= NULL
;
1501 _cleanup_free_
char *t
= NULL
;
1504 r
= calendar_spec_from_string(*p
, &spec
);
1506 ret
= log_error_errno(r
, "Failed to parse calendar specification '%s': %m", *p
);
1510 r
= calendar_spec_normalize(spec
);
1512 ret
= log_error_errno(r
, "Failed to normalize calendar specification '%s': %m", *p
);
1516 r
= calendar_spec_to_string(spec
, &t
);
1518 ret
= log_error_errno(r
, "Failed to fomat calendar specification '%s': %m", *p
);
1523 printf(" Original form: %s\n", *p
);
1525 printf("Normalized form: %s\n", t
);
1527 r
= calendar_spec_next_usec(spec
, n
, &next
);
1529 printf(" Next elapse: never\n");
1531 ret
= log_error_errno(r
, "Failed to determine next elapse for '%s': %m", *p
);
1534 char buffer
[CONST_MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_TIMESTAMP_RELATIVE_MAX
)];
1536 printf(" Next elapse: %s\n", format_timestamp(buffer
, sizeof(buffer
), next
));
1538 if (!in_utc_timezone())
1539 printf(" (in UTC): %s\n", format_timestamp_utc(buffer
, sizeof(buffer
), next
));
1541 printf(" From now: %s\n", format_timestamp_relative(buffer
, sizeof(buffer
), next
));
1551 static int service_watchdogs(int argc
, char *argv
[], void *userdata
) {
1552 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1553 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1556 assert(IN_SET(argc
, 1, 2));
1559 r
= acquire_bus(false, &bus
);
1561 return log_error_errno(r
, "Failed to create bus connection: %m");
1563 /* get ServiceWatchdogs */
1565 r
= sd_bus_get_property_trivial(
1567 "org.freedesktop.systemd1",
1568 "/org/freedesktop/systemd1",
1569 "org.freedesktop.systemd1.Manager",
1575 return log_error_errno(r
, "Failed to get service-watchdog state: %s", bus_error_message(&error
, r
));
1577 printf("%s\n", yes_no(!!b
));
1582 /* set ServiceWatchdogs */
1583 b
= parse_boolean(argv
[1]);
1585 log_error("Failed to parse service-watchdogs argument.");
1589 r
= sd_bus_set_property(
1591 "org.freedesktop.systemd1",
1592 "/org/freedesktop/systemd1",
1593 "org.freedesktop.systemd1.Manager",
1599 return log_error_errno(r
, "Failed to set service-watchdog state: %s", bus_error_message(&error
, r
));
1604 static int do_verify(int argc
, char *argv
[], void *userdata
) {
1605 return verify_units(strv_skip(argv
, 1), arg_scope
, arg_man
, arg_generators
);
1608 static int help(int argc
, char *argv
[], void *userdata
) {
1610 pager_open(arg_no_pager
, false);
1612 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1613 "Profile systemd, show unit dependencies, check unit files.\n\n"
1614 " -h --help Show this help\n"
1615 " --version Show package version\n"
1616 " --no-pager Do not pipe output into a pager\n"
1617 " --system Operate on system systemd instance\n"
1618 " --user Operate on user systemd instance\n"
1619 " --global Operate on global user configuration\n"
1620 " -H --host=[USER@]HOST Operate on remote host\n"
1621 " -M --machine=CONTAINER Operate on local container\n"
1622 " --order Show only order in the graph\n"
1623 " --require Show only requirement in the graph\n"
1624 " --from-pattern=GLOB Show only origins in the graph\n"
1625 " --to-pattern=GLOB Show only destinations in the graph\n"
1626 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1627 " earlier than the latest in the branch\n"
1628 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1629 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
1631 " time Print time spent in the kernel\n"
1632 " blame Print list of running units ordered by time to init\n"
1633 " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
1634 " plot Output SVG graphic showing service initialization\n"
1635 " dot [UNIT...] Output dependency graph in man:dot(1) format\n"
1636 " log-level [LEVEL] Get/set logging threshold for manager\n"
1637 " log-target [TARGET] Get/set logging target for manager\n"
1638 " dump Output state serialization of service manager\n"
1639 " unit-paths List load directories for units\n"
1640 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1641 " verify FILE... Check unit files for correctness\n"
1642 " calendar SPEC... Validate repetitive calendar time events\n"
1643 " service-watchdogs [BOOL] Get/set service watchdog state\n"
1644 , program_invocation_short_name
);
1646 /* When updating this list, including descriptions, apply
1647 * changes to shell-completion/bash/systemd-analyze and
1648 * shell-completion/zsh/_systemd-analyze too. */
1653 static int parse_argv(int argc
, char *argv
[]) {
1655 ARG_VERSION
= 0x100,
1661 ARG_DOT_FROM_PATTERN
,
1669 static const struct option options
[] = {
1670 { "help", no_argument
, NULL
, 'h' },
1671 { "version", no_argument
, NULL
, ARG_VERSION
},
1672 { "order", no_argument
, NULL
, ARG_ORDER
},
1673 { "require", no_argument
, NULL
, ARG_REQUIRE
},
1674 { "system", no_argument
, NULL
, ARG_SYSTEM
},
1675 { "user", no_argument
, NULL
, ARG_USER
},
1676 { "global", no_argument
, NULL
, ARG_GLOBAL
},
1677 { "from-pattern", required_argument
, NULL
, ARG_DOT_FROM_PATTERN
},
1678 { "to-pattern", required_argument
, NULL
, ARG_DOT_TO_PATTERN
},
1679 { "fuzz", required_argument
, NULL
, ARG_FUZZ
},
1680 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1681 { "man", optional_argument
, NULL
, ARG_MAN
},
1682 { "generators", optional_argument
, NULL
, ARG_GENERATORS
},
1683 { "host", required_argument
, NULL
, 'H' },
1684 { "machine", required_argument
, NULL
, 'M' },
1693 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
1697 return help(0, NULL
, NULL
);
1703 arg_scope
= UNIT_FILE_SYSTEM
;
1707 arg_scope
= UNIT_FILE_USER
;
1711 arg_scope
= UNIT_FILE_GLOBAL
;
1715 arg_dot
= DEP_ORDER
;
1719 arg_dot
= DEP_REQUIRE
;
1722 case ARG_DOT_FROM_PATTERN
:
1723 if (strv_extend(&arg_dot_from_patterns
, optarg
) < 0)
1728 case ARG_DOT_TO_PATTERN
:
1729 if (strv_extend(&arg_dot_to_patterns
, optarg
) < 0)
1735 r
= parse_sec(optarg
, &arg_fuzz
);
1741 arg_no_pager
= true;
1745 arg_transport
= BUS_TRANSPORT_REMOTE
;
1750 arg_transport
= BUS_TRANSPORT_MACHINE
;
1756 r
= parse_boolean(optarg
);
1758 log_error("Failed to parse --man= argument.");
1768 case ARG_GENERATORS
:
1770 r
= parse_boolean(optarg
);
1772 log_error("Failed to parse --generators= argument.");
1776 arg_generators
= !!r
;
1778 arg_generators
= true;
1786 assert_not_reached("Unhandled option code.");
1789 if (arg_scope
== UNIT_FILE_GLOBAL
&&
1790 !STR_IN_SET(argv
[optind
] ?: "time", "dot", "unit-paths", "verify")) {
1791 log_error("Option --global only makes sense with verbs dot, unit-paths, verify.");
1795 return 1; /* work to do */
1798 int main(int argc
, char *argv
[]) {
1800 static const Verb verbs
[] = {
1801 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
1802 { "time", VERB_ANY
, 1, VERB_DEFAULT
, analyze_time
},
1803 { "blame", VERB_ANY
, 1, 0, analyze_blame
},
1804 { "critical-chain", VERB_ANY
, VERB_ANY
, 0, analyze_critical_chain
},
1805 { "plot", VERB_ANY
, 1, 0, analyze_plot
},
1806 { "dot", VERB_ANY
, VERB_ANY
, 0, dot
},
1807 { "log-level", VERB_ANY
, 2, 0, get_or_set_log_level
},
1808 { "log-target", VERB_ANY
, 2, 0, get_or_set_log_target
},
1809 /* The following four verbs are deprecated aliases */
1810 { "set-log-level", 2, 2, 0, set_log_level
},
1811 { "get-log-level", VERB_ANY
, 1, 0, get_log_level
},
1812 { "set-log-target", 2, 2, 0, set_log_target
},
1813 { "get-log-target", VERB_ANY
, 1, 0, get_log_target
},
1814 { "dump", VERB_ANY
, 1, 0, dump
},
1815 { "unit-paths", 1, 1, 0, dump_unit_paths
},
1816 { "syscall-filter", VERB_ANY
, VERB_ANY
, 0, dump_syscall_filters
},
1817 { "verify", 2, VERB_ANY
, 0, do_verify
},
1818 { "calendar", 2, VERB_ANY
, 0, test_calendar
},
1819 { "service-watchdogs", VERB_ANY
, 2, 0, service_watchdogs
},
1825 setlocale(LC_ALL
, "");
1826 setlocale(LC_NUMERIC
, "C"); /* we want to format/parse floats in C style */
1828 log_parse_environment();
1831 r
= parse_argv(argc
, argv
);
1835 r
= dispatch_verb(argc
, argv
, verbs
, NULL
);
1840 strv_free(arg_dot_from_patterns
);
1841 strv_free(arg_dot_to_patterns
);
1843 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;