]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/analyze/analyze.c
Merge pull request #8303 from yuwata/fix-8276
[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 &error,
477 host);
478 if (r < 0)
479 log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
480
481 r = bus_map_all_properties(bus,
482 "org.freedesktop.systemd1",
483 "/org/freedesktop/systemd1",
484 manager_map,
485 &error,
486 host);
487 if (r < 0)
488 return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r));
489
490 *hi = host;
491 host = NULL;
492
493 return 0;
494 }
495
496 static int pretty_boot_time(sd_bus *bus, char **_buf) {
497 char ts[FORMAT_TIMESPAN_MAX];
498 struct boot_times *t;
499 static char buf[4096];
500 size_t size;
501 char *ptr;
502 int r;
503 usec_t activated_time = USEC_INFINITY;
504 _cleanup_free_ char* path = NULL, *unit_id = NULL;
505 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
506
507 r = acquire_boot_times(bus, &t);
508 if (r < 0)
509 return r;
510
511 path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
512 if (!path)
513 return log_oom();
514
515 r = sd_bus_get_property_string(
516 bus,
517 "org.freedesktop.systemd1",
518 path,
519 "org.freedesktop.systemd1.Unit",
520 "Id",
521 &error,
522 &unit_id);
523 if (r < 0) {
524 log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
525 unit_id = NULL;
526 }
527
528 r = bus_get_uint64_property(bus, path,
529 "org.freedesktop.systemd1.Unit",
530 "ActiveEnterTimestampMonotonic",
531 &activated_time);
532 if (r < 0) {
533 log_info_errno(r, "Could not get time to reach default.target. Continuing...");
534 activated_time = USEC_INFINITY;
535 }
536
537 ptr = buf;
538 size = sizeof(buf);
539
540 size = strpcpyf(&ptr, size, "Startup finished in ");
541 if (t->firmware_time > 0)
542 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
543 if (t->loader_time > 0)
544 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
545 if (t->kernel_time > 0)
546 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
547 if (t->initrd_time > 0)
548 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
549
550 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
551 if (t->kernel_time > 0)
552 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
553
554 if (unit_id && activated_time > 0 && activated_time != USEC_INFINITY)
555 size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id, format_timespan(ts, sizeof(ts), activated_time - t->userspace_time, USEC_PER_MSEC));
556 else if (unit_id && activated_time == 0)
557 size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id);
558 else if (unit_id && activated_time == USEC_INFINITY)
559 size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.",unit_id);
560 else if (!unit_id)
561 size = strpcpyf(&ptr, size, "\ncould not find default.target");
562
563
564 ptr = strdup(buf);
565 if (!ptr)
566 return log_oom();
567
568 *_buf = ptr;
569 return 0;
570 }
571
572 static void svg_graph_box(double height, double begin, double end) {
573 long long i;
574
575 /* outside box, fill */
576 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
577 SCALE_X * (end - begin), SCALE_Y * height);
578
579 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
580 /* lines for each second */
581 if (i % 5000000 == 0)
582 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
583 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
584 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
585 else if (i % 1000000 == 0)
586 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
587 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
588 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
589 else
590 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
591 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
592 }
593 }
594
595 static int analyze_plot(int argc, char *argv[], void *userdata) {
596 _cleanup_(free_host_infop) struct host_info *host = NULL;
597 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
598 struct unit_times *times;
599 struct boot_times *boot;
600 int n, m = 1, y = 0, r;
601 double width;
602 _cleanup_free_ char *pretty_times = NULL;
603 struct unit_times *u;
604
605 r = acquire_bus(true, &bus);
606 if (r < 0)
607 return log_error_errno(r, "Failed to create bus connection: %m");
608
609 n = acquire_boot_times(bus, &boot);
610 if (n < 0)
611 return n;
612
613 n = pretty_boot_time(bus, &pretty_times);
614 if (n < 0)
615 return n;
616
617 n = acquire_host_info(bus, &host);
618 if (n < 0)
619 return n;
620
621 n = acquire_time_data(bus, &times);
622 if (n <= 0)
623 return n;
624
625 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
626
627 width = SCALE_X * (boot->firmware_time + boot->finish_time);
628 if (width < 800.0)
629 width = 800.0;
630
631 if (boot->firmware_time > boot->loader_time)
632 m++;
633 if (boot->loader_time > 0) {
634 m++;
635 if (width < 1000.0)
636 width = 1000.0;
637 }
638 if (boot->initrd_time > 0)
639 m++;
640 if (boot->kernel_time > 0)
641 m++;
642
643 for (u = times; u < times + n; u++) {
644 double text_start, text_width;
645
646 if (u->activating < boot->userspace_time ||
647 u->activating > boot->finish_time) {
648 u->name = mfree(u->name);
649 continue;
650 }
651
652 /* If the text cannot fit on the left side then
653 * increase the svg width so it fits on the right.
654 * TODO: calculate the text width more accurately */
655 text_width = 8.0 * strlen(u->name);
656 text_start = (boot->firmware_time + u->activating) * SCALE_X;
657 if (text_width > text_start && text_width + text_start > width)
658 width = text_width + text_start;
659
660 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
661 && u->activated == 0 && u->deactivating == 0)
662 u->activated = u->deactivating = u->deactivated;
663 if (u->activated < u->activating || u->activated > boot->finish_time)
664 u->activated = boot->finish_time;
665 if (u->deactivating < u->activated || u->activated > boot->finish_time)
666 u->deactivating = boot->finish_time;
667 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
668 u->deactivated = boot->finish_time;
669 m++;
670 }
671
672 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
673 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
674 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
675
676 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
677 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
678 80.0 + width, 150.0 + (m * SCALE_Y) +
679 5 * SCALE_Y /* legend */);
680
681 /* write some basic info as a comment, including some help */
682 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
683 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
684 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
685 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
686 "<!-- point your browser to this file. -->\n\n"
687 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION);
688
689 /* style sheet */
690 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
691 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
692 " rect.background { fill: rgb(255,255,255); }\n"
693 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
694 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
695 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
696 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
697 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
698 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
699 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
700 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
701 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
702 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
703 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
704 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
705 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
706 "// line.sec1 { }\n"
707 " line.sec5 { stroke-width: 2; }\n"
708 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
709 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
710 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
711 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
712 " text.sec { font-size: 10px; }\n"
713 " ]]>\n </style>\n</defs>\n\n");
714
715 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
716 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
717 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
718 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
719 strempty(host->hostname),
720 strempty(host->kernel_name),
721 strempty(host->kernel_release),
722 strempty(host->kernel_version),
723 strempty(host->architecture),
724 strempty(host->virtualization));
725
726 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
727 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
728
729 if (boot->firmware_time > 0) {
730 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
731 svg_text(true, -(double) boot->firmware_time, y, "firmware");
732 y++;
733 }
734 if (boot->loader_time > 0) {
735 svg_bar("loader", -(double) boot->loader_time, 0, y);
736 svg_text(true, -(double) boot->loader_time, y, "loader");
737 y++;
738 }
739 if (boot->kernel_time > 0) {
740 svg_bar("kernel", 0, boot->kernel_done_time, y);
741 svg_text(true, 0, y, "kernel");
742 y++;
743 }
744 if (boot->initrd_time > 0) {
745 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
746 svg_text(true, boot->initrd_time, y, "initrd");
747 y++;
748 }
749 svg_bar("active", boot->userspace_time, boot->finish_time, y);
750 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
751 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
752 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
753 svg_text(true, boot->userspace_time, y, "systemd");
754 y++;
755
756 for (u = times; u < times + n; u++) {
757 char ts[FORMAT_TIMESPAN_MAX];
758 bool b;
759
760 if (!u->name)
761 continue;
762
763 svg_bar("activating", u->activating, u->activated, y);
764 svg_bar("active", u->activated, u->deactivating, y);
765 svg_bar("deactivating", u->deactivating, u->deactivated, y);
766
767 /* place the text on the left if we have passed the half of the svg width */
768 b = u->activating * SCALE_X < width / 2;
769 if (u->time)
770 svg_text(b, u->activating, y, "%s (%s)",
771 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
772 else
773 svg_text(b, u->activating, y, "%s", u->name);
774 y++;
775 }
776
777 svg("</g>\n");
778
779 /* Legend */
780 svg("<g transform=\"translate(20,100)\">\n");
781 y++;
782 svg_bar("activating", 0, 300000, y);
783 svg_text(true, 400000, y, "Activating");
784 y++;
785 svg_bar("active", 0, 300000, y);
786 svg_text(true, 400000, y, "Active");
787 y++;
788 svg_bar("deactivating", 0, 300000, y);
789 svg_text(true, 400000, y, "Deactivating");
790 y++;
791 svg_bar("security", 0, 300000, y);
792 svg_text(true, 400000, y, "Setting up security module");
793 y++;
794 svg_bar("generators", 0, 300000, y);
795 svg_text(true, 400000, y, "Generators");
796 y++;
797 svg_bar("unitsload", 0, 300000, y);
798 svg_text(true, 400000, y, "Loading unit files");
799 y++;
800
801 svg("</g>\n\n");
802
803 svg("</svg>\n");
804
805 free_unit_times(times, (unsigned) n);
806
807 n = 0;
808 return n;
809 }
810
811 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
812 bool last, struct unit_times *times, struct boot_times *boot) {
813 unsigned int i;
814 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
815
816 for (i = level; i != 0; i--)
817 printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE));
818
819 printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH));
820
821 if (times) {
822 if (times->time > 0)
823 printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
824 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
825 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal());
826 else if (times->activated > boot->userspace_time)
827 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
828 else
829 printf("%s", name);
830 } else
831 printf("%s", name);
832 printf("\n");
833
834 return 0;
835 }
836
837 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
838 _cleanup_free_ char *path = NULL;
839
840 assert(bus);
841 assert(name);
842 assert(deps);
843
844 path = unit_dbus_path_from_name(name);
845 if (!path)
846 return -ENOMEM;
847
848 return bus_get_unit_property_strv(bus, path, "After", deps);
849 }
850
851 static Hashmap *unit_times_hashmap;
852
853 static int list_dependencies_compare(const void *_a, const void *_b) {
854 const char **a = (const char**) _a, **b = (const char**) _b;
855 usec_t usa = 0, usb = 0;
856 struct unit_times *times;
857
858 times = hashmap_get(unit_times_hashmap, *a);
859 if (times)
860 usa = times->activated;
861 times = hashmap_get(unit_times_hashmap, *b);
862 if (times)
863 usb = times->activated;
864
865 return usb - usa;
866 }
867
868 static bool times_in_range(const struct unit_times *times, const struct boot_times *boot) {
869 return times &&
870 times->activated > 0 && times->activated <= boot->finish_time;
871 }
872
873 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
874 unsigned int branches) {
875 _cleanup_strv_free_ char **deps = NULL;
876 char **c;
877 int r = 0;
878 usec_t service_longest = 0;
879 int to_print = 0;
880 struct unit_times *times;
881 struct boot_times *boot;
882
883 if (strv_extend(units, name))
884 return log_oom();
885
886 r = list_dependencies_get_dependencies(bus, name, &deps);
887 if (r < 0)
888 return r;
889
890 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
891
892 r = acquire_boot_times(bus, &boot);
893 if (r < 0)
894 return r;
895
896 STRV_FOREACH(c, deps) {
897 times = hashmap_get(unit_times_hashmap, *c);
898 if (times_in_range(times, boot) &&
899 (times->activated >= service_longest
900 || service_longest == 0)) {
901 service_longest = times->activated;
902 break;
903 }
904 }
905
906 if (service_longest == 0)
907 return r;
908
909 STRV_FOREACH(c, deps) {
910 times = hashmap_get(unit_times_hashmap, *c);
911 if (times_in_range(times, boot) &&
912 service_longest - times->activated <= arg_fuzz)
913 to_print++;
914 }
915
916 if (!to_print)
917 return r;
918
919 STRV_FOREACH(c, deps) {
920 times = hashmap_get(unit_times_hashmap, *c);
921 if (!times_in_range(times, boot) ||
922 service_longest - times->activated > arg_fuzz)
923 continue;
924
925 to_print--;
926
927 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
928 if (r < 0)
929 return r;
930
931 if (strv_contains(*units, *c)) {
932 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
933 true, NULL, boot);
934 if (r < 0)
935 return r;
936 continue;
937 }
938
939 r = list_dependencies_one(bus, *c, level + 1, units,
940 (branches << 1) | (to_print ? 1 : 0));
941 if (r < 0)
942 return r;
943
944 if (to_print == 0)
945 break;
946 }
947 return 0;
948 }
949
950 static int list_dependencies(sd_bus *bus, const char *name) {
951 _cleanup_strv_free_ char **units = NULL;
952 char ts[FORMAT_TIMESPAN_MAX];
953 struct unit_times *times;
954 int r;
955 const char *id;
956 _cleanup_free_ char *path = NULL;
957 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
958 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
959 struct boot_times *boot;
960
961 assert(bus);
962
963 path = unit_dbus_path_from_name(name);
964 if (!path)
965 return -ENOMEM;
966
967 r = sd_bus_get_property(
968 bus,
969 "org.freedesktop.systemd1",
970 path,
971 "org.freedesktop.systemd1.Unit",
972 "Id",
973 &error,
974 &reply,
975 "s");
976 if (r < 0) {
977 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
978 return r;
979 }
980
981 r = sd_bus_message_read(reply, "s", &id);
982 if (r < 0)
983 return bus_log_parse_error(r);
984
985 times = hashmap_get(unit_times_hashmap, id);
986
987 r = acquire_boot_times(bus, &boot);
988 if (r < 0)
989 return r;
990
991 if (times) {
992 if (times->time)
993 printf("%s%s +%s%s\n", ansi_highlight_red(), id,
994 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal());
995 else if (times->activated > boot->userspace_time)
996 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
997 else
998 printf("%s\n", id);
999 }
1000
1001 return list_dependencies_one(bus, name, 0, &units, 0);
1002 }
1003
1004 static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
1005 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1006 struct unit_times *times;
1007 unsigned int i;
1008 Hashmap *h;
1009 int n, r;
1010
1011 r = acquire_bus(false, &bus);
1012 if (r < 0)
1013 return log_error_errno(r, "Failed to create bus connection: %m");
1014
1015 n = acquire_time_data(bus, &times);
1016 if (n <= 0)
1017 return n;
1018
1019 h = hashmap_new(&string_hash_ops);
1020 if (!h)
1021 return log_oom();
1022
1023 for (i = 0; i < (unsigned) n; i++) {
1024 r = hashmap_put(h, times[i].name, &times[i]);
1025 if (r < 0)
1026 return log_error_errno(r, "Failed to add entry to hashmap: %m");
1027 }
1028 unit_times_hashmap = h;
1029
1030 pager_open(arg_no_pager, false);
1031
1032 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1033 "The time the unit takes to start is printed after the \"+\" character.\n");
1034
1035 if (argc > 1) {
1036 char **name;
1037 STRV_FOREACH(name, strv_skip(argv, 1))
1038 list_dependencies(bus, *name);
1039 } else
1040 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
1041
1042 h = hashmap_free(h);
1043 free_unit_times(times, (unsigned) n);
1044 return 0;
1045 }
1046
1047 static int analyze_blame(int argc, char *argv[], void *userdata) {
1048 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1049 struct unit_times *times;
1050 unsigned i;
1051 int n, r;
1052
1053 r = acquire_bus(false, &bus);
1054 if (r < 0)
1055 return log_error_errno(r, "Failed to create bus connection: %m");
1056
1057 n = acquire_time_data(bus, &times);
1058 if (n <= 0)
1059 return n;
1060
1061 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
1062
1063 pager_open(arg_no_pager, false);
1064
1065 for (i = 0; i < (unsigned) n; i++) {
1066 char ts[FORMAT_TIMESPAN_MAX];
1067
1068 if (times[i].time > 0)
1069 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
1070 }
1071
1072 free_unit_times(times, (unsigned) n);
1073 return 0;
1074 }
1075
1076 static int analyze_time(int argc, char *argv[], void *userdata) {
1077 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1078 _cleanup_free_ char *buf = NULL;
1079 int r;
1080
1081 r = acquire_bus(false, &bus);
1082 if (r < 0)
1083 return log_error_errno(r, "Failed to create bus connection: %m");
1084
1085 r = pretty_boot_time(bus, &buf);
1086 if (r < 0)
1087 return r;
1088
1089 puts(buf);
1090 return 0;
1091 }
1092
1093 static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[], char* from_patterns[], char* to_patterns[]) {
1094 _cleanup_strv_free_ char **units = NULL;
1095 char **unit;
1096 int r;
1097 bool match_patterns;
1098
1099 assert(u);
1100 assert(prop);
1101 assert(color);
1102
1103 match_patterns = strv_fnmatch(patterns, u->id, 0);
1104
1105 if (!strv_isempty(from_patterns) &&
1106 !match_patterns &&
1107 !strv_fnmatch(from_patterns, u->id, 0))
1108 return 0;
1109
1110 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
1111 if (r < 0)
1112 return r;
1113
1114 STRV_FOREACH(unit, units) {
1115 bool match_patterns2;
1116
1117 match_patterns2 = strv_fnmatch(patterns, *unit, 0);
1118
1119 if (!strv_isempty(to_patterns) &&
1120 !match_patterns2 &&
1121 !strv_fnmatch(to_patterns, *unit, 0))
1122 continue;
1123
1124 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
1125 continue;
1126
1127 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1128 }
1129
1130 return 0;
1131 }
1132
1133 static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
1134 int r;
1135
1136 assert(bus);
1137 assert(u);
1138
1139 if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
1140 r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
1141 if (r < 0)
1142 return r;
1143 }
1144
1145 if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
1146 r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
1147 if (r < 0)
1148 return r;
1149 r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
1150 if (r < 0)
1151 return r;
1152 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
1153 if (r < 0)
1154 return r;
1155 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
1156 if (r < 0)
1157 return r;
1158 }
1159
1160 return 0;
1161 }
1162
1163 static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1164 _cleanup_strv_free_ char **expanded_patterns = NULL;
1165 char **pattern;
1166 int r;
1167
1168 STRV_FOREACH(pattern, patterns) {
1169 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1170 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
1171
1172 if (strv_extend(&expanded_patterns, *pattern) < 0)
1173 return log_oom();
1174
1175 if (string_is_glob(*pattern))
1176 continue;
1177
1178 unit = unit_dbus_path_from_name(*pattern);
1179 if (!unit)
1180 return log_oom();
1181
1182 r = sd_bus_get_property_string(
1183 bus,
1184 "org.freedesktop.systemd1",
1185 unit,
1186 "org.freedesktop.systemd1.Unit",
1187 "Id",
1188 &error,
1189 &unit_id);
1190 if (r < 0)
1191 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1192
1193 if (!streq(*pattern, unit_id)) {
1194 if (strv_extend(&expanded_patterns, unit_id) < 0)
1195 return log_oom();
1196 }
1197 }
1198
1199 *ret = expanded_patterns;
1200 expanded_patterns = NULL; /* do not free */
1201
1202 return 0;
1203 }
1204
1205 static int dot(int argc, char *argv[], void *userdata) {
1206 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1207 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1208 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1209 _cleanup_strv_free_ char **expanded_patterns = NULL;
1210 _cleanup_strv_free_ char **expanded_from_patterns = NULL;
1211 _cleanup_strv_free_ char **expanded_to_patterns = NULL;
1212 int r;
1213 UnitInfo u;
1214
1215 r = acquire_bus(false, &bus);
1216 if (r < 0)
1217 return log_error_errno(r, "Failed to create bus connection: %m");
1218
1219 r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
1220 if (r < 0)
1221 return r;
1222
1223 r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1224 if (r < 0)
1225 return r;
1226
1227 r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1228 if (r < 0)
1229 return r;
1230
1231 r = sd_bus_call_method(
1232 bus,
1233 "org.freedesktop.systemd1",
1234 "/org/freedesktop/systemd1",
1235 "org.freedesktop.systemd1.Manager",
1236 "ListUnits",
1237 &error,
1238 &reply,
1239 "");
1240 if (r < 0) {
1241 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1242 return r;
1243 }
1244
1245 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1246 if (r < 0)
1247 return bus_log_parse_error(r);
1248
1249 printf("digraph systemd {\n");
1250
1251 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1252
1253 r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1254 if (r < 0)
1255 return r;
1256 }
1257 if (r < 0)
1258 return bus_log_parse_error(r);
1259
1260 printf("}\n");
1261
1262 log_info(" Color legend: black = Requires\n"
1263 " dark blue = Requisite\n"
1264 " dark grey = Wants\n"
1265 " red = Conflicts\n"
1266 " green = After\n");
1267
1268 if (on_tty())
1269 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1270 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1271
1272 return 0;
1273 }
1274
1275 static int dump(int argc, char *argv[], void *userdata) {
1276 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1277 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1278 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1279 const char *text = NULL;
1280 int r;
1281
1282 r = acquire_bus(false, &bus);
1283 if (r < 0)
1284 return log_error_errno(r, "Failed to create bus connection: %m");
1285
1286 pager_open(arg_no_pager, false);
1287
1288 r = sd_bus_call_method(
1289 bus,
1290 "org.freedesktop.systemd1",
1291 "/org/freedesktop/systemd1",
1292 "org.freedesktop.systemd1.Manager",
1293 "Dump",
1294 &error,
1295 &reply,
1296 NULL);
1297 if (r < 0)
1298 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1299
1300 r = sd_bus_message_read(reply, "s", &text);
1301 if (r < 0)
1302 return bus_log_parse_error(r);
1303
1304 fputs(text, stdout);
1305 return 0;
1306 }
1307
1308 static int set_log_level(int argc, char *argv[], void *userdata) {
1309 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1310 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1311 int r;
1312
1313 assert(argc == 2);
1314 assert(argv);
1315
1316 r = acquire_bus(false, &bus);
1317 if (r < 0)
1318 return log_error_errno(r, "Failed to create bus connection: %m");
1319
1320 r = sd_bus_set_property(
1321 bus,
1322 "org.freedesktop.systemd1",
1323 "/org/freedesktop/systemd1",
1324 "org.freedesktop.systemd1.Manager",
1325 "LogLevel",
1326 &error,
1327 "s",
1328 argv[1]);
1329 if (r < 0)
1330 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1331
1332 return 0;
1333 }
1334
1335 static int get_log_level(int argc, char *argv[], void *userdata) {
1336 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1337 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1338 _cleanup_free_ char *level = NULL;
1339 int r;
1340
1341 r = acquire_bus(false, &bus);
1342 if (r < 0)
1343 return log_error_errno(r, "Failed to create bus connection: %m");
1344
1345 r = sd_bus_get_property_string(
1346 bus,
1347 "org.freedesktop.systemd1",
1348 "/org/freedesktop/systemd1",
1349 "org.freedesktop.systemd1.Manager",
1350 "LogLevel",
1351 &error,
1352 &level);
1353 if (r < 0)
1354 return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
1355
1356 puts(level);
1357 return 0;
1358 }
1359
1360 static int get_or_set_log_level(int argc, char *argv[], void *userdata) {
1361 return (argc == 1) ? get_log_level(argc, argv, userdata) : set_log_level(argc, argv, userdata);
1362 }
1363
1364 static int set_log_target(int argc, char *argv[], void *userdata) {
1365 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1366 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1367 int r;
1368
1369 assert(argc == 2);
1370 assert(argv);
1371
1372 r = acquire_bus(false, &bus);
1373 if (r < 0)
1374 return log_error_errno(r, "Failed to create bus connection: %m");
1375
1376 r = sd_bus_set_property(
1377 bus,
1378 "org.freedesktop.systemd1",
1379 "/org/freedesktop/systemd1",
1380 "org.freedesktop.systemd1.Manager",
1381 "LogTarget",
1382 &error,
1383 "s",
1384 argv[1]);
1385 if (r < 0)
1386 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1387
1388 return 0;
1389 }
1390
1391 static int get_log_target(int argc, char *argv[], void *userdata) {
1392 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1393 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1394 _cleanup_free_ char *target = NULL;
1395 int r;
1396
1397 r = acquire_bus(false, &bus);
1398 if (r < 0)
1399 return log_error_errno(r, "Failed to create bus connection: %m");
1400
1401 r = sd_bus_get_property_string(
1402 bus,
1403 "org.freedesktop.systemd1",
1404 "/org/freedesktop/systemd1",
1405 "org.freedesktop.systemd1.Manager",
1406 "LogTarget",
1407 &error,
1408 &target);
1409 if (r < 0)
1410 return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
1411
1412 puts(target);
1413 return 0;
1414 }
1415
1416 static int get_or_set_log_target(int argc, char *argv[], void *userdata) {
1417 return (argc == 1) ? get_log_target(argc, argv, userdata) : set_log_target(argc, argv, userdata);
1418 }
1419
1420 static int dump_unit_paths(int argc, char *argv[], void *userdata) {
1421 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1422 int r;
1423 char **p;
1424
1425 r = lookup_paths_init(&paths, arg_scope, 0, NULL);
1426 if (r < 0)
1427 return log_error_errno(r, "lookup_paths_init() failed: %m");
1428
1429 STRV_FOREACH(p, paths.search_path)
1430 puts(*p);
1431
1432 return 0;
1433 }
1434
1435 #if HAVE_SECCOMP
1436 static void dump_syscall_filter(const SyscallFilterSet *set) {
1437 const char *syscall;
1438
1439 printf("%s\n", set->name);
1440 printf(" # %s\n", set->help);
1441 NULSTR_FOREACH(syscall, set->value)
1442 printf(" %s\n", syscall);
1443 }
1444
1445 static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
1446 bool first = true;
1447
1448 pager_open(arg_no_pager, false);
1449
1450 if (strv_isempty(strv_skip(argv, 1))) {
1451 int i;
1452
1453 for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
1454 if (!first)
1455 puts("");
1456 dump_syscall_filter(syscall_filter_sets + i);
1457 first = false;
1458 }
1459 } else {
1460 char **name;
1461
1462 STRV_FOREACH(name, strv_skip(argv, 1)) {
1463 const SyscallFilterSet *set;
1464
1465 if (!first)
1466 puts("");
1467
1468 set = syscall_filter_set_find(*name);
1469 if (!set) {
1470 /* make sure the error appears below normal output */
1471 fflush(stdout);
1472
1473 log_error("Filter set \"%s\" not found.", *name);
1474 return -ENOENT;
1475 }
1476
1477 dump_syscall_filter(set);
1478 first = false;
1479 }
1480 }
1481
1482 return 0;
1483 }
1484
1485 #else
1486 static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
1487 log_error("Not compiled with syscall filters, sorry.");
1488 return -EOPNOTSUPP;
1489 }
1490 #endif
1491
1492 static int test_calendar(int argc, char *argv[], void *userdata) {
1493 int ret = 0, r;
1494 char **p;
1495 usec_t n;
1496
1497 n = now(CLOCK_REALTIME);
1498
1499 STRV_FOREACH(p, strv_skip(argv, 1)) {
1500 _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
1501 _cleanup_free_ char *t = NULL;
1502 usec_t next;
1503
1504 r = calendar_spec_from_string(*p, &spec);
1505 if (r < 0) {
1506 ret = log_error_errno(r, "Failed to parse calendar specification '%s': %m", *p);
1507 continue;
1508 }
1509
1510 r = calendar_spec_normalize(spec);
1511 if (r < 0) {
1512 ret = log_error_errno(r, "Failed to normalize calendar specification '%s': %m", *p);
1513 continue;
1514 }
1515
1516 r = calendar_spec_to_string(spec, &t);
1517 if (r < 0) {
1518 ret = log_error_errno(r, "Failed to format calendar specification '%s': %m", *p);
1519 continue;
1520 }
1521
1522 if (!streq(t, *p))
1523 printf(" Original form: %s\n", *p);
1524
1525 printf("Normalized form: %s\n", t);
1526
1527 r = calendar_spec_next_usec(spec, n, &next);
1528 if (r == -ENOENT)
1529 printf(" Next elapse: never\n");
1530 else if (r < 0) {
1531 ret = log_error_errno(r, "Failed to determine next elapse for '%s': %m", *p);
1532 continue;
1533 } else {
1534 char buffer[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESTAMP_RELATIVE_MAX)];
1535
1536 printf(" Next elapse: %s\n", format_timestamp(buffer, sizeof(buffer), next));
1537
1538 if (!in_utc_timezone())
1539 printf(" (in UTC): %s\n", format_timestamp_utc(buffer, sizeof(buffer), next));
1540
1541 printf(" From now: %s\n", format_timestamp_relative(buffer, sizeof(buffer), next));
1542 }
1543
1544 if (*(p+1))
1545 putchar('\n');
1546 }
1547
1548 return ret;
1549 }
1550
1551 static int service_watchdogs(int argc, char *argv[], void *userdata) {
1552 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1553 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1554 int b, r;
1555
1556 assert(IN_SET(argc, 1, 2));
1557 assert(argv);
1558
1559 r = acquire_bus(false, &bus);
1560 if (r < 0)
1561 return log_error_errno(r, "Failed to create bus connection: %m");
1562
1563 /* get ServiceWatchdogs */
1564 if (argc == 1) {
1565 r = sd_bus_get_property_trivial(
1566 bus,
1567 "org.freedesktop.systemd1",
1568 "/org/freedesktop/systemd1",
1569 "org.freedesktop.systemd1.Manager",
1570 "ServiceWatchdogs",
1571 &error,
1572 'b',
1573 &b);
1574 if (r < 0)
1575 return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
1576
1577 printf("%s\n", yes_no(!!b));
1578
1579 return 0;
1580 }
1581
1582 /* set ServiceWatchdogs */
1583 b = parse_boolean(argv[1]);
1584 if (b < 0) {
1585 log_error("Failed to parse service-watchdogs argument.");
1586 return -EINVAL;
1587 }
1588
1589 r = sd_bus_set_property(
1590 bus,
1591 "org.freedesktop.systemd1",
1592 "/org/freedesktop/systemd1",
1593 "org.freedesktop.systemd1.Manager",
1594 "ServiceWatchdogs",
1595 &error,
1596 "b",
1597 b);
1598 if (r < 0)
1599 return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
1600
1601 return 0;
1602 }
1603
1604 static int do_verify(int argc, char *argv[], void *userdata) {
1605 return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
1606 }
1607
1608 static int help(int argc, char *argv[], void *userdata) {
1609
1610 pager_open(arg_no_pager, false);
1611
1612 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1613 "Profile systemd, show unit dependencies, check unit files.\n\n"
1614 " -h --help Show this help\n"
1615 " --version Show package version\n"
1616 " --no-pager Do not pipe output into a pager\n"
1617 " --system Operate on system systemd instance\n"
1618 " --user Operate on user systemd instance\n"
1619 " --global Operate on global user configuration\n"
1620 " -H --host=[USER@]HOST Operate on remote host\n"
1621 " -M --machine=CONTAINER Operate on local container\n"
1622 " --order Show only order in the graph\n"
1623 " --require Show only requirement in the graph\n"
1624 " --from-pattern=GLOB Show only origins in the graph\n"
1625 " --to-pattern=GLOB Show only destinations in the graph\n"
1626 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1627 " earlier than the latest in the branch\n"
1628 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1629 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
1630 "Commands:\n"
1631 " time Print time spent in the kernel\n"
1632 " blame Print list of running units ordered by time to init\n"
1633 " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
1634 " plot Output SVG graphic showing service initialization\n"
1635 " dot [UNIT...] Output dependency graph in man:dot(1) format\n"
1636 " log-level [LEVEL] Get/set logging threshold for manager\n"
1637 " log-target [TARGET] Get/set logging target for manager\n"
1638 " dump Output state serialization of service manager\n"
1639 " unit-paths List load directories for units\n"
1640 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1641 " verify FILE... Check unit files for correctness\n"
1642 " calendar SPEC... Validate repetitive calendar time events\n"
1643 " service-watchdogs [BOOL] Get/set service watchdog state\n"
1644 , program_invocation_short_name);
1645
1646 /* When updating this list, including descriptions, apply
1647 * changes to shell-completion/bash/systemd-analyze and
1648 * shell-completion/zsh/_systemd-analyze too. */
1649
1650 return 0;
1651 }
1652
1653 static int parse_argv(int argc, char *argv[]) {
1654 enum {
1655 ARG_VERSION = 0x100,
1656 ARG_ORDER,
1657 ARG_REQUIRE,
1658 ARG_SYSTEM,
1659 ARG_USER,
1660 ARG_GLOBAL,
1661 ARG_DOT_FROM_PATTERN,
1662 ARG_DOT_TO_PATTERN,
1663 ARG_FUZZ,
1664 ARG_NO_PAGER,
1665 ARG_MAN,
1666 ARG_GENERATORS,
1667 };
1668
1669 static const struct option options[] = {
1670 { "help", no_argument, NULL, 'h' },
1671 { "version", no_argument, NULL, ARG_VERSION },
1672 { "order", no_argument, NULL, ARG_ORDER },
1673 { "require", no_argument, NULL, ARG_REQUIRE },
1674 { "system", no_argument, NULL, ARG_SYSTEM },
1675 { "user", no_argument, NULL, ARG_USER },
1676 { "global", no_argument, NULL, ARG_GLOBAL },
1677 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1678 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1679 { "fuzz", required_argument, NULL, ARG_FUZZ },
1680 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1681 { "man", optional_argument, NULL, ARG_MAN },
1682 { "generators", optional_argument, NULL, ARG_GENERATORS },
1683 { "host", required_argument, NULL, 'H' },
1684 { "machine", required_argument, NULL, 'M' },
1685 {}
1686 };
1687
1688 int r, c;
1689
1690 assert(argc >= 0);
1691 assert(argv);
1692
1693 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1694 switch (c) {
1695
1696 case 'h':
1697 return help(0, NULL, NULL);
1698
1699 case ARG_VERSION:
1700 return version();
1701
1702 case ARG_SYSTEM:
1703 arg_scope = UNIT_FILE_SYSTEM;
1704 break;
1705
1706 case ARG_USER:
1707 arg_scope = UNIT_FILE_USER;
1708 break;
1709
1710 case ARG_GLOBAL:
1711 arg_scope = UNIT_FILE_GLOBAL;
1712 break;
1713
1714 case ARG_ORDER:
1715 arg_dot = DEP_ORDER;
1716 break;
1717
1718 case ARG_REQUIRE:
1719 arg_dot = DEP_REQUIRE;
1720 break;
1721
1722 case ARG_DOT_FROM_PATTERN:
1723 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1724 return log_oom();
1725
1726 break;
1727
1728 case ARG_DOT_TO_PATTERN:
1729 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1730 return log_oom();
1731
1732 break;
1733
1734 case ARG_FUZZ:
1735 r = parse_sec(optarg, &arg_fuzz);
1736 if (r < 0)
1737 return r;
1738 break;
1739
1740 case ARG_NO_PAGER:
1741 arg_no_pager = true;
1742 break;
1743
1744 case 'H':
1745 arg_transport = BUS_TRANSPORT_REMOTE;
1746 arg_host = optarg;
1747 break;
1748
1749 case 'M':
1750 arg_transport = BUS_TRANSPORT_MACHINE;
1751 arg_host = optarg;
1752 break;
1753
1754 case ARG_MAN:
1755 if (optarg) {
1756 r = parse_boolean(optarg);
1757 if (r < 0) {
1758 log_error("Failed to parse --man= argument.");
1759 return -EINVAL;
1760 }
1761
1762 arg_man = !!r;
1763 } else
1764 arg_man = true;
1765
1766 break;
1767
1768 case ARG_GENERATORS:
1769 if (optarg) {
1770 r = parse_boolean(optarg);
1771 if (r < 0) {
1772 log_error("Failed to parse --generators= argument.");
1773 return -EINVAL;
1774 }
1775
1776 arg_generators = !!r;
1777 } else
1778 arg_generators = true;
1779
1780 break;
1781
1782 case '?':
1783 return -EINVAL;
1784
1785 default:
1786 assert_not_reached("Unhandled option code.");
1787 }
1788
1789 if (arg_scope == UNIT_FILE_GLOBAL &&
1790 !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify")) {
1791 log_error("Option --global only makes sense with verbs dot, unit-paths, verify.");
1792 return -EINVAL;
1793 }
1794
1795 return 1; /* work to do */
1796 }
1797
1798 int main(int argc, char *argv[]) {
1799
1800 static const Verb verbs[] = {
1801 { "help", VERB_ANY, VERB_ANY, 0, help },
1802 { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time },
1803 { "blame", VERB_ANY, 1, 0, analyze_blame },
1804 { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain },
1805 { "plot", VERB_ANY, 1, 0, analyze_plot },
1806 { "dot", VERB_ANY, VERB_ANY, 0, dot },
1807 { "log-level", VERB_ANY, 2, 0, get_or_set_log_level },
1808 { "log-target", VERB_ANY, 2, 0, get_or_set_log_target },
1809 /* The following four verbs are deprecated aliases */
1810 { "set-log-level", 2, 2, 0, set_log_level },
1811 { "get-log-level", VERB_ANY, 1, 0, get_log_level },
1812 { "set-log-target", 2, 2, 0, set_log_target },
1813 { "get-log-target", VERB_ANY, 1, 0, get_log_target },
1814 { "dump", VERB_ANY, 1, 0, dump },
1815 { "unit-paths", 1, 1, 0, dump_unit_paths },
1816 { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
1817 { "verify", 2, VERB_ANY, 0, do_verify },
1818 { "calendar", 2, VERB_ANY, 0, test_calendar },
1819 { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
1820 {}
1821 };
1822
1823 int r;
1824
1825 setlocale(LC_ALL, "");
1826 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1827
1828 log_parse_environment();
1829 log_open();
1830
1831 r = parse_argv(argc, argv);
1832 if (r <= 0)
1833 goto finish;
1834
1835 r = dispatch_verb(argc, argv, verbs, NULL);
1836
1837 finish:
1838 pager_close();
1839
1840 strv_free(arg_dot_from_patterns);
1841 strv_free(arg_dot_to_patterns);
1842
1843 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1844 }