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",
481 log_debug_errno(r
, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error
, r
));
483 r
= bus_map_all_properties(bus
,
484 "org.freedesktop.systemd1",
485 "/org/freedesktop/systemd1",
492 return log_error_errno(r
, "Failed to get host information from systemd: %s", bus_error_message(&error
, r
));
500 static int pretty_boot_time(sd_bus
*bus
, char **_buf
) {
501 char ts
[FORMAT_TIMESPAN_MAX
];
502 struct boot_times
*t
;
503 static char buf
[4096];
507 usec_t activated_time
= USEC_INFINITY
;
508 _cleanup_free_
char* path
= NULL
, *unit_id
= NULL
;
509 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
511 r
= acquire_boot_times(bus
, &t
);
515 path
= unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET
);
519 r
= sd_bus_get_property_string(
521 "org.freedesktop.systemd1",
523 "org.freedesktop.systemd1.Unit",
528 log_error_errno(r
, "default.target doesn't seem to exist: %s", bus_error_message(&error
, r
));
532 r
= bus_get_uint64_property(bus
, path
,
533 "org.freedesktop.systemd1.Unit",
534 "ActiveEnterTimestampMonotonic",
537 log_info_errno(r
, "Could not get time to reach default.target. Continuing...");
538 activated_time
= USEC_INFINITY
;
544 size
= strpcpyf(&ptr
, size
, "Startup finished in ");
545 if (t
->firmware_time
> 0)
546 size
= strpcpyf(&ptr
, size
, "%s (firmware) + ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
- t
->loader_time
, USEC_PER_MSEC
));
547 if (t
->loader_time
> 0)
548 size
= strpcpyf(&ptr
, size
, "%s (loader) + ", format_timespan(ts
, sizeof(ts
), t
->loader_time
, USEC_PER_MSEC
));
549 if (t
->kernel_time
> 0)
550 size
= strpcpyf(&ptr
, size
, "%s (kernel) + ", format_timespan(ts
, sizeof(ts
), t
->kernel_done_time
, USEC_PER_MSEC
));
551 if (t
->initrd_time
> 0)
552 size
= strpcpyf(&ptr
, size
, "%s (initrd) + ", format_timespan(ts
, sizeof(ts
), t
->userspace_time
- t
->initrd_time
, USEC_PER_MSEC
));
554 size
= strpcpyf(&ptr
, size
, "%s (userspace) ", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
555 if (t
->kernel_time
> 0)
556 strpcpyf(&ptr
, size
, "= %s", format_timespan(ts
, sizeof(ts
), t
->firmware_time
+ t
->finish_time
, USEC_PER_MSEC
));
558 if (unit_id
&& activated_time
> 0 && activated_time
!= USEC_INFINITY
)
559 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
));
560 else if (unit_id
&& activated_time
== 0)
561 size
= strpcpyf(&ptr
, size
, "\n%s was never reached", unit_id
);
562 else if (unit_id
&& activated_time
== USEC_INFINITY
)
563 size
= strpcpyf(&ptr
, size
, "\nCould not get time to reach %s.",unit_id
);
565 size
= strpcpyf(&ptr
, size
, "\ncould not find default.target");
576 static void svg_graph_box(double height
, double begin
, double end
) {
579 /* outside box, fill */
580 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
581 SCALE_X
* (end
- begin
), SCALE_Y
* height
);
583 for (i
= ((long long) (begin
/ 100000)) * 100000; i
<= end
; i
+=100000) {
584 /* lines for each second */
585 if (i
% 5000000 == 0)
586 svg(" <line class=\"sec5\" 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
);
589 else if (i
% 1000000 == 0)
590 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
591 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
592 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
594 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
595 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
);
599 static int analyze_plot(int argc
, char *argv
[], void *userdata
) {
600 _cleanup_(free_host_infop
) struct host_info
*host
= NULL
;
601 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
602 struct unit_times
*times
;
603 struct boot_times
*boot
;
604 int n
, m
= 1, y
= 0, r
;
606 _cleanup_free_
char *pretty_times
= NULL
;
607 struct unit_times
*u
;
609 r
= acquire_bus(true, &bus
);
611 return log_error_errno(r
, "Failed to create bus connection: %m");
613 n
= acquire_boot_times(bus
, &boot
);
617 n
= pretty_boot_time(bus
, &pretty_times
);
621 n
= acquire_host_info(bus
, &host
);
625 n
= acquire_time_data(bus
, ×
);
629 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_start
);
631 width
= SCALE_X
* (boot
->firmware_time
+ boot
->finish_time
);
635 if (boot
->firmware_time
> boot
->loader_time
)
637 if (boot
->loader_time
> 0) {
642 if (boot
->initrd_time
> 0)
644 if (boot
->kernel_time
> 0)
647 for (u
= times
; u
< times
+ n
; u
++) {
648 double text_start
, text_width
;
650 if (u
->activating
< boot
->userspace_time
||
651 u
->activating
> boot
->finish_time
) {
652 u
->name
= mfree(u
->name
);
656 /* If the text cannot fit on the left side then
657 * increase the svg width so it fits on the right.
658 * TODO: calculate the text width more accurately */
659 text_width
= 8.0 * strlen(u
->name
);
660 text_start
= (boot
->firmware_time
+ u
->activating
) * SCALE_X
;
661 if (text_width
> text_start
&& text_width
+ text_start
> width
)
662 width
= text_width
+ text_start
;
664 if (u
->deactivated
> u
->activating
&& u
->deactivated
<= boot
->finish_time
665 && u
->activated
== 0 && u
->deactivating
== 0)
666 u
->activated
= u
->deactivating
= u
->deactivated
;
667 if (u
->activated
< u
->activating
|| u
->activated
> boot
->finish_time
)
668 u
->activated
= boot
->finish_time
;
669 if (u
->deactivating
< u
->activated
|| u
->activated
> boot
->finish_time
)
670 u
->deactivating
= boot
->finish_time
;
671 if (u
->deactivated
< u
->deactivating
|| u
->deactivated
> boot
->finish_time
)
672 u
->deactivated
= boot
->finish_time
;
676 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
677 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
678 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
680 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
681 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
682 80.0 + width
, 150.0 + (m
* SCALE_Y
) +
683 5 * SCALE_Y
/* legend */);
685 /* write some basic info as a comment, including some help */
686 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
687 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
688 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
689 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
690 "<!-- point your browser to this file. -->\n\n"
691 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION
);
694 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
695 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
696 " rect.background { fill: rgb(255,255,255); }\n"
697 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
698 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
699 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
700 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
701 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
702 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
703 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
704 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
705 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
706 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
707 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
708 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
709 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
711 " line.sec5 { stroke-width: 2; }\n"
712 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
713 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
714 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
715 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
716 " text.sec { font-size: 10px; }\n"
717 " ]]>\n </style>\n</defs>\n\n");
719 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
720 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times
);
721 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
722 isempty(host
->os_pretty_name
) ? "Linux" : host
->os_pretty_name
,
723 strempty(host
->hostname
),
724 strempty(host
->kernel_name
),
725 strempty(host
->kernel_release
),
726 strempty(host
->kernel_version
),
727 strempty(host
->architecture
),
728 strempty(host
->virtualization
));
730 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X
* boot
->firmware_time
));
731 svg_graph_box(m
, -(double) boot
->firmware_time
, boot
->finish_time
);
733 if (boot
->firmware_time
> 0) {
734 svg_bar("firmware", -(double) boot
->firmware_time
, -(double) boot
->loader_time
, y
);
735 svg_text(true, -(double) boot
->firmware_time
, y
, "firmware");
738 if (boot
->loader_time
> 0) {
739 svg_bar("loader", -(double) boot
->loader_time
, 0, y
);
740 svg_text(true, -(double) boot
->loader_time
, y
, "loader");
743 if (boot
->kernel_time
> 0) {
744 svg_bar("kernel", 0, boot
->kernel_done_time
, y
);
745 svg_text(true, 0, y
, "kernel");
748 if (boot
->initrd_time
> 0) {
749 svg_bar("initrd", boot
->initrd_time
, boot
->userspace_time
, y
);
750 svg_text(true, boot
->initrd_time
, y
, "initrd");
753 svg_bar("active", boot
->userspace_time
, boot
->finish_time
, y
);
754 svg_bar("security", boot
->security_start_time
, boot
->security_finish_time
, y
);
755 svg_bar("generators", boot
->generators_start_time
, boot
->generators_finish_time
, y
);
756 svg_bar("unitsload", boot
->unitsload_start_time
, boot
->unitsload_finish_time
, y
);
757 svg_text(true, boot
->userspace_time
, y
, "systemd");
760 for (u
= times
; u
< times
+ n
; u
++) {
761 char ts
[FORMAT_TIMESPAN_MAX
];
767 svg_bar("activating", u
->activating
, u
->activated
, y
);
768 svg_bar("active", u
->activated
, u
->deactivating
, y
);
769 svg_bar("deactivating", u
->deactivating
, u
->deactivated
, y
);
771 /* place the text on the left if we have passed the half of the svg width */
772 b
= u
->activating
* SCALE_X
< width
/ 2;
774 svg_text(b
, u
->activating
, y
, "%s (%s)",
775 u
->name
, format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
));
777 svg_text(b
, u
->activating
, y
, "%s", u
->name
);
784 svg("<g transform=\"translate(20,100)\">\n");
786 svg_bar("activating", 0, 300000, y
);
787 svg_text(true, 400000, y
, "Activating");
789 svg_bar("active", 0, 300000, y
);
790 svg_text(true, 400000, y
, "Active");
792 svg_bar("deactivating", 0, 300000, y
);
793 svg_text(true, 400000, y
, "Deactivating");
795 svg_bar("security", 0, 300000, y
);
796 svg_text(true, 400000, y
, "Setting up security module");
798 svg_bar("generators", 0, 300000, y
);
799 svg_text(true, 400000, y
, "Generators");
801 svg_bar("unitsload", 0, 300000, y
);
802 svg_text(true, 400000, y
, "Loading unit files");
809 free_unit_times(times
, (unsigned) n
);
815 static int list_dependencies_print(const char *name
, unsigned int level
, unsigned int branches
,
816 bool last
, struct unit_times
*times
, struct boot_times
*boot
) {
818 char ts
[FORMAT_TIMESPAN_MAX
], ts2
[FORMAT_TIMESPAN_MAX
];
820 for (i
= level
; i
!= 0; i
--)
821 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? TREE_VERTICAL
: TREE_SPACE
));
823 printf("%s", special_glyph(last
? TREE_RIGHT
: TREE_BRANCH
));
827 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
828 format_timespan(ts
, sizeof(ts
), times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
829 format_timespan(ts2
, sizeof(ts2
), times
->time
, USEC_PER_MSEC
), ansi_normal());
830 else if (times
->activated
> boot
->userspace_time
)
831 printf("%s @%s", name
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
841 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
842 _cleanup_free_
char *path
= NULL
;
848 path
= unit_dbus_path_from_name(name
);
852 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
855 static Hashmap
*unit_times_hashmap
;
857 static int list_dependencies_compare(const void *_a
, const void *_b
) {
858 const char **a
= (const char**) _a
, **b
= (const char**) _b
;
859 usec_t usa
= 0, usb
= 0;
860 struct unit_times
*times
;
862 times
= hashmap_get(unit_times_hashmap
, *a
);
864 usa
= times
->activated
;
865 times
= hashmap_get(unit_times_hashmap
, *b
);
867 usb
= times
->activated
;
872 static bool times_in_range(const struct unit_times
*times
, const struct boot_times
*boot
) {
874 times
->activated
> 0 && times
->activated
<= boot
->finish_time
;
877 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned int level
, char ***units
,
878 unsigned int branches
) {
879 _cleanup_strv_free_
char **deps
= NULL
;
882 usec_t service_longest
= 0;
884 struct unit_times
*times
;
885 struct boot_times
*boot
;
887 if (strv_extend(units
, name
))
890 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
894 qsort_safe(deps
, strv_length(deps
), sizeof (char*), list_dependencies_compare
);
896 r
= acquire_boot_times(bus
, &boot
);
900 STRV_FOREACH(c
, deps
) {
901 times
= hashmap_get(unit_times_hashmap
, *c
);
902 if (times_in_range(times
, boot
) &&
903 (times
->activated
>= service_longest
904 || service_longest
== 0)) {
905 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 log_error("Failed to get ID: %s", bus_error_message(&error
, -r
));
985 r
= sd_bus_message_read(reply
, "s", &id
);
987 return bus_log_parse_error(r
);
989 times
= hashmap_get(unit_times_hashmap
, id
);
991 r
= acquire_boot_times(bus
, &boot
);
997 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
998 format_timespan(ts
, sizeof(ts
), times
->time
, USEC_PER_MSEC
), ansi_normal());
999 else if (times
->activated
> boot
->userspace_time
)
1000 printf("%s @%s\n", id
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
1005 return list_dependencies_one(bus
, name
, 0, &units
, 0);
1008 static int analyze_critical_chain(int argc
, char *argv
[], void *userdata
) {
1009 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1010 struct unit_times
*times
;
1015 r
= acquire_bus(false, &bus
);
1017 return log_error_errno(r
, "Failed to create bus connection: %m");
1019 n
= acquire_time_data(bus
, ×
);
1023 h
= hashmap_new(&string_hash_ops
);
1027 for (i
= 0; i
< (unsigned) n
; i
++) {
1028 r
= hashmap_put(h
, times
[i
].name
, ×
[i
]);
1030 return log_error_errno(r
, "Failed to add entry to hashmap: %m");
1032 unit_times_hashmap
= h
;
1034 (void) pager_open(arg_no_pager
, false);
1036 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1037 "The time the unit takes to start is printed after the \"+\" character.\n");
1041 STRV_FOREACH(name
, strv_skip(argv
, 1))
1042 list_dependencies(bus
, *name
);
1044 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);
1046 h
= hashmap_free(h
);
1047 free_unit_times(times
, (unsigned) n
);
1051 static int analyze_blame(int argc
, char *argv
[], void *userdata
) {
1052 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1053 struct unit_times
*times
;
1057 r
= acquire_bus(false, &bus
);
1059 return log_error_errno(r
, "Failed to create bus connection: %m");
1061 n
= acquire_time_data(bus
, ×
);
1065 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_time
);
1067 (void) pager_open(arg_no_pager
, false);
1069 for (i
= 0; i
< (unsigned) n
; i
++) {
1070 char ts
[FORMAT_TIMESPAN_MAX
];
1072 if (times
[i
].time
> 0)
1073 printf("%16s %s\n", format_timespan(ts
, sizeof(ts
), times
[i
].time
, USEC_PER_MSEC
), times
[i
].name
);
1076 free_unit_times(times
, (unsigned) n
);
1080 static int analyze_time(int argc
, char *argv
[], void *userdata
) {
1081 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1082 _cleanup_free_
char *buf
= NULL
;
1085 r
= acquire_bus(false, &bus
);
1087 return log_error_errno(r
, "Failed to create bus connection: %m");
1089 r
= pretty_boot_time(bus
, &buf
);
1097 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
[]) {
1098 _cleanup_strv_free_
char **units
= NULL
;
1101 bool match_patterns
;
1107 match_patterns
= strv_fnmatch(patterns
, u
->id
, 0);
1109 if (!strv_isempty(from_patterns
) &&
1111 !strv_fnmatch(from_patterns
, u
->id
, 0))
1114 r
= bus_get_unit_property_strv(bus
, u
->unit_path
, prop
, &units
);
1118 STRV_FOREACH(unit
, units
) {
1119 bool match_patterns2
;
1121 match_patterns2
= strv_fnmatch(patterns
, *unit
, 0);
1123 if (!strv_isempty(to_patterns
) &&
1125 !strv_fnmatch(to_patterns
, *unit
, 0))
1128 if (!strv_isempty(patterns
) && !match_patterns
&& !match_patterns2
)
1131 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u
->id
, *unit
, color
);
1137 static int graph_one(sd_bus
*bus
, const UnitInfo
*u
, char *patterns
[], char *from_patterns
[], char *to_patterns
[]) {
1143 if (IN_SET(arg_dot
, DEP_ORDER
, DEP_ALL
)) {
1144 r
= graph_one_property(bus
, u
, "After", "green", patterns
, from_patterns
, to_patterns
);
1149 if (IN_SET(arg_dot
, DEP_REQUIRE
, DEP_ALL
)) {
1150 r
= graph_one_property(bus
, u
, "Requires", "black", patterns
, from_patterns
, to_patterns
);
1153 r
= graph_one_property(bus
, u
, "Requisite", "darkblue", patterns
, from_patterns
, to_patterns
);
1156 r
= graph_one_property(bus
, u
, "Wants", "grey66", patterns
, from_patterns
, to_patterns
);
1159 r
= graph_one_property(bus
, u
, "Conflicts", "red", patterns
, from_patterns
, to_patterns
);
1167 static int expand_patterns(sd_bus
*bus
, char **patterns
, char ***ret
) {
1168 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1172 STRV_FOREACH(pattern
, patterns
) {
1173 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1174 _cleanup_free_
char *unit
= NULL
, *unit_id
= NULL
;
1176 if (strv_extend(&expanded_patterns
, *pattern
) < 0)
1179 if (string_is_glob(*pattern
))
1182 unit
= unit_dbus_path_from_name(*pattern
);
1186 r
= sd_bus_get_property_string(
1188 "org.freedesktop.systemd1",
1190 "org.freedesktop.systemd1.Unit",
1195 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
1197 if (!streq(*pattern
, unit_id
)) {
1198 if (strv_extend(&expanded_patterns
, unit_id
) < 0)
1203 *ret
= expanded_patterns
;
1204 expanded_patterns
= NULL
; /* do not free */
1209 static int dot(int argc
, char *argv
[], void *userdata
) {
1210 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1211 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1212 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1213 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1214 _cleanup_strv_free_
char **expanded_from_patterns
= NULL
;
1215 _cleanup_strv_free_
char **expanded_to_patterns
= NULL
;
1219 r
= acquire_bus(false, &bus
);
1221 return log_error_errno(r
, "Failed to create bus connection: %m");
1223 r
= expand_patterns(bus
, strv_skip(argv
, 1), &expanded_patterns
);
1227 r
= expand_patterns(bus
, arg_dot_from_patterns
, &expanded_from_patterns
);
1231 r
= expand_patterns(bus
, arg_dot_to_patterns
, &expanded_to_patterns
);
1235 r
= sd_bus_call_method(
1237 "org.freedesktop.systemd1",
1238 "/org/freedesktop/systemd1",
1239 "org.freedesktop.systemd1.Manager",
1245 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
1249 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1251 return bus_log_parse_error(r
);
1253 printf("digraph systemd {\n");
1255 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
1257 r
= graph_one(bus
, &u
, expanded_patterns
, expanded_from_patterns
, expanded_to_patterns
);
1262 return bus_log_parse_error(r
);
1266 log_info(" Color legend: black = Requires\n"
1267 " dark blue = Requisite\n"
1268 " dark grey = Wants\n"
1269 " red = Conflicts\n"
1270 " green = After\n");
1273 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1274 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1279 static int dump(int argc
, char *argv
[], void *userdata
) {
1280 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1281 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1282 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1283 const char *text
= NULL
;
1286 r
= acquire_bus(false, &bus
);
1288 return log_error_errno(r
, "Failed to create bus connection: %m");
1290 (void) pager_open(arg_no_pager
, false);
1292 r
= sd_bus_call_method(
1294 "org.freedesktop.systemd1",
1295 "/org/freedesktop/systemd1",
1296 "org.freedesktop.systemd1.Manager",
1302 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1304 r
= sd_bus_message_read(reply
, "s", &text
);
1306 return bus_log_parse_error(r
);
1308 fputs(text
, stdout
);
1312 static int set_log_level(int argc
, char *argv
[], void *userdata
) {
1313 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1314 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1320 r
= acquire_bus(false, &bus
);
1322 return log_error_errno(r
, "Failed to create bus connection: %m");
1324 r
= sd_bus_set_property(
1326 "org.freedesktop.systemd1",
1327 "/org/freedesktop/systemd1",
1328 "org.freedesktop.systemd1.Manager",
1334 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1339 static int get_log_level(int argc
, char *argv
[], void *userdata
) {
1340 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1341 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1342 _cleanup_free_
char *level
= NULL
;
1345 r
= acquire_bus(false, &bus
);
1347 return log_error_errno(r
, "Failed to create bus connection: %m");
1349 r
= sd_bus_get_property_string(
1351 "org.freedesktop.systemd1",
1352 "/org/freedesktop/systemd1",
1353 "org.freedesktop.systemd1.Manager",
1358 return log_error_errno(r
, "Failed to get log level: %s", bus_error_message(&error
, r
));
1364 static int get_or_set_log_level(int argc
, char *argv
[], void *userdata
) {
1365 return (argc
== 1) ? get_log_level(argc
, argv
, userdata
) : set_log_level(argc
, argv
, userdata
);
1368 static int set_log_target(int argc
, char *argv
[], void *userdata
) {
1369 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1370 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1376 r
= acquire_bus(false, &bus
);
1378 return log_error_errno(r
, "Failed to create bus connection: %m");
1380 r
= sd_bus_set_property(
1382 "org.freedesktop.systemd1",
1383 "/org/freedesktop/systemd1",
1384 "org.freedesktop.systemd1.Manager",
1390 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1395 static int get_log_target(int argc
, char *argv
[], void *userdata
) {
1396 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1397 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1398 _cleanup_free_
char *target
= NULL
;
1401 r
= acquire_bus(false, &bus
);
1403 return log_error_errno(r
, "Failed to create bus connection: %m");
1405 r
= sd_bus_get_property_string(
1407 "org.freedesktop.systemd1",
1408 "/org/freedesktop/systemd1",
1409 "org.freedesktop.systemd1.Manager",
1414 return log_error_errno(r
, "Failed to get log target: %s", bus_error_message(&error
, r
));
1420 static int get_or_set_log_target(int argc
, char *argv
[], void *userdata
) {
1421 return (argc
== 1) ? get_log_target(argc
, argv
, userdata
) : set_log_target(argc
, argv
, userdata
);
1424 static int dump_unit_paths(int argc
, char *argv
[], void *userdata
) {
1425 _cleanup_lookup_paths_free_ LookupPaths paths
= {};
1429 r
= lookup_paths_init(&paths
, arg_scope
, 0, NULL
);
1431 return log_error_errno(r
, "lookup_paths_init() failed: %m");
1433 STRV_FOREACH(p
, paths
.search_path
)
1440 static void dump_syscall_filter(const SyscallFilterSet
*set
) {
1441 const char *syscall
;
1443 printf("%s\n", set
->name
);
1444 printf(" # %s\n", set
->help
);
1445 NULSTR_FOREACH(syscall
, set
->value
)
1446 printf(" %s\n", syscall
);
1449 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1452 (void) pager_open(arg_no_pager
, false);
1454 if (strv_isempty(strv_skip(argv
, 1))) {
1457 for (i
= 0; i
< _SYSCALL_FILTER_SET_MAX
; i
++) {
1460 dump_syscall_filter(syscall_filter_sets
+ i
);
1466 STRV_FOREACH(name
, strv_skip(argv
, 1)) {
1467 const SyscallFilterSet
*set
;
1472 set
= syscall_filter_set_find(*name
);
1474 /* make sure the error appears below normal output */
1477 log_error("Filter set \"%s\" not found.", *name
);
1481 dump_syscall_filter(set
);
1490 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1491 log_error("Not compiled with syscall filters, sorry.");
1496 static int test_calendar(int argc
, char *argv
[], void *userdata
) {
1501 n
= now(CLOCK_REALTIME
);
1503 STRV_FOREACH(p
, strv_skip(argv
, 1)) {
1504 _cleanup_(calendar_spec_freep
) CalendarSpec
*spec
= NULL
;
1505 _cleanup_free_
char *t
= NULL
;
1508 r
= calendar_spec_from_string(*p
, &spec
);
1510 ret
= log_error_errno(r
, "Failed to parse calendar specification '%s': %m", *p
);
1514 r
= calendar_spec_normalize(spec
);
1516 ret
= log_error_errno(r
, "Failed to normalize calendar specification '%s': %m", *p
);
1520 r
= calendar_spec_to_string(spec
, &t
);
1522 ret
= log_error_errno(r
, "Failed to format calendar specification '%s': %m", *p
);
1527 printf(" Original form: %s\n", *p
);
1529 printf("Normalized form: %s\n", t
);
1531 r
= calendar_spec_next_usec(spec
, n
, &next
);
1533 printf(" Next elapse: never\n");
1535 ret
= log_error_errno(r
, "Failed to determine next elapse for '%s': %m", *p
);
1538 char buffer
[CONST_MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_TIMESTAMP_RELATIVE_MAX
)];
1540 printf(" Next elapse: %s\n", format_timestamp(buffer
, sizeof(buffer
), next
));
1542 if (!in_utc_timezone())
1543 printf(" (in UTC): %s\n", format_timestamp_utc(buffer
, sizeof(buffer
), next
));
1545 printf(" From now: %s\n", format_timestamp_relative(buffer
, sizeof(buffer
), next
));
1555 static int service_watchdogs(int argc
, char *argv
[], void *userdata
) {
1556 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1557 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1560 assert(IN_SET(argc
, 1, 2));
1563 r
= acquire_bus(false, &bus
);
1565 return log_error_errno(r
, "Failed to create bus connection: %m");
1567 /* get ServiceWatchdogs */
1569 r
= sd_bus_get_property_trivial(
1571 "org.freedesktop.systemd1",
1572 "/org/freedesktop/systemd1",
1573 "org.freedesktop.systemd1.Manager",
1579 return log_error_errno(r
, "Failed to get service-watchdog state: %s", bus_error_message(&error
, r
));
1581 printf("%s\n", yes_no(!!b
));
1586 /* set ServiceWatchdogs */
1587 b
= parse_boolean(argv
[1]);
1589 log_error("Failed to parse service-watchdogs argument.");
1593 r
= sd_bus_set_property(
1595 "org.freedesktop.systemd1",
1596 "/org/freedesktop/systemd1",
1597 "org.freedesktop.systemd1.Manager",
1603 return log_error_errno(r
, "Failed to set service-watchdog state: %s", bus_error_message(&error
, r
));
1608 static int do_verify(int argc
, char *argv
[], void *userdata
) {
1609 return verify_units(strv_skip(argv
, 1), arg_scope
, arg_man
, arg_generators
);
1612 static int help(int argc
, char *argv
[], void *userdata
) {
1614 (void) pager_open(arg_no_pager
, false);
1616 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1617 "Profile systemd, show unit dependencies, check unit files.\n\n"
1618 " -h --help Show this help\n"
1619 " --version Show package version\n"
1620 " --no-pager Do not pipe output into a pager\n"
1621 " --system Operate on system systemd instance\n"
1622 " --user Operate on user systemd instance\n"
1623 " --global Operate on global user configuration\n"
1624 " -H --host=[USER@]HOST Operate on remote host\n"
1625 " -M --machine=CONTAINER Operate on local container\n"
1626 " --order Show only order in the graph\n"
1627 " --require Show only requirement in the graph\n"
1628 " --from-pattern=GLOB Show only origins in the graph\n"
1629 " --to-pattern=GLOB Show only destinations in the graph\n"
1630 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1631 " earlier than the latest in the branch\n"
1632 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1633 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
1635 " time Print time spent in the kernel\n"
1636 " blame Print list of running units ordered by time to init\n"
1637 " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
1638 " plot Output SVG graphic showing service initialization\n"
1639 " dot [UNIT...] Output dependency graph in man:dot(1) format\n"
1640 " log-level [LEVEL] Get/set logging threshold for manager\n"
1641 " log-target [TARGET] Get/set logging target for manager\n"
1642 " dump Output state serialization of service manager\n"
1643 " unit-paths List load directories for units\n"
1644 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1645 " verify FILE... Check unit files for correctness\n"
1646 " calendar SPEC... Validate repetitive calendar time events\n"
1647 " service-watchdogs [BOOL] Get/set service watchdog state\n"
1648 , program_invocation_short_name
);
1650 /* When updating this list, including descriptions, apply
1651 * changes to shell-completion/bash/systemd-analyze and
1652 * shell-completion/zsh/_systemd-analyze too. */
1657 static int parse_argv(int argc
, char *argv
[]) {
1659 ARG_VERSION
= 0x100,
1665 ARG_DOT_FROM_PATTERN
,
1673 static const struct option options
[] = {
1674 { "help", no_argument
, NULL
, 'h' },
1675 { "version", no_argument
, NULL
, ARG_VERSION
},
1676 { "order", no_argument
, NULL
, ARG_ORDER
},
1677 { "require", no_argument
, NULL
, ARG_REQUIRE
},
1678 { "system", no_argument
, NULL
, ARG_SYSTEM
},
1679 { "user", no_argument
, NULL
, ARG_USER
},
1680 { "global", no_argument
, NULL
, ARG_GLOBAL
},
1681 { "from-pattern", required_argument
, NULL
, ARG_DOT_FROM_PATTERN
},
1682 { "to-pattern", required_argument
, NULL
, ARG_DOT_TO_PATTERN
},
1683 { "fuzz", required_argument
, NULL
, ARG_FUZZ
},
1684 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1685 { "man", optional_argument
, NULL
, ARG_MAN
},
1686 { "generators", optional_argument
, NULL
, ARG_GENERATORS
},
1687 { "host", required_argument
, NULL
, 'H' },
1688 { "machine", required_argument
, NULL
, 'M' },
1697 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
1701 return help(0, NULL
, NULL
);
1707 arg_scope
= UNIT_FILE_SYSTEM
;
1711 arg_scope
= UNIT_FILE_USER
;
1715 arg_scope
= UNIT_FILE_GLOBAL
;
1719 arg_dot
= DEP_ORDER
;
1723 arg_dot
= DEP_REQUIRE
;
1726 case ARG_DOT_FROM_PATTERN
:
1727 if (strv_extend(&arg_dot_from_patterns
, optarg
) < 0)
1732 case ARG_DOT_TO_PATTERN
:
1733 if (strv_extend(&arg_dot_to_patterns
, optarg
) < 0)
1739 r
= parse_sec(optarg
, &arg_fuzz
);
1745 arg_no_pager
= true;
1749 arg_transport
= BUS_TRANSPORT_REMOTE
;
1754 arg_transport
= BUS_TRANSPORT_MACHINE
;
1760 r
= parse_boolean(optarg
);
1762 log_error("Failed to parse --man= argument.");
1772 case ARG_GENERATORS
:
1774 r
= parse_boolean(optarg
);
1776 log_error("Failed to parse --generators= argument.");
1780 arg_generators
= !!r
;
1782 arg_generators
= true;
1790 assert_not_reached("Unhandled option code.");
1793 if (arg_scope
== UNIT_FILE_GLOBAL
&&
1794 !STR_IN_SET(argv
[optind
] ?: "time", "dot", "unit-paths", "verify")) {
1795 log_error("Option --global only makes sense with verbs dot, unit-paths, verify.");
1799 return 1; /* work to do */
1802 int main(int argc
, char *argv
[]) {
1804 static const Verb verbs
[] = {
1805 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
1806 { "time", VERB_ANY
, 1, VERB_DEFAULT
, analyze_time
},
1807 { "blame", VERB_ANY
, 1, 0, analyze_blame
},
1808 { "critical-chain", VERB_ANY
, VERB_ANY
, 0, analyze_critical_chain
},
1809 { "plot", VERB_ANY
, 1, 0, analyze_plot
},
1810 { "dot", VERB_ANY
, VERB_ANY
, 0, dot
},
1811 { "log-level", VERB_ANY
, 2, 0, get_or_set_log_level
},
1812 { "log-target", VERB_ANY
, 2, 0, get_or_set_log_target
},
1813 /* The following four verbs are deprecated aliases */
1814 { "set-log-level", 2, 2, 0, set_log_level
},
1815 { "get-log-level", VERB_ANY
, 1, 0, get_log_level
},
1816 { "set-log-target", 2, 2, 0, set_log_target
},
1817 { "get-log-target", VERB_ANY
, 1, 0, get_log_target
},
1818 { "dump", VERB_ANY
, 1, 0, dump
},
1819 { "unit-paths", 1, 1, 0, dump_unit_paths
},
1820 { "syscall-filter", VERB_ANY
, VERB_ANY
, 0, dump_syscall_filters
},
1821 { "verify", 2, VERB_ANY
, 0, do_verify
},
1822 { "calendar", 2, VERB_ANY
, 0, test_calendar
},
1823 { "service-watchdogs", VERB_ANY
, 2, 0, service_watchdogs
},
1829 setlocale(LC_ALL
, "");
1830 setlocale(LC_NUMERIC
, "C"); /* we want to format/parse floats in C style */
1832 log_parse_environment();
1835 r
= parse_argv(argc
, argv
);
1839 r
= dispatch_verb(argc
, argv
, verbs
, NULL
);
1844 strv_free(arg_dot_from_patterns
);
1845 strv_free(arg_dot_to_patterns
);
1847 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;