]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/analyze/analyze.c
bus-util: add flags for bus_map_all_properties() (#8546)
[thirdparty/systemd.git] / src / analyze / analyze.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010-2013 Lennart Poettering
6 Copyright 2013 Simon Peeters
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <getopt.h>
23 #include <locale.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include "sd-bus.h"
28
29 #include "alloc-util.h"
30 #include "analyze-verify.h"
31 #include "bus-error.h"
32 #include "bus-unit-util.h"
33 #include "bus-util.h"
34 #include "calendarspec.h"
35 #include "glob-util.h"
36 #include "hashmap.h"
37 #include "locale-util.h"
38 #include "log.h"
39 #include "pager.h"
40 #include "parse-util.h"
41 #if HAVE_SECCOMP
42 #include "seccomp-util.h"
43 #endif
44 #include "special.h"
45 #include "strv.h"
46 #include "strxcpyx.h"
47 #include "terminal-util.h"
48 #include "unit-name.h"
49 #include "util.h"
50 #include "verbs.h"
51
52 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
53 #define SCALE_Y (20.0)
54
55 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
56
57 #define svg(...) printf(__VA_ARGS__)
58
59 #define svg_bar(class, x1, x2, y) \
60 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
61 (class), \
62 SCALE_X * (x1), SCALE_Y * (y), \
63 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
64
65 #define svg_text(b, x, y, format, ...) \
66 do { \
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__); \
69 svg("</text>\n"); \
70 } while (false)
71
72 static enum dot {
73 DEP_ALL,
74 DEP_ORDER,
75 DEP_REQUIRE
76 } arg_dot = DEP_ALL;
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;
86
87 struct boot_times {
88 usec_t firmware_time;
89 usec_t loader_time;
90 usec_t kernel_time;
91 usec_t kernel_done_time;
92 usec_t initrd_time;
93 usec_t userspace_time;
94 usec_t finish_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;
101
102 /*
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.
107 *
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).
111 */
112 usec_t reverse_offset;
113 };
114
115 struct unit_times {
116 char *name;
117 usec_t activating;
118 usec_t activated;
119 usec_t deactivated;
120 usec_t deactivating;
121 usec_t time;
122 };
123
124 struct host_info {
125 char *hostname;
126 char *kernel_name;
127 char *kernel_release;
128 char *kernel_version;
129 char *os_pretty_name;
130 char *virtualization;
131 char *architecture;
132 };
133
134 static int acquire_bus(bool need_full_bus, sd_bus **bus) {
135 bool user = arg_scope != UNIT_FILE_SYSTEM;
136
137 if (need_full_bus)
138 return bus_connect_transport(arg_transport, arg_host, user, bus);
139 else
140 return bus_connect_transport_systemd(arg_transport, arg_host, user, bus);
141 }
142
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;
145 int r;
146
147 assert(bus);
148 assert(path);
149 assert(interface);
150 assert(property);
151 assert(val);
152
153 r = sd_bus_get_property_trivial(
154 bus,
155 "org.freedesktop.systemd1",
156 path,
157 interface,
158 property,
159 &error,
160 't', val);
161
162 if (r < 0) {
163 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
164 return r;
165 }
166
167 return 0;
168 }
169
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;
172 int r;
173
174 assert(bus);
175 assert(path);
176 assert(property);
177 assert(strv);
178
179 r = sd_bus_get_property_strv(
180 bus,
181 "org.freedesktop.systemd1",
182 path,
183 "org.freedesktop.systemd1.Unit",
184 property,
185 &error,
186 strv);
187 if (r < 0) {
188 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
189 return r;
190 }
191
192 return 0;
193 }
194
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);
198 }
199
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);
203 }
204
205 static void free_unit_times(struct unit_times *t, unsigned n) {
206 struct unit_times *p;
207
208 for (p = t; p < t + n; p++)
209 free(p->name);
210
211 free(t);
212 }
213
214 static void subtract_timestamp(usec_t *a, usec_t b) {
215 assert(a);
216
217 if (*a > 0) {
218 assert(*a >= b);
219 *a -= b;
220 }
221 }
222
223 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
224 static struct boot_times times;
225 static bool cached = false;
226
227 if (cached)
228 goto finish;
229
230 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
231
232 if (bus_get_uint64_property(bus,
233 "/org/freedesktop/systemd1",
234 "org.freedesktop.systemd1.Manager",
235 "FirmwareTimestampMonotonic",
236 &times.firmware_time) < 0 ||
237 bus_get_uint64_property(bus,
238 "/org/freedesktop/systemd1",
239 "org.freedesktop.systemd1.Manager",
240 "LoaderTimestampMonotonic",
241 &times.loader_time) < 0 ||
242 bus_get_uint64_property(bus,
243 "/org/freedesktop/systemd1",
244 "org.freedesktop.systemd1.Manager",
245 "KernelTimestamp",
246 &times.kernel_time) < 0 ||
247 bus_get_uint64_property(bus,
248 "/org/freedesktop/systemd1",
249 "org.freedesktop.systemd1.Manager",
250 "InitRDTimestampMonotonic",
251 &times.initrd_time) < 0 ||
252 bus_get_uint64_property(bus,
253 "/org/freedesktop/systemd1",
254 "org.freedesktop.systemd1.Manager",
255 "UserspaceTimestampMonotonic",
256 &times.userspace_time) < 0 ||
257 bus_get_uint64_property(bus,
258 "/org/freedesktop/systemd1",
259 "org.freedesktop.systemd1.Manager",
260 "FinishTimestampMonotonic",
261 &times.finish_time) < 0 ||
262 bus_get_uint64_property(bus,
263 "/org/freedesktop/systemd1",
264 "org.freedesktop.systemd1.Manager",
265 "SecurityStartTimestampMonotonic",
266 &times.security_start_time) < 0 ||
267 bus_get_uint64_property(bus,
268 "/org/freedesktop/systemd1",
269 "org.freedesktop.systemd1.Manager",
270 "SecurityFinishTimestampMonotonic",
271 &times.security_finish_time) < 0 ||
272 bus_get_uint64_property(bus,
273 "/org/freedesktop/systemd1",
274 "org.freedesktop.systemd1.Manager",
275 "GeneratorsStartTimestampMonotonic",
276 &times.generators_start_time) < 0 ||
277 bus_get_uint64_property(bus,
278 "/org/freedesktop/systemd1",
279 "org.freedesktop.systemd1.Manager",
280 "GeneratorsFinishTimestampMonotonic",
281 &times.generators_finish_time) < 0 ||
282 bus_get_uint64_property(bus,
283 "/org/freedesktop/systemd1",
284 "org.freedesktop.systemd1.Manager",
285 "UnitsLoadStartTimestampMonotonic",
286 &times.unitsload_start_time) < 0 ||
287 bus_get_uint64_property(bus,
288 "/org/freedesktop/systemd1",
289 "org.freedesktop.systemd1.Manager",
290 "UnitsLoadFinishTimestampMonotonic",
291 &times.unitsload_finish_time) < 0)
292 return -EIO;
293
294 if (times.finish_time <= 0) {
295 log_error("Bootup is not yet finished. Please try again later.");
296 return -EINPROGRESS;
297 }
298
299 if (arg_scope == UNIT_FILE_SYSTEM) {
300 if (times.initrd_time > 0)
301 times.kernel_done_time = times.initrd_time;
302 else
303 times.kernel_done_time = times.userspace_time;
304 } else {
305 /*
306 * User-instance-specific timestamps processing
307 * (see comment to reverse_offset in struct boot_times).
308 */
309 times.reverse_offset = times.userspace_time;
310
311 times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
312 subtract_timestamp(&times.finish_time, times.reverse_offset);
313
314 subtract_timestamp(&times.security_start_time, times.reverse_offset);
315 subtract_timestamp(&times.security_finish_time, times.reverse_offset);
316
317 subtract_timestamp(&times.generators_start_time, times.reverse_offset);
318 subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
319
320 subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
321 subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
322 }
323
324 cached = true;
325
326 finish:
327 *bt = &times;
328 return 0;
329 }
330
331 static void free_host_info(struct host_info *hi) {
332
333 if (!hi)
334 return;
335
336 free(hi->hostname);
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);
343 free(hi);
344 }
345
346 DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info*, free_host_info);
347
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;
351 int r, c = 0;
352 struct boot_times *boot_times = NULL;
353 struct unit_times *unit_times = NULL;
354 size_t size = 0;
355 UnitInfo u;
356
357 r = acquire_boot_times(bus, &boot_times);
358 if (r < 0)
359 goto fail;
360
361 r = sd_bus_call_method(
362 bus,
363 "org.freedesktop.systemd1",
364 "/org/freedesktop/systemd1",
365 "org.freedesktop.systemd1.Manager",
366 "ListUnits",
367 &error, &reply,
368 NULL);
369 if (r < 0) {
370 log_error("Failed to list units: %s", bus_error_message(&error, -r));
371 goto fail;
372 }
373
374 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
375 if (r < 0) {
376 bus_log_parse_error(r);
377 goto fail;
378 }
379
380 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
381 struct unit_times *t;
382
383 if (!GREEDY_REALLOC(unit_times, size, c+1)) {
384 r = log_oom();
385 goto fail;
386 }
387
388 t = unit_times+c;
389 t->name = NULL;
390
391 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
392
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) {
409 r = -EIO;
410 goto fail;
411 }
412
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);
417
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;
422 else
423 t->time = 0;
424
425 if (t->activating == 0)
426 continue;
427
428 t->name = strdup(u.id);
429 if (!t->name) {
430 r = log_oom();
431 goto fail;
432 }
433 c++;
434 }
435 if (r < 0) {
436 bus_log_parse_error(r);
437 goto fail;
438 }
439
440 *out = unit_times;
441 return c;
442
443 fail:
444 free_unit_times(unit_times, (unsigned) c);
445 return r;
446 }
447
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) },
455 {}
456 };
457
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) },
461 {}
462 };
463
464 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
465 _cleanup_(free_host_infop) struct host_info *host;
466 int r;
467
468 host = new0(struct host_info, 1);
469 if (!host)
470 return log_oom();
471
472 r = bus_map_all_properties(bus,
473 "org.freedesktop.hostname1",
474 "/org/freedesktop/hostname1",
475 hostname_map,
476 BUS_MAP_STRDUP,
477 &error,
478 NULL,
479 host);
480 if (r < 0)
481 log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
482
483 r = bus_map_all_properties(bus,
484 "org.freedesktop.systemd1",
485 "/org/freedesktop/systemd1",
486 manager_map,
487 BUS_MAP_STRDUP,
488 &error,
489 NULL,
490 host);
491 if (r < 0)
492 return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r));
493
494 *hi = host;
495 host = NULL;
496
497 return 0;
498 }
499
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];
504 size_t size;
505 char *ptr;
506 int r;
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;
510
511 r = acquire_boot_times(bus, &t);
512 if (r < 0)
513 return r;
514
515 path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
516 if (!path)
517 return log_oom();
518
519 r = sd_bus_get_property_string(
520 bus,
521 "org.freedesktop.systemd1",
522 path,
523 "org.freedesktop.systemd1.Unit",
524 "Id",
525 &error,
526 &unit_id);
527 if (r < 0) {
528 log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
529 unit_id = NULL;
530 }
531
532 r = bus_get_uint64_property(bus, path,
533 "org.freedesktop.systemd1.Unit",
534 "ActiveEnterTimestampMonotonic",
535 &activated_time);
536 if (r < 0) {
537 log_info_errno(r, "Could not get time to reach default.target. Continuing...");
538 activated_time = USEC_INFINITY;
539 }
540
541 ptr = buf;
542 size = sizeof(buf);
543
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));
553
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));
557
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);
564 else if (!unit_id)
565 size = strpcpyf(&ptr, size, "\ncould not find default.target");
566
567
568 ptr = strdup(buf);
569 if (!ptr)
570 return log_oom();
571
572 *_buf = ptr;
573 return 0;
574 }
575
576 static void svg_graph_box(double height, double begin, double end) {
577 long long i;
578
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);
582
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);
593 else
594 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
595 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
596 }
597 }
598
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;
605 double width;
606 _cleanup_free_ char *pretty_times = NULL;
607 struct unit_times *u;
608
609 r = acquire_bus(true, &bus);
610 if (r < 0)
611 return log_error_errno(r, "Failed to create bus connection: %m");
612
613 n = acquire_boot_times(bus, &boot);
614 if (n < 0)
615 return n;
616
617 n = pretty_boot_time(bus, &pretty_times);
618 if (n < 0)
619 return n;
620
621 n = acquire_host_info(bus, &host);
622 if (n < 0)
623 return n;
624
625 n = acquire_time_data(bus, &times);
626 if (n <= 0)
627 return n;
628
629 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
630
631 width = SCALE_X * (boot->firmware_time + boot->finish_time);
632 if (width < 800.0)
633 width = 800.0;
634
635 if (boot->firmware_time > boot->loader_time)
636 m++;
637 if (boot->loader_time > 0) {
638 m++;
639 if (width < 1000.0)
640 width = 1000.0;
641 }
642 if (boot->initrd_time > 0)
643 m++;
644 if (boot->kernel_time > 0)
645 m++;
646
647 for (u = times; u < times + n; u++) {
648 double text_start, text_width;
649
650 if (u->activating < boot->userspace_time ||
651 u->activating > boot->finish_time) {
652 u->name = mfree(u->name);
653 continue;
654 }
655
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;
663
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;
673 m++;
674 }
675
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");
679
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 */);
684
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);
692
693 /* style sheet */
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"
710 "// line.sec1 { }\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");
718
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));
729
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);
732
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");
736 y++;
737 }
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");
741 y++;
742 }
743 if (boot->kernel_time > 0) {
744 svg_bar("kernel", 0, boot->kernel_done_time, y);
745 svg_text(true, 0, y, "kernel");
746 y++;
747 }
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");
751 y++;
752 }
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");
758 y++;
759
760 for (u = times; u < times + n; u++) {
761 char ts[FORMAT_TIMESPAN_MAX];
762 bool b;
763
764 if (!u->name)
765 continue;
766
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);
770
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;
773 if (u->time)
774 svg_text(b, u->activating, y, "%s (%s)",
775 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
776 else
777 svg_text(b, u->activating, y, "%s", u->name);
778 y++;
779 }
780
781 svg("</g>\n");
782
783 /* Legend */
784 svg("<g transform=\"translate(20,100)\">\n");
785 y++;
786 svg_bar("activating", 0, 300000, y);
787 svg_text(true, 400000, y, "Activating");
788 y++;
789 svg_bar("active", 0, 300000, y);
790 svg_text(true, 400000, y, "Active");
791 y++;
792 svg_bar("deactivating", 0, 300000, y);
793 svg_text(true, 400000, y, "Deactivating");
794 y++;
795 svg_bar("security", 0, 300000, y);
796 svg_text(true, 400000, y, "Setting up security module");
797 y++;
798 svg_bar("generators", 0, 300000, y);
799 svg_text(true, 400000, y, "Generators");
800 y++;
801 svg_bar("unitsload", 0, 300000, y);
802 svg_text(true, 400000, y, "Loading unit files");
803 y++;
804
805 svg("</g>\n\n");
806
807 svg("</svg>\n");
808
809 free_unit_times(times, (unsigned) n);
810
811 n = 0;
812 return n;
813 }
814
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) {
817 unsigned int i;
818 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
819
820 for (i = level; i != 0; i--)
821 printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE));
822
823 printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH));
824
825 if (times) {
826 if (times->time > 0)
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));
832 else
833 printf("%s", name);
834 } else
835 printf("%s", name);
836 printf("\n");
837
838 return 0;
839 }
840
841 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
842 _cleanup_free_ char *path = NULL;
843
844 assert(bus);
845 assert(name);
846 assert(deps);
847
848 path = unit_dbus_path_from_name(name);
849 if (!path)
850 return -ENOMEM;
851
852 return bus_get_unit_property_strv(bus, path, "After", deps);
853 }
854
855 static Hashmap *unit_times_hashmap;
856
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;
861
862 times = hashmap_get(unit_times_hashmap, *a);
863 if (times)
864 usa = times->activated;
865 times = hashmap_get(unit_times_hashmap, *b);
866 if (times)
867 usb = times->activated;
868
869 return usb - usa;
870 }
871
872 static bool times_in_range(const struct unit_times *times, const struct boot_times *boot) {
873 return times &&
874 times->activated > 0 && times->activated <= boot->finish_time;
875 }
876
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;
880 char **c;
881 int r = 0;
882 usec_t service_longest = 0;
883 int to_print = 0;
884 struct unit_times *times;
885 struct boot_times *boot;
886
887 if (strv_extend(units, name))
888 return log_oom();
889
890 r = list_dependencies_get_dependencies(bus, name, &deps);
891 if (r < 0)
892 return r;
893
894 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
895
896 r = acquire_boot_times(bus, &boot);
897 if (r < 0)
898 return r;
899
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;
906 break;
907 }
908 }
909
910 if (service_longest == 0)
911 return r;
912
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)
917 to_print++;
918 }
919
920 if (!to_print)
921 return r;
922
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)
927 continue;
928
929 to_print--;
930
931 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
932 if (r < 0)
933 return r;
934
935 if (strv_contains(*units, *c)) {
936 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
937 true, NULL, boot);
938 if (r < 0)
939 return r;
940 continue;
941 }
942
943 r = list_dependencies_one(bus, *c, level + 1, units,
944 (branches << 1) | (to_print ? 1 : 0));
945 if (r < 0)
946 return r;
947
948 if (to_print == 0)
949 break;
950 }
951 return 0;
952 }
953
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;
958 int r;
959 const char *id;
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;
964
965 assert(bus);
966
967 path = unit_dbus_path_from_name(name);
968 if (!path)
969 return -ENOMEM;
970
971 r = sd_bus_get_property(
972 bus,
973 "org.freedesktop.systemd1",
974 path,
975 "org.freedesktop.systemd1.Unit",
976 "Id",
977 &error,
978 &reply,
979 "s");
980 if (r < 0) {
981 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
982 return r;
983 }
984
985 r = sd_bus_message_read(reply, "s", &id);
986 if (r < 0)
987 return bus_log_parse_error(r);
988
989 times = hashmap_get(unit_times_hashmap, id);
990
991 r = acquire_boot_times(bus, &boot);
992 if (r < 0)
993 return r;
994
995 if (times) {
996 if (times->time)
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));
1001 else
1002 printf("%s\n", id);
1003 }
1004
1005 return list_dependencies_one(bus, name, 0, &units, 0);
1006 }
1007
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;
1011 unsigned int i;
1012 Hashmap *h;
1013 int n, r;
1014
1015 r = acquire_bus(false, &bus);
1016 if (r < 0)
1017 return log_error_errno(r, "Failed to create bus connection: %m");
1018
1019 n = acquire_time_data(bus, &times);
1020 if (n <= 0)
1021 return n;
1022
1023 h = hashmap_new(&string_hash_ops);
1024 if (!h)
1025 return log_oom();
1026
1027 for (i = 0; i < (unsigned) n; i++) {
1028 r = hashmap_put(h, times[i].name, &times[i]);
1029 if (r < 0)
1030 return log_error_errno(r, "Failed to add entry to hashmap: %m");
1031 }
1032 unit_times_hashmap = h;
1033
1034 (void) pager_open(arg_no_pager, false);
1035
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");
1038
1039 if (argc > 1) {
1040 char **name;
1041 STRV_FOREACH(name, strv_skip(argv, 1))
1042 list_dependencies(bus, *name);
1043 } else
1044 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
1045
1046 h = hashmap_free(h);
1047 free_unit_times(times, (unsigned) n);
1048 return 0;
1049 }
1050
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;
1054 unsigned i;
1055 int n, r;
1056
1057 r = acquire_bus(false, &bus);
1058 if (r < 0)
1059 return log_error_errno(r, "Failed to create bus connection: %m");
1060
1061 n = acquire_time_data(bus, &times);
1062 if (n <= 0)
1063 return n;
1064
1065 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
1066
1067 (void) pager_open(arg_no_pager, false);
1068
1069 for (i = 0; i < (unsigned) n; i++) {
1070 char ts[FORMAT_TIMESPAN_MAX];
1071
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);
1074 }
1075
1076 free_unit_times(times, (unsigned) n);
1077 return 0;
1078 }
1079
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;
1083 int r;
1084
1085 r = acquire_bus(false, &bus);
1086 if (r < 0)
1087 return log_error_errno(r, "Failed to create bus connection: %m");
1088
1089 r = pretty_boot_time(bus, &buf);
1090 if (r < 0)
1091 return r;
1092
1093 puts(buf);
1094 return 0;
1095 }
1096
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;
1099 char **unit;
1100 int r;
1101 bool match_patterns;
1102
1103 assert(u);
1104 assert(prop);
1105 assert(color);
1106
1107 match_patterns = strv_fnmatch(patterns, u->id, 0);
1108
1109 if (!strv_isempty(from_patterns) &&
1110 !match_patterns &&
1111 !strv_fnmatch(from_patterns, u->id, 0))
1112 return 0;
1113
1114 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
1115 if (r < 0)
1116 return r;
1117
1118 STRV_FOREACH(unit, units) {
1119 bool match_patterns2;
1120
1121 match_patterns2 = strv_fnmatch(patterns, *unit, 0);
1122
1123 if (!strv_isempty(to_patterns) &&
1124 !match_patterns2 &&
1125 !strv_fnmatch(to_patterns, *unit, 0))
1126 continue;
1127
1128 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
1129 continue;
1130
1131 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1132 }
1133
1134 return 0;
1135 }
1136
1137 static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
1138 int r;
1139
1140 assert(bus);
1141 assert(u);
1142
1143 if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
1144 r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
1145 if (r < 0)
1146 return r;
1147 }
1148
1149 if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
1150 r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
1151 if (r < 0)
1152 return r;
1153 r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
1154 if (r < 0)
1155 return r;
1156 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
1157 if (r < 0)
1158 return r;
1159 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
1160 if (r < 0)
1161 return r;
1162 }
1163
1164 return 0;
1165 }
1166
1167 static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1168 _cleanup_strv_free_ char **expanded_patterns = NULL;
1169 char **pattern;
1170 int r;
1171
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;
1175
1176 if (strv_extend(&expanded_patterns, *pattern) < 0)
1177 return log_oom();
1178
1179 if (string_is_glob(*pattern))
1180 continue;
1181
1182 unit = unit_dbus_path_from_name(*pattern);
1183 if (!unit)
1184 return log_oom();
1185
1186 r = sd_bus_get_property_string(
1187 bus,
1188 "org.freedesktop.systemd1",
1189 unit,
1190 "org.freedesktop.systemd1.Unit",
1191 "Id",
1192 &error,
1193 &unit_id);
1194 if (r < 0)
1195 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1196
1197 if (!streq(*pattern, unit_id)) {
1198 if (strv_extend(&expanded_patterns, unit_id) < 0)
1199 return log_oom();
1200 }
1201 }
1202
1203 *ret = expanded_patterns;
1204 expanded_patterns = NULL; /* do not free */
1205
1206 return 0;
1207 }
1208
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;
1216 int r;
1217 UnitInfo u;
1218
1219 r = acquire_bus(false, &bus);
1220 if (r < 0)
1221 return log_error_errno(r, "Failed to create bus connection: %m");
1222
1223 r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
1224 if (r < 0)
1225 return r;
1226
1227 r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1228 if (r < 0)
1229 return r;
1230
1231 r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1232 if (r < 0)
1233 return r;
1234
1235 r = sd_bus_call_method(
1236 bus,
1237 "org.freedesktop.systemd1",
1238 "/org/freedesktop/systemd1",
1239 "org.freedesktop.systemd1.Manager",
1240 "ListUnits",
1241 &error,
1242 &reply,
1243 "");
1244 if (r < 0) {
1245 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1246 return r;
1247 }
1248
1249 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1250 if (r < 0)
1251 return bus_log_parse_error(r);
1252
1253 printf("digraph systemd {\n");
1254
1255 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1256
1257 r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1258 if (r < 0)
1259 return r;
1260 }
1261 if (r < 0)
1262 return bus_log_parse_error(r);
1263
1264 printf("}\n");
1265
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");
1271
1272 if (on_tty())
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");
1275
1276 return 0;
1277 }
1278
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;
1284 int r;
1285
1286 r = acquire_bus(false, &bus);
1287 if (r < 0)
1288 return log_error_errno(r, "Failed to create bus connection: %m");
1289
1290 (void) pager_open(arg_no_pager, false);
1291
1292 r = sd_bus_call_method(
1293 bus,
1294 "org.freedesktop.systemd1",
1295 "/org/freedesktop/systemd1",
1296 "org.freedesktop.systemd1.Manager",
1297 "Dump",
1298 &error,
1299 &reply,
1300 NULL);
1301 if (r < 0)
1302 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1303
1304 r = sd_bus_message_read(reply, "s", &text);
1305 if (r < 0)
1306 return bus_log_parse_error(r);
1307
1308 fputs(text, stdout);
1309 return 0;
1310 }
1311
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;
1315 int r;
1316
1317 assert(argc == 2);
1318 assert(argv);
1319
1320 r = acquire_bus(false, &bus);
1321 if (r < 0)
1322 return log_error_errno(r, "Failed to create bus connection: %m");
1323
1324 r = sd_bus_set_property(
1325 bus,
1326 "org.freedesktop.systemd1",
1327 "/org/freedesktop/systemd1",
1328 "org.freedesktop.systemd1.Manager",
1329 "LogLevel",
1330 &error,
1331 "s",
1332 argv[1]);
1333 if (r < 0)
1334 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1335
1336 return 0;
1337 }
1338
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;
1343 int r;
1344
1345 r = acquire_bus(false, &bus);
1346 if (r < 0)
1347 return log_error_errno(r, "Failed to create bus connection: %m");
1348
1349 r = sd_bus_get_property_string(
1350 bus,
1351 "org.freedesktop.systemd1",
1352 "/org/freedesktop/systemd1",
1353 "org.freedesktop.systemd1.Manager",
1354 "LogLevel",
1355 &error,
1356 &level);
1357 if (r < 0)
1358 return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
1359
1360 puts(level);
1361 return 0;
1362 }
1363
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);
1366 }
1367
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;
1371 int r;
1372
1373 assert(argc == 2);
1374 assert(argv);
1375
1376 r = acquire_bus(false, &bus);
1377 if (r < 0)
1378 return log_error_errno(r, "Failed to create bus connection: %m");
1379
1380 r = sd_bus_set_property(
1381 bus,
1382 "org.freedesktop.systemd1",
1383 "/org/freedesktop/systemd1",
1384 "org.freedesktop.systemd1.Manager",
1385 "LogTarget",
1386 &error,
1387 "s",
1388 argv[1]);
1389 if (r < 0)
1390 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1391
1392 return 0;
1393 }
1394
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;
1399 int r;
1400
1401 r = acquire_bus(false, &bus);
1402 if (r < 0)
1403 return log_error_errno(r, "Failed to create bus connection: %m");
1404
1405 r = sd_bus_get_property_string(
1406 bus,
1407 "org.freedesktop.systemd1",
1408 "/org/freedesktop/systemd1",
1409 "org.freedesktop.systemd1.Manager",
1410 "LogTarget",
1411 &error,
1412 &target);
1413 if (r < 0)
1414 return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
1415
1416 puts(target);
1417 return 0;
1418 }
1419
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);
1422 }
1423
1424 static int dump_unit_paths(int argc, char *argv[], void *userdata) {
1425 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1426 int r;
1427 char **p;
1428
1429 r = lookup_paths_init(&paths, arg_scope, 0, NULL);
1430 if (r < 0)
1431 return log_error_errno(r, "lookup_paths_init() failed: %m");
1432
1433 STRV_FOREACH(p, paths.search_path)
1434 puts(*p);
1435
1436 return 0;
1437 }
1438
1439 #if HAVE_SECCOMP
1440 static void dump_syscall_filter(const SyscallFilterSet *set) {
1441 const char *syscall;
1442
1443 printf("%s\n", set->name);
1444 printf(" # %s\n", set->help);
1445 NULSTR_FOREACH(syscall, set->value)
1446 printf(" %s\n", syscall);
1447 }
1448
1449 static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
1450 bool first = true;
1451
1452 (void) pager_open(arg_no_pager, false);
1453
1454 if (strv_isempty(strv_skip(argv, 1))) {
1455 int i;
1456
1457 for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
1458 if (!first)
1459 puts("");
1460 dump_syscall_filter(syscall_filter_sets + i);
1461 first = false;
1462 }
1463 } else {
1464 char **name;
1465
1466 STRV_FOREACH(name, strv_skip(argv, 1)) {
1467 const SyscallFilterSet *set;
1468
1469 if (!first)
1470 puts("");
1471
1472 set = syscall_filter_set_find(*name);
1473 if (!set) {
1474 /* make sure the error appears below normal output */
1475 fflush(stdout);
1476
1477 log_error("Filter set \"%s\" not found.", *name);
1478 return -ENOENT;
1479 }
1480
1481 dump_syscall_filter(set);
1482 first = false;
1483 }
1484 }
1485
1486 return 0;
1487 }
1488
1489 #else
1490 static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
1491 log_error("Not compiled with syscall filters, sorry.");
1492 return -EOPNOTSUPP;
1493 }
1494 #endif
1495
1496 static int test_calendar(int argc, char *argv[], void *userdata) {
1497 int ret = 0, r;
1498 char **p;
1499 usec_t n;
1500
1501 n = now(CLOCK_REALTIME);
1502
1503 STRV_FOREACH(p, strv_skip(argv, 1)) {
1504 _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
1505 _cleanup_free_ char *t = NULL;
1506 usec_t next;
1507
1508 r = calendar_spec_from_string(*p, &spec);
1509 if (r < 0) {
1510 ret = log_error_errno(r, "Failed to parse calendar specification '%s': %m", *p);
1511 continue;
1512 }
1513
1514 r = calendar_spec_normalize(spec);
1515 if (r < 0) {
1516 ret = log_error_errno(r, "Failed to normalize calendar specification '%s': %m", *p);
1517 continue;
1518 }
1519
1520 r = calendar_spec_to_string(spec, &t);
1521 if (r < 0) {
1522 ret = log_error_errno(r, "Failed to format calendar specification '%s': %m", *p);
1523 continue;
1524 }
1525
1526 if (!streq(t, *p))
1527 printf(" Original form: %s\n", *p);
1528
1529 printf("Normalized form: %s\n", t);
1530
1531 r = calendar_spec_next_usec(spec, n, &next);
1532 if (r == -ENOENT)
1533 printf(" Next elapse: never\n");
1534 else if (r < 0) {
1535 ret = log_error_errno(r, "Failed to determine next elapse for '%s': %m", *p);
1536 continue;
1537 } else {
1538 char buffer[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESTAMP_RELATIVE_MAX)];
1539
1540 printf(" Next elapse: %s\n", format_timestamp(buffer, sizeof(buffer), next));
1541
1542 if (!in_utc_timezone())
1543 printf(" (in UTC): %s\n", format_timestamp_utc(buffer, sizeof(buffer), next));
1544
1545 printf(" From now: %s\n", format_timestamp_relative(buffer, sizeof(buffer), next));
1546 }
1547
1548 if (*(p+1))
1549 putchar('\n');
1550 }
1551
1552 return ret;
1553 }
1554
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;
1558 int b, r;
1559
1560 assert(IN_SET(argc, 1, 2));
1561 assert(argv);
1562
1563 r = acquire_bus(false, &bus);
1564 if (r < 0)
1565 return log_error_errno(r, "Failed to create bus connection: %m");
1566
1567 /* get ServiceWatchdogs */
1568 if (argc == 1) {
1569 r = sd_bus_get_property_trivial(
1570 bus,
1571 "org.freedesktop.systemd1",
1572 "/org/freedesktop/systemd1",
1573 "org.freedesktop.systemd1.Manager",
1574 "ServiceWatchdogs",
1575 &error,
1576 'b',
1577 &b);
1578 if (r < 0)
1579 return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
1580
1581 printf("%s\n", yes_no(!!b));
1582
1583 return 0;
1584 }
1585
1586 /* set ServiceWatchdogs */
1587 b = parse_boolean(argv[1]);
1588 if (b < 0) {
1589 log_error("Failed to parse service-watchdogs argument.");
1590 return -EINVAL;
1591 }
1592
1593 r = sd_bus_set_property(
1594 bus,
1595 "org.freedesktop.systemd1",
1596 "/org/freedesktop/systemd1",
1597 "org.freedesktop.systemd1.Manager",
1598 "ServiceWatchdogs",
1599 &error,
1600 "b",
1601 b);
1602 if (r < 0)
1603 return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
1604
1605 return 0;
1606 }
1607
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);
1610 }
1611
1612 static int help(int argc, char *argv[], void *userdata) {
1613
1614 (void) pager_open(arg_no_pager, false);
1615
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"
1634 "Commands:\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);
1649
1650 /* When updating this list, including descriptions, apply
1651 * changes to shell-completion/bash/systemd-analyze and
1652 * shell-completion/zsh/_systemd-analyze too. */
1653
1654 return 0;
1655 }
1656
1657 static int parse_argv(int argc, char *argv[]) {
1658 enum {
1659 ARG_VERSION = 0x100,
1660 ARG_ORDER,
1661 ARG_REQUIRE,
1662 ARG_SYSTEM,
1663 ARG_USER,
1664 ARG_GLOBAL,
1665 ARG_DOT_FROM_PATTERN,
1666 ARG_DOT_TO_PATTERN,
1667 ARG_FUZZ,
1668 ARG_NO_PAGER,
1669 ARG_MAN,
1670 ARG_GENERATORS,
1671 };
1672
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' },
1689 {}
1690 };
1691
1692 int r, c;
1693
1694 assert(argc >= 0);
1695 assert(argv);
1696
1697 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1698 switch (c) {
1699
1700 case 'h':
1701 return help(0, NULL, NULL);
1702
1703 case ARG_VERSION:
1704 return version();
1705
1706 case ARG_SYSTEM:
1707 arg_scope = UNIT_FILE_SYSTEM;
1708 break;
1709
1710 case ARG_USER:
1711 arg_scope = UNIT_FILE_USER;
1712 break;
1713
1714 case ARG_GLOBAL:
1715 arg_scope = UNIT_FILE_GLOBAL;
1716 break;
1717
1718 case ARG_ORDER:
1719 arg_dot = DEP_ORDER;
1720 break;
1721
1722 case ARG_REQUIRE:
1723 arg_dot = DEP_REQUIRE;
1724 break;
1725
1726 case ARG_DOT_FROM_PATTERN:
1727 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1728 return log_oom();
1729
1730 break;
1731
1732 case ARG_DOT_TO_PATTERN:
1733 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1734 return log_oom();
1735
1736 break;
1737
1738 case ARG_FUZZ:
1739 r = parse_sec(optarg, &arg_fuzz);
1740 if (r < 0)
1741 return r;
1742 break;
1743
1744 case ARG_NO_PAGER:
1745 arg_no_pager = true;
1746 break;
1747
1748 case 'H':
1749 arg_transport = BUS_TRANSPORT_REMOTE;
1750 arg_host = optarg;
1751 break;
1752
1753 case 'M':
1754 arg_transport = BUS_TRANSPORT_MACHINE;
1755 arg_host = optarg;
1756 break;
1757
1758 case ARG_MAN:
1759 if (optarg) {
1760 r = parse_boolean(optarg);
1761 if (r < 0) {
1762 log_error("Failed to parse --man= argument.");
1763 return -EINVAL;
1764 }
1765
1766 arg_man = !!r;
1767 } else
1768 arg_man = true;
1769
1770 break;
1771
1772 case ARG_GENERATORS:
1773 if (optarg) {
1774 r = parse_boolean(optarg);
1775 if (r < 0) {
1776 log_error("Failed to parse --generators= argument.");
1777 return -EINVAL;
1778 }
1779
1780 arg_generators = !!r;
1781 } else
1782 arg_generators = true;
1783
1784 break;
1785
1786 case '?':
1787 return -EINVAL;
1788
1789 default:
1790 assert_not_reached("Unhandled option code.");
1791 }
1792
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.");
1796 return -EINVAL;
1797 }
1798
1799 return 1; /* work to do */
1800 }
1801
1802 int main(int argc, char *argv[]) {
1803
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 },
1824 {}
1825 };
1826
1827 int r;
1828
1829 setlocale(LC_ALL, "");
1830 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1831
1832 log_parse_environment();
1833 log_open();
1834
1835 r = parse_argv(argc, argv);
1836 if (r <= 0)
1837 goto finish;
1838
1839 r = dispatch_verb(argc, argv, verbs, NULL);
1840
1841 finish:
1842 pager_close();
1843
1844 strv_free(arg_dot_from_patterns);
1845 strv_free(arg_dot_to_patterns);
1846
1847 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1848 }