]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/analyze/analyze.c
Merge pull request #7419 from keszybz/tmpfiles-fixes
[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
51 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
52 #define SCALE_Y (20.0)
53
54 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
55
56 #define svg(...) printf(__VA_ARGS__)
57
58 #define svg_bar(class, x1, x2, y) \
59 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
60 (class), \
61 SCALE_X * (x1), SCALE_Y * (y), \
62 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
63
64 #define svg_text(b, x, y, format, ...) \
65 do { \
66 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
67 svg(format, ## __VA_ARGS__); \
68 svg("</text>\n"); \
69 } while (false)
70
71 static enum dot {
72 DEP_ALL,
73 DEP_ORDER,
74 DEP_REQUIRE
75 } arg_dot = DEP_ALL;
76 static char** arg_dot_from_patterns = NULL;
77 static char** arg_dot_to_patterns = NULL;
78 static usec_t arg_fuzz = 0;
79 static bool arg_no_pager = false;
80 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
81 static char *arg_host = NULL;
82 static bool arg_user = false;
83 static bool arg_man = true;
84 static bool arg_generators = false;
85
86 struct boot_times {
87 usec_t firmware_time;
88 usec_t loader_time;
89 usec_t kernel_time;
90 usec_t kernel_done_time;
91 usec_t initrd_time;
92 usec_t userspace_time;
93 usec_t finish_time;
94 usec_t security_start_time;
95 usec_t security_finish_time;
96 usec_t generators_start_time;
97 usec_t generators_finish_time;
98 usec_t unitsload_start_time;
99 usec_t unitsload_finish_time;
100
101 /*
102 * If we're analyzing the user instance, all timestamps will be offset
103 * by its own start-up timestamp, which may be arbitrarily big.
104 * With "plot", this causes arbitrarily wide output SVG files which almost
105 * completely consist of empty space. Thus we cancel out this offset.
106 *
107 * This offset is subtracted from times above by acquire_boot_times(),
108 * but it still needs to be subtracted from unit-specific timestamps
109 * (so it is stored here for reference).
110 */
111 usec_t reverse_offset;
112 };
113
114 struct unit_times {
115 char *name;
116 usec_t activating;
117 usec_t activated;
118 usec_t deactivated;
119 usec_t deactivating;
120 usec_t time;
121 };
122
123 struct host_info {
124 char *hostname;
125 char *kernel_name;
126 char *kernel_release;
127 char *kernel_version;
128 char *os_pretty_name;
129 char *virtualization;
130 char *architecture;
131 };
132
133 static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
134 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
135 int r;
136
137 assert(bus);
138 assert(path);
139 assert(interface);
140 assert(property);
141 assert(val);
142
143 r = sd_bus_get_property_trivial(
144 bus,
145 "org.freedesktop.systemd1",
146 path,
147 interface,
148 property,
149 &error,
150 't', val);
151
152 if (r < 0) {
153 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
154 return r;
155 }
156
157 return 0;
158 }
159
160 static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
161 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
162 int r;
163
164 assert(bus);
165 assert(path);
166 assert(property);
167 assert(strv);
168
169 r = sd_bus_get_property_strv(
170 bus,
171 "org.freedesktop.systemd1",
172 path,
173 "org.freedesktop.systemd1.Unit",
174 property,
175 &error,
176 strv);
177 if (r < 0) {
178 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
179 return r;
180 }
181
182 return 0;
183 }
184
185 static int compare_unit_time(const void *a, const void *b) {
186 return compare(((struct unit_times *)b)->time,
187 ((struct unit_times *)a)->time);
188 }
189
190 static int compare_unit_start(const void *a, const void *b) {
191 return compare(((struct unit_times *)a)->activating,
192 ((struct unit_times *)b)->activating);
193 }
194
195 static void free_unit_times(struct unit_times *t, unsigned n) {
196 struct unit_times *p;
197
198 for (p = t; p < t + n; p++)
199 free(p->name);
200
201 free(t);
202 }
203
204 static void subtract_timestamp(usec_t *a, usec_t b) {
205 assert(a);
206
207 if (*a > 0) {
208 assert(*a >= b);
209 *a -= b;
210 }
211 }
212
213 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
214 static struct boot_times times;
215 static bool cached = false;
216
217 if (cached)
218 goto finish;
219
220 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
221
222 if (bus_get_uint64_property(bus,
223 "/org/freedesktop/systemd1",
224 "org.freedesktop.systemd1.Manager",
225 "FirmwareTimestampMonotonic",
226 &times.firmware_time) < 0 ||
227 bus_get_uint64_property(bus,
228 "/org/freedesktop/systemd1",
229 "org.freedesktop.systemd1.Manager",
230 "LoaderTimestampMonotonic",
231 &times.loader_time) < 0 ||
232 bus_get_uint64_property(bus,
233 "/org/freedesktop/systemd1",
234 "org.freedesktop.systemd1.Manager",
235 "KernelTimestamp",
236 &times.kernel_time) < 0 ||
237 bus_get_uint64_property(bus,
238 "/org/freedesktop/systemd1",
239 "org.freedesktop.systemd1.Manager",
240 "InitRDTimestampMonotonic",
241 &times.initrd_time) < 0 ||
242 bus_get_uint64_property(bus,
243 "/org/freedesktop/systemd1",
244 "org.freedesktop.systemd1.Manager",
245 "UserspaceTimestampMonotonic",
246 &times.userspace_time) < 0 ||
247 bus_get_uint64_property(bus,
248 "/org/freedesktop/systemd1",
249 "org.freedesktop.systemd1.Manager",
250 "FinishTimestampMonotonic",
251 &times.finish_time) < 0 ||
252 bus_get_uint64_property(bus,
253 "/org/freedesktop/systemd1",
254 "org.freedesktop.systemd1.Manager",
255 "SecurityStartTimestampMonotonic",
256 &times.security_start_time) < 0 ||
257 bus_get_uint64_property(bus,
258 "/org/freedesktop/systemd1",
259 "org.freedesktop.systemd1.Manager",
260 "SecurityFinishTimestampMonotonic",
261 &times.security_finish_time) < 0 ||
262 bus_get_uint64_property(bus,
263 "/org/freedesktop/systemd1",
264 "org.freedesktop.systemd1.Manager",
265 "GeneratorsStartTimestampMonotonic",
266 &times.generators_start_time) < 0 ||
267 bus_get_uint64_property(bus,
268 "/org/freedesktop/systemd1",
269 "org.freedesktop.systemd1.Manager",
270 "GeneratorsFinishTimestampMonotonic",
271 &times.generators_finish_time) < 0 ||
272 bus_get_uint64_property(bus,
273 "/org/freedesktop/systemd1",
274 "org.freedesktop.systemd1.Manager",
275 "UnitsLoadStartTimestampMonotonic",
276 &times.unitsload_start_time) < 0 ||
277 bus_get_uint64_property(bus,
278 "/org/freedesktop/systemd1",
279 "org.freedesktop.systemd1.Manager",
280 "UnitsLoadFinishTimestampMonotonic",
281 &times.unitsload_finish_time) < 0)
282 return -EIO;
283
284 if (times.finish_time <= 0) {
285 log_error("Bootup is not yet finished. Please try again later.");
286 return -EINPROGRESS;
287 }
288
289 if (arg_user) {
290 /*
291 * User-instance-specific timestamps processing
292 * (see comment to reverse_offset in struct boot_times).
293 */
294 times.reverse_offset = times.userspace_time;
295
296 times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
297 subtract_timestamp(&times.finish_time, times.reverse_offset);
298
299 subtract_timestamp(&times.security_start_time, times.reverse_offset);
300 subtract_timestamp(&times.security_finish_time, times.reverse_offset);
301
302 subtract_timestamp(&times.generators_start_time, times.reverse_offset);
303 subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
304
305 subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
306 subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
307 } else {
308 if (times.initrd_time)
309 times.kernel_done_time = times.initrd_time;
310 else
311 times.kernel_done_time = times.userspace_time;
312 }
313
314 cached = true;
315
316 finish:
317 *bt = &times;
318 return 0;
319 }
320
321 static void free_host_info(struct host_info *hi) {
322
323 if (!hi)
324 return;
325
326 free(hi->hostname);
327 free(hi->kernel_name);
328 free(hi->kernel_release);
329 free(hi->kernel_version);
330 free(hi->os_pretty_name);
331 free(hi->virtualization);
332 free(hi->architecture);
333 free(hi);
334 }
335
336 DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info*, free_host_info);
337
338 static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
339 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
340 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
341 int r, c = 0;
342 struct boot_times *boot_times = NULL;
343 struct unit_times *unit_times = NULL;
344 size_t size = 0;
345 UnitInfo u;
346
347 r = acquire_boot_times(bus, &boot_times);
348 if (r < 0)
349 goto fail;
350
351 r = sd_bus_call_method(
352 bus,
353 "org.freedesktop.systemd1",
354 "/org/freedesktop/systemd1",
355 "org.freedesktop.systemd1.Manager",
356 "ListUnits",
357 &error, &reply,
358 NULL);
359 if (r < 0) {
360 log_error("Failed to list units: %s", bus_error_message(&error, -r));
361 goto fail;
362 }
363
364 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
365 if (r < 0) {
366 bus_log_parse_error(r);
367 goto fail;
368 }
369
370 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
371 struct unit_times *t;
372
373 if (!GREEDY_REALLOC(unit_times, size, c+1)) {
374 r = log_oom();
375 goto fail;
376 }
377
378 t = unit_times+c;
379 t->name = NULL;
380
381 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
382
383 if (bus_get_uint64_property(bus, u.unit_path,
384 "org.freedesktop.systemd1.Unit",
385 "InactiveExitTimestampMonotonic",
386 &t->activating) < 0 ||
387 bus_get_uint64_property(bus, u.unit_path,
388 "org.freedesktop.systemd1.Unit",
389 "ActiveEnterTimestampMonotonic",
390 &t->activated) < 0 ||
391 bus_get_uint64_property(bus, u.unit_path,
392 "org.freedesktop.systemd1.Unit",
393 "ActiveExitTimestampMonotonic",
394 &t->deactivating) < 0 ||
395 bus_get_uint64_property(bus, u.unit_path,
396 "org.freedesktop.systemd1.Unit",
397 "InactiveEnterTimestampMonotonic",
398 &t->deactivated) < 0) {
399 r = -EIO;
400 goto fail;
401 }
402
403 subtract_timestamp(&t->activating, boot_times->reverse_offset);
404 subtract_timestamp(&t->activated, boot_times->reverse_offset);
405 subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
406 subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
407
408 if (t->activated >= t->activating)
409 t->time = t->activated - t->activating;
410 else if (t->deactivated >= t->activating)
411 t->time = t->deactivated - t->activating;
412 else
413 t->time = 0;
414
415 if (t->activating == 0)
416 continue;
417
418 t->name = strdup(u.id);
419 if (t->name == NULL) {
420 r = log_oom();
421 goto fail;
422 }
423 c++;
424 }
425 if (r < 0) {
426 bus_log_parse_error(r);
427 goto fail;
428 }
429
430 *out = unit_times;
431 return c;
432
433 fail:
434 if (unit_times)
435 free_unit_times(unit_times, (unsigned) c);
436 return r;
437 }
438
439 static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
440 static const struct bus_properties_map hostname_map[] = {
441 { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
442 { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
443 { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
444 { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
445 { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
446 {}
447 };
448
449 static const struct bus_properties_map manager_map[] = {
450 { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
451 { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
452 {}
453 };
454
455 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
456 _cleanup_(free_host_infop) struct host_info *host;
457 int r;
458
459 host = new0(struct host_info, 1);
460 if (!host)
461 return log_oom();
462
463 r = bus_map_all_properties(bus,
464 "org.freedesktop.hostname1",
465 "/org/freedesktop/hostname1",
466 hostname_map,
467 &error,
468 host);
469 if (r < 0)
470 log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
471
472 r = bus_map_all_properties(bus,
473 "org.freedesktop.systemd1",
474 "/org/freedesktop/systemd1",
475 manager_map,
476 &error,
477 host);
478 if (r < 0)
479 return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r));
480
481 *hi = host;
482 host = NULL;
483
484 return 0;
485 }
486
487 static int pretty_boot_time(sd_bus *bus, char **_buf) {
488 char ts[FORMAT_TIMESPAN_MAX];
489 struct boot_times *t;
490 static char buf[4096];
491 size_t size;
492 char *ptr;
493 int r;
494 usec_t activated_time = USEC_INFINITY;
495 _cleanup_free_ char* path = NULL, *unit_id = NULL;
496 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
497
498 r = acquire_boot_times(bus, &t);
499 if (r < 0)
500 return r;
501
502 path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
503 if (!path)
504 return log_oom();
505
506 r = sd_bus_get_property_string(
507 bus,
508 "org.freedesktop.systemd1",
509 path,
510 "org.freedesktop.systemd1.Unit",
511 "Id",
512 &error,
513 &unit_id);
514 if (r < 0) {
515 log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
516 unit_id = NULL;
517 }
518
519 r = bus_get_uint64_property(bus, path,
520 "org.freedesktop.systemd1.Unit",
521 "ActiveEnterTimestampMonotonic",
522 &activated_time);
523 if (r < 0) {
524 log_info_errno(r, "default.target seems not to be started. Continuing...");
525 activated_time = USEC_INFINITY;
526 }
527
528 ptr = buf;
529 size = sizeof(buf);
530
531 size = strpcpyf(&ptr, size, "Startup finished in ");
532 if (t->firmware_time)
533 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
534 if (t->loader_time)
535 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
536 if (t->kernel_time)
537 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
538 if (t->initrd_time > 0)
539 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
540
541 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
542 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
543
544 if (unit_id && activated_time != USEC_INFINITY)
545 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));
546
547 ptr = strdup(buf);
548 if (!ptr)
549 return log_oom();
550
551 *_buf = ptr;
552 return 0;
553 }
554
555 static void svg_graph_box(double height, double begin, double end) {
556 long long i;
557
558 /* outside box, fill */
559 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
560 SCALE_X * (end - begin), SCALE_Y * height);
561
562 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
563 /* lines for each second */
564 if (i % 5000000 == 0)
565 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
566 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
567 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
568 else if (i % 1000000 == 0)
569 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
570 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
571 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
572 else
573 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
574 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
575 }
576 }
577
578 static int analyze_plot(sd_bus *bus) {
579 _cleanup_(free_host_infop) struct host_info *host = NULL;
580 struct unit_times *times;
581 struct boot_times *boot;
582 int n, m = 1, y=0;
583 double width;
584 _cleanup_free_ char *pretty_times = NULL;
585 struct unit_times *u;
586
587 n = acquire_boot_times(bus, &boot);
588 if (n < 0)
589 return n;
590
591 n = pretty_boot_time(bus, &pretty_times);
592 if (n < 0)
593 return n;
594
595 n = acquire_host_info(bus, &host);
596 if (n < 0)
597 return n;
598
599 n = acquire_time_data(bus, &times);
600 if (n <= 0)
601 return n;
602
603 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
604
605 width = SCALE_X * (boot->firmware_time + boot->finish_time);
606 if (width < 800.0)
607 width = 800.0;
608
609 if (boot->firmware_time > boot->loader_time)
610 m++;
611 if (boot->loader_time) {
612 m++;
613 if (width < 1000.0)
614 width = 1000.0;
615 }
616 if (boot->initrd_time)
617 m++;
618 if (boot->kernel_time)
619 m++;
620
621 for (u = times; u < times + n; u++) {
622 double text_start, text_width;
623
624 if (u->activating < boot->userspace_time ||
625 u->activating > boot->finish_time) {
626 u->name = mfree(u->name);
627 continue;
628 }
629
630 /* If the text cannot fit on the left side then
631 * increase the svg width so it fits on the right.
632 * TODO: calculate the text width more accurately */
633 text_width = 8.0 * strlen(u->name);
634 text_start = (boot->firmware_time + u->activating) * SCALE_X;
635 if (text_width > text_start && text_width + text_start > width)
636 width = text_width + text_start;
637
638 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
639 && u->activated == 0 && u->deactivating == 0)
640 u->activated = u->deactivating = u->deactivated;
641 if (u->activated < u->activating || u->activated > boot->finish_time)
642 u->activated = boot->finish_time;
643 if (u->deactivating < u->activated || u->activated > boot->finish_time)
644 u->deactivating = boot->finish_time;
645 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
646 u->deactivated = boot->finish_time;
647 m++;
648 }
649
650 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
651 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
652 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
653
654 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
655 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
656 80.0 + width, 150.0 + (m * SCALE_Y) +
657 5 * SCALE_Y /* legend */);
658
659 /* write some basic info as a comment, including some help */
660 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
661 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
662 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
663 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
664 "<!-- point your browser to this file. -->\n\n"
665 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION);
666
667 /* style sheet */
668 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
669 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
670 " rect.background { fill: rgb(255,255,255); }\n"
671 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
672 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
673 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
674 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
675 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
676 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
677 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
678 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
679 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
680 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
681 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
682 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
683 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
684 "// line.sec1 { }\n"
685 " line.sec5 { stroke-width: 2; }\n"
686 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
687 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
688 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
689 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
690 " text.sec { font-size: 10px; }\n"
691 " ]]>\n </style>\n</defs>\n\n");
692
693 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
694 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
695 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
696 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
697 strempty(host->hostname),
698 strempty(host->kernel_name),
699 strempty(host->kernel_release),
700 strempty(host->kernel_version),
701 strempty(host->architecture),
702 strempty(host->virtualization));
703
704 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
705 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
706
707 if (boot->firmware_time) {
708 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
709 svg_text(true, -(double) boot->firmware_time, y, "firmware");
710 y++;
711 }
712 if (boot->loader_time) {
713 svg_bar("loader", -(double) boot->loader_time, 0, y);
714 svg_text(true, -(double) boot->loader_time, y, "loader");
715 y++;
716 }
717 if (boot->kernel_time) {
718 svg_bar("kernel", 0, boot->kernel_done_time, y);
719 svg_text(true, 0, y, "kernel");
720 y++;
721 }
722 if (boot->initrd_time) {
723 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
724 svg_text(true, boot->initrd_time, y, "initrd");
725 y++;
726 }
727 svg_bar("active", boot->userspace_time, boot->finish_time, y);
728 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
729 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
730 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
731 svg_text(true, boot->userspace_time, y, "systemd");
732 y++;
733
734 for (u = times; u < times + n; u++) {
735 char ts[FORMAT_TIMESPAN_MAX];
736 bool b;
737
738 if (!u->name)
739 continue;
740
741 svg_bar("activating", u->activating, u->activated, y);
742 svg_bar("active", u->activated, u->deactivating, y);
743 svg_bar("deactivating", u->deactivating, u->deactivated, y);
744
745 /* place the text on the left if we have passed the half of the svg width */
746 b = u->activating * SCALE_X < width / 2;
747 if (u->time)
748 svg_text(b, u->activating, y, "%s (%s)",
749 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
750 else
751 svg_text(b, u->activating, y, "%s", u->name);
752 y++;
753 }
754
755 svg("</g>\n");
756
757 /* Legend */
758 svg("<g transform=\"translate(20,100)\">\n");
759 y++;
760 svg_bar("activating", 0, 300000, y);
761 svg_text(true, 400000, y, "Activating");
762 y++;
763 svg_bar("active", 0, 300000, y);
764 svg_text(true, 400000, y, "Active");
765 y++;
766 svg_bar("deactivating", 0, 300000, y);
767 svg_text(true, 400000, y, "Deactivating");
768 y++;
769 svg_bar("security", 0, 300000, y);
770 svg_text(true, 400000, y, "Setting up security module");
771 y++;
772 svg_bar("generators", 0, 300000, y);
773 svg_text(true, 400000, y, "Generators");
774 y++;
775 svg_bar("unitsload", 0, 300000, y);
776 svg_text(true, 400000, y, "Loading unit files");
777 y++;
778
779 svg("</g>\n\n");
780
781 svg("</svg>\n");
782
783 free_unit_times(times, (unsigned) n);
784
785 n = 0;
786 return n;
787 }
788
789 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
790 bool last, struct unit_times *times, struct boot_times *boot) {
791 unsigned int i;
792 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
793
794 for (i = level; i != 0; i--)
795 printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE));
796
797 printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH));
798
799 if (times) {
800 if (times->time)
801 printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
802 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
803 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal());
804 else if (times->activated > boot->userspace_time)
805 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
806 else
807 printf("%s", name);
808 } else
809 printf("%s", name);
810 printf("\n");
811
812 return 0;
813 }
814
815 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
816 _cleanup_free_ char *path = NULL;
817
818 assert(bus);
819 assert(name);
820 assert(deps);
821
822 path = unit_dbus_path_from_name(name);
823 if (path == NULL)
824 return -ENOMEM;
825
826 return bus_get_unit_property_strv(bus, path, "After", deps);
827 }
828
829 static Hashmap *unit_times_hashmap;
830
831 static int list_dependencies_compare(const void *_a, const void *_b) {
832 const char **a = (const char**) _a, **b = (const char**) _b;
833 usec_t usa = 0, usb = 0;
834 struct unit_times *times;
835
836 times = hashmap_get(unit_times_hashmap, *a);
837 if (times)
838 usa = times->activated;
839 times = hashmap_get(unit_times_hashmap, *b);
840 if (times)
841 usb = times->activated;
842
843 return usb - usa;
844 }
845
846 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
847 unsigned int branches) {
848 _cleanup_strv_free_ char **deps = NULL;
849 char **c;
850 int r = 0;
851 usec_t service_longest = 0;
852 int to_print = 0;
853 struct unit_times *times;
854 struct boot_times *boot;
855
856 if (strv_extend(units, name))
857 return log_oom();
858
859 r = list_dependencies_get_dependencies(bus, name, &deps);
860 if (r < 0)
861 return r;
862
863 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
864
865 r = acquire_boot_times(bus, &boot);
866 if (r < 0)
867 return r;
868
869 STRV_FOREACH(c, deps) {
870 times = hashmap_get(unit_times_hashmap, *c);
871 if (times
872 && times->activated
873 && times->activated <= boot->finish_time
874 && (times->activated >= service_longest
875 || service_longest == 0)) {
876 service_longest = times->activated;
877 break;
878 }
879 }
880
881 if (service_longest == 0 )
882 return r;
883
884 STRV_FOREACH(c, deps) {
885 times = hashmap_get(unit_times_hashmap, *c);
886 if (times && times->activated && times->activated <= boot->finish_time && (service_longest - times->activated) <= arg_fuzz)
887 to_print++;
888 }
889
890 if (!to_print)
891 return r;
892
893 STRV_FOREACH(c, deps) {
894 times = hashmap_get(unit_times_hashmap, *c);
895 if (!times
896 || !times->activated
897 || times->activated > boot->finish_time
898 || service_longest - times->activated > arg_fuzz)
899 continue;
900
901 to_print--;
902
903 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
904 if (r < 0)
905 return r;
906
907 if (strv_contains(*units, *c)) {
908 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
909 true, NULL, boot);
910 if (r < 0)
911 return r;
912 continue;
913 }
914
915 r = list_dependencies_one(bus, *c, level + 1, units,
916 (branches << 1) | (to_print ? 1 : 0));
917 if (r < 0)
918 return r;
919
920 if (!to_print)
921 break;
922 }
923 return 0;
924 }
925
926 static int list_dependencies(sd_bus *bus, const char *name) {
927 _cleanup_strv_free_ char **units = NULL;
928 char ts[FORMAT_TIMESPAN_MAX];
929 struct unit_times *times;
930 int r;
931 const char *id;
932 _cleanup_free_ char *path = NULL;
933 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
934 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
935 struct boot_times *boot;
936
937 assert(bus);
938
939 path = unit_dbus_path_from_name(name);
940 if (path == NULL)
941 return -ENOMEM;
942
943 r = sd_bus_get_property(
944 bus,
945 "org.freedesktop.systemd1",
946 path,
947 "org.freedesktop.systemd1.Unit",
948 "Id",
949 &error,
950 &reply,
951 "s");
952 if (r < 0) {
953 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
954 return r;
955 }
956
957 r = sd_bus_message_read(reply, "s", &id);
958 if (r < 0)
959 return bus_log_parse_error(r);
960
961 times = hashmap_get(unit_times_hashmap, id);
962
963 r = acquire_boot_times(bus, &boot);
964 if (r < 0)
965 return r;
966
967 if (times) {
968 if (times->time)
969 printf("%s%s +%s%s\n", ansi_highlight_red(), id,
970 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal());
971 else if (times->activated > boot->userspace_time)
972 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
973 else
974 printf("%s\n", id);
975 }
976
977 return list_dependencies_one(bus, name, 0, &units, 0);
978 }
979
980 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
981 struct unit_times *times;
982 unsigned int i;
983 Hashmap *h;
984 int n, r;
985
986 n = acquire_time_data(bus, &times);
987 if (n <= 0)
988 return n;
989
990 h = hashmap_new(&string_hash_ops);
991 if (!h)
992 return -ENOMEM;
993
994 for (i = 0; i < (unsigned)n; i++) {
995 r = hashmap_put(h, times[i].name, &times[i]);
996 if (r < 0)
997 return r;
998 }
999 unit_times_hashmap = h;
1000
1001 pager_open(arg_no_pager, false);
1002
1003 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1004 "The time the unit takes to start is printed after the \"+\" character.\n");
1005
1006 if (!strv_isempty(names)) {
1007 char **name;
1008 STRV_FOREACH(name, names)
1009 list_dependencies(bus, *name);
1010 } else
1011 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
1012
1013 hashmap_free(h);
1014 free_unit_times(times, (unsigned) n);
1015 return 0;
1016 }
1017
1018 static int analyze_blame(sd_bus *bus) {
1019 struct unit_times *times;
1020 unsigned i;
1021 int n;
1022
1023 n = acquire_time_data(bus, &times);
1024 if (n <= 0)
1025 return n;
1026
1027 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
1028
1029 pager_open(arg_no_pager, false);
1030
1031 for (i = 0; i < (unsigned) n; i++) {
1032 char ts[FORMAT_TIMESPAN_MAX];
1033
1034 if (times[i].time > 0)
1035 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
1036 }
1037
1038 free_unit_times(times, (unsigned) n);
1039 return 0;
1040 }
1041
1042 static int analyze_time(sd_bus *bus) {
1043 _cleanup_free_ char *buf = NULL;
1044 int r;
1045
1046 r = pretty_boot_time(bus, &buf);
1047 if (r < 0)
1048 return r;
1049
1050 puts(buf);
1051 return 0;
1052 }
1053
1054 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[]) {
1055 _cleanup_strv_free_ char **units = NULL;
1056 char **unit;
1057 int r;
1058 bool match_patterns;
1059
1060 assert(u);
1061 assert(prop);
1062 assert(color);
1063
1064 match_patterns = strv_fnmatch(patterns, u->id, 0);
1065
1066 if (!strv_isempty(from_patterns) &&
1067 !match_patterns &&
1068 !strv_fnmatch(from_patterns, u->id, 0))
1069 return 0;
1070
1071 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
1072 if (r < 0)
1073 return r;
1074
1075 STRV_FOREACH(unit, units) {
1076 bool match_patterns2;
1077
1078 match_patterns2 = strv_fnmatch(patterns, *unit, 0);
1079
1080 if (!strv_isempty(to_patterns) &&
1081 !match_patterns2 &&
1082 !strv_fnmatch(to_patterns, *unit, 0))
1083 continue;
1084
1085 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
1086 continue;
1087
1088 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1089 }
1090
1091 return 0;
1092 }
1093
1094 static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
1095 int r;
1096
1097 assert(bus);
1098 assert(u);
1099
1100 if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
1101 r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
1102 if (r < 0)
1103 return r;
1104 }
1105
1106 if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
1107 r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
1108 if (r < 0)
1109 return r;
1110 r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
1111 if (r < 0)
1112 return r;
1113 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
1114 if (r < 0)
1115 return r;
1116 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
1117 if (r < 0)
1118 return r;
1119 }
1120
1121 return 0;
1122 }
1123
1124 static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1125 _cleanup_strv_free_ char **expanded_patterns = NULL;
1126 char **pattern;
1127 int r;
1128
1129 STRV_FOREACH(pattern, patterns) {
1130 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1131 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
1132
1133 if (strv_extend(&expanded_patterns, *pattern) < 0)
1134 return log_oom();
1135
1136 if (string_is_glob(*pattern))
1137 continue;
1138
1139 unit = unit_dbus_path_from_name(*pattern);
1140 if (!unit)
1141 return log_oom();
1142
1143 r = sd_bus_get_property_string(
1144 bus,
1145 "org.freedesktop.systemd1",
1146 unit,
1147 "org.freedesktop.systemd1.Unit",
1148 "Id",
1149 &error,
1150 &unit_id);
1151 if (r < 0)
1152 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1153
1154 if (!streq(*pattern, unit_id)) {
1155 if (strv_extend(&expanded_patterns, unit_id) < 0)
1156 return log_oom();
1157 }
1158 }
1159
1160 *ret = expanded_patterns;
1161 expanded_patterns = NULL; /* do not free */
1162
1163 return 0;
1164 }
1165
1166 static int dot(sd_bus *bus, char* patterns[]) {
1167 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1168 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1169 _cleanup_strv_free_ char **expanded_patterns = NULL;
1170 _cleanup_strv_free_ char **expanded_from_patterns = NULL;
1171 _cleanup_strv_free_ char **expanded_to_patterns = NULL;
1172 int r;
1173 UnitInfo u;
1174
1175 r = expand_patterns(bus, patterns, &expanded_patterns);
1176 if (r < 0)
1177 return r;
1178
1179 r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1180 if (r < 0)
1181 return r;
1182
1183 r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1184 if (r < 0)
1185 return r;
1186
1187 r = sd_bus_call_method(
1188 bus,
1189 "org.freedesktop.systemd1",
1190 "/org/freedesktop/systemd1",
1191 "org.freedesktop.systemd1.Manager",
1192 "ListUnits",
1193 &error,
1194 &reply,
1195 "");
1196 if (r < 0) {
1197 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1198 return r;
1199 }
1200
1201 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1202 if (r < 0)
1203 return bus_log_parse_error(r);
1204
1205 printf("digraph systemd {\n");
1206
1207 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1208
1209 r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1210 if (r < 0)
1211 return r;
1212 }
1213 if (r < 0)
1214 return bus_log_parse_error(r);
1215
1216 printf("}\n");
1217
1218 log_info(" Color legend: black = Requires\n"
1219 " dark blue = Requisite\n"
1220 " dark grey = Wants\n"
1221 " red = Conflicts\n"
1222 " green = After\n");
1223
1224 if (on_tty())
1225 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1226 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1227
1228 return 0;
1229 }
1230
1231 static int dump(sd_bus *bus, char **args) {
1232 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1233 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1234 const char *text = NULL;
1235 int r;
1236
1237 if (!strv_isempty(args)) {
1238 log_error("Too many arguments.");
1239 return -E2BIG;
1240 }
1241
1242 pager_open(arg_no_pager, false);
1243
1244 r = sd_bus_call_method(
1245 bus,
1246 "org.freedesktop.systemd1",
1247 "/org/freedesktop/systemd1",
1248 "org.freedesktop.systemd1.Manager",
1249 "Dump",
1250 &error,
1251 &reply,
1252 "");
1253 if (r < 0)
1254 return log_error_errno(r, "Failed issue method call: %s", bus_error_message(&error, r));
1255
1256 r = sd_bus_message_read(reply, "s", &text);
1257 if (r < 0)
1258 return bus_log_parse_error(r);
1259
1260 fputs(text, stdout);
1261 return 0;
1262 }
1263
1264 static int set_log_level(sd_bus *bus, char **args) {
1265 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1266 int r;
1267
1268 assert(bus);
1269 assert(args);
1270
1271 if (strv_length(args) != 1) {
1272 log_error("This command expects one argument only.");
1273 return -E2BIG;
1274 }
1275
1276 r = sd_bus_set_property(
1277 bus,
1278 "org.freedesktop.systemd1",
1279 "/org/freedesktop/systemd1",
1280 "org.freedesktop.systemd1.Manager",
1281 "LogLevel",
1282 &error,
1283 "s",
1284 args[0]);
1285 if (r < 0)
1286 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1287
1288 return 0;
1289 }
1290
1291 static int get_log_level(sd_bus *bus, char **args) {
1292 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1293 int r;
1294 _cleanup_free_ char *level = NULL;
1295
1296 assert(bus);
1297 assert(args);
1298
1299 if (!strv_isempty(args)) {
1300 log_error("Too many arguments.");
1301 return -E2BIG;
1302 }
1303
1304 r = sd_bus_get_property_string(
1305 bus,
1306 "org.freedesktop.systemd1",
1307 "/org/freedesktop/systemd1",
1308 "org.freedesktop.systemd1.Manager",
1309 "LogLevel",
1310 &error,
1311 &level);
1312 if (r < 0)
1313 return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
1314
1315 puts(level);
1316 return 0;
1317 }
1318
1319 static int set_log_target(sd_bus *bus, char **args) {
1320 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1321 int r;
1322
1323 assert(bus);
1324 assert(args);
1325
1326 if (strv_length(args) != 1) {
1327 log_error("This command expects one argument only.");
1328 return -E2BIG;
1329 }
1330
1331 r = sd_bus_set_property(
1332 bus,
1333 "org.freedesktop.systemd1",
1334 "/org/freedesktop/systemd1",
1335 "org.freedesktop.systemd1.Manager",
1336 "LogTarget",
1337 &error,
1338 "s",
1339 args[0]);
1340 if (r < 0)
1341 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1342
1343 return 0;
1344 }
1345
1346 static int get_log_target(sd_bus *bus, char **args) {
1347 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1348 int r;
1349 _cleanup_free_ char *target = NULL;
1350
1351 assert(bus);
1352 assert(args);
1353
1354 if (!strv_isempty(args)) {
1355 log_error("Too many arguments.");
1356 return -E2BIG;
1357 }
1358
1359 r = sd_bus_get_property_string(
1360 bus,
1361 "org.freedesktop.systemd1",
1362 "/org/freedesktop/systemd1",
1363 "org.freedesktop.systemd1.Manager",
1364 "LogTarget",
1365 &error,
1366 &target);
1367 if (r < 0)
1368 return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
1369
1370 puts(target);
1371 return 0;
1372 }
1373
1374 #if HAVE_SECCOMP
1375 static void dump_syscall_filter(const SyscallFilterSet *set) {
1376 const char *syscall;
1377
1378 printf("%s\n", set->name);
1379 printf(" # %s\n", set->help);
1380 NULSTR_FOREACH(syscall, set->value)
1381 printf(" %s\n", syscall);
1382 }
1383
1384 static int dump_syscall_filters(char** names) {
1385 bool first = true;
1386
1387 pager_open(arg_no_pager, false);
1388
1389 if (strv_isempty(names)) {
1390 int i;
1391
1392 for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
1393 if (!first)
1394 puts("");
1395 dump_syscall_filter(syscall_filter_sets + i);
1396 first = false;
1397 }
1398 } else {
1399 char **name;
1400
1401 STRV_FOREACH(name, names) {
1402 const SyscallFilterSet *set;
1403
1404 if (!first)
1405 puts("");
1406
1407 set = syscall_filter_set_find(*name);
1408 if (!set) {
1409 /* make sure the error appears below normal output */
1410 fflush(stdout);
1411
1412 log_error("Filter set \"%s\" not found.", *name);
1413 return -ENOENT;
1414 }
1415
1416 dump_syscall_filter(set);
1417 first = false;
1418 }
1419 }
1420
1421 return 0;
1422 }
1423
1424 #else
1425 static int dump_syscall_filters(char** names) {
1426 log_error("Not compiled with syscall filters, sorry.");
1427 return -EOPNOTSUPP;
1428 }
1429 #endif
1430
1431 static int test_calendar(char **args) {
1432 int ret = 0, r;
1433 char **p;
1434 usec_t n;
1435
1436 if (strv_isempty(args)) {
1437 log_error("Expected at least one calendar specification string as argument.");
1438 return -EINVAL;
1439 }
1440
1441 n = now(CLOCK_REALTIME);
1442
1443 STRV_FOREACH(p, args) {
1444 _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
1445 _cleanup_free_ char *t = NULL;
1446 usec_t next;
1447
1448 r = calendar_spec_from_string(*p, &spec);
1449 if (r < 0) {
1450 ret = log_error_errno(r, "Failed to parse calendar specification '%s': %m", *p);
1451 continue;
1452 }
1453
1454 r = calendar_spec_normalize(spec);
1455 if (r < 0) {
1456 ret = log_error_errno(r, "Failed to normalize calendar specification '%s': %m", *p);
1457 continue;
1458 }
1459
1460 r = calendar_spec_to_string(spec, &t);
1461 if (r < 0) {
1462 ret = log_error_errno(r, "Failed to fomat calendar specification '%s': %m", *p);
1463 continue;
1464 }
1465
1466 if (!streq(t, *p))
1467 printf(" Original form: %s\n", *p);
1468
1469 printf("Normalized form: %s\n", t);
1470
1471 r = calendar_spec_next_usec(spec, n, &next);
1472 if (r == -ENOENT)
1473 printf(" Next elapse: never\n");
1474 else if (r < 0) {
1475 ret = log_error_errno(r, "Failed to determine next elapse for '%s': %m", *p);
1476 continue;
1477 } else {
1478 char buffer[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESTAMP_RELATIVE_MAX)];
1479
1480 printf(" Next elapse: %s\n", format_timestamp(buffer, sizeof(buffer), next));
1481
1482 if (!in_utc_timezone())
1483 printf(" (in UTC): %s\n", format_timestamp_utc(buffer, sizeof(buffer), next));
1484
1485 printf(" From now: %s\n", format_timestamp_relative(buffer, sizeof(buffer), next));
1486 }
1487
1488 if (*(p+1))
1489 putchar('\n');
1490 }
1491
1492 return ret;
1493 }
1494
1495 static void help(void) {
1496
1497 pager_open(arg_no_pager, false);
1498
1499 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1500 "Profile systemd, show unit dependencies, check unit files.\n\n"
1501 " -h --help Show this help\n"
1502 " --version Show package version\n"
1503 " --no-pager Do not pipe output into a pager\n"
1504 " --system Operate on system systemd instance\n"
1505 " --user Operate on user systemd instance\n"
1506 " -H --host=[USER@]HOST Operate on remote host\n"
1507 " -M --machine=CONTAINER Operate on local container\n"
1508 " --order Show only order in the graph\n"
1509 " --require Show only requirement in the graph\n"
1510 " --from-pattern=GLOB Show only origins in the graph\n"
1511 " --to-pattern=GLOB Show only destinations in the graph\n"
1512 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1513 " earlier than the latest in the branch\n"
1514 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1515 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
1516 "Commands:\n"
1517 " time Print time spent in the kernel\n"
1518 " blame Print list of running units ordered by time to init\n"
1519 " critical-chain Print a tree of the time critical chain of units\n"
1520 " plot Output SVG graphic showing service initialization\n"
1521 " dot Output dependency graph in man:dot(1) format\n"
1522 " set-log-level LEVEL Set logging threshold for manager\n"
1523 " set-log-target TARGET Set logging target for manager\n"
1524 " get-log-level Get logging threshold for manager\n"
1525 " get-log-target Get logging target for manager\n"
1526 " dump Output state serialization of service manager\n"
1527 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1528 " verify FILE... Check unit files for correctness\n"
1529 " calendar SPEC... Validate repetitive calendar time events\n"
1530 , program_invocation_short_name);
1531
1532 /* When updating this list, including descriptions, apply
1533 * changes to shell-completion/bash/systemd-analyze and
1534 * shell-completion/zsh/_systemd-analyze too. */
1535 }
1536
1537 static int parse_argv(int argc, char *argv[]) {
1538 enum {
1539 ARG_VERSION = 0x100,
1540 ARG_ORDER,
1541 ARG_REQUIRE,
1542 ARG_USER,
1543 ARG_SYSTEM,
1544 ARG_DOT_FROM_PATTERN,
1545 ARG_DOT_TO_PATTERN,
1546 ARG_FUZZ,
1547 ARG_NO_PAGER,
1548 ARG_MAN,
1549 ARG_GENERATORS,
1550 };
1551
1552 static const struct option options[] = {
1553 { "help", no_argument, NULL, 'h' },
1554 { "version", no_argument, NULL, ARG_VERSION },
1555 { "order", no_argument, NULL, ARG_ORDER },
1556 { "require", no_argument, NULL, ARG_REQUIRE },
1557 { "user", no_argument, NULL, ARG_USER },
1558 { "system", no_argument, NULL, ARG_SYSTEM },
1559 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1560 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1561 { "fuzz", required_argument, NULL, ARG_FUZZ },
1562 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1563 { "man", optional_argument, NULL, ARG_MAN },
1564 { "generators", optional_argument, NULL, ARG_GENERATORS },
1565 { "host", required_argument, NULL, 'H' },
1566 { "machine", required_argument, NULL, 'M' },
1567 {}
1568 };
1569
1570 int r, c;
1571
1572 assert(argc >= 0);
1573 assert(argv);
1574
1575 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1576 switch (c) {
1577
1578 case 'h':
1579 help();
1580 return 0;
1581
1582 case ARG_VERSION:
1583 return version();
1584
1585 case ARG_USER:
1586 arg_user = true;
1587 break;
1588
1589 case ARG_SYSTEM:
1590 arg_user = false;
1591 break;
1592
1593 case ARG_ORDER:
1594 arg_dot = DEP_ORDER;
1595 break;
1596
1597 case ARG_REQUIRE:
1598 arg_dot = DEP_REQUIRE;
1599 break;
1600
1601 case ARG_DOT_FROM_PATTERN:
1602 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1603 return log_oom();
1604
1605 break;
1606
1607 case ARG_DOT_TO_PATTERN:
1608 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1609 return log_oom();
1610
1611 break;
1612
1613 case ARG_FUZZ:
1614 r = parse_sec(optarg, &arg_fuzz);
1615 if (r < 0)
1616 return r;
1617 break;
1618
1619 case ARG_NO_PAGER:
1620 arg_no_pager = true;
1621 break;
1622
1623 case 'H':
1624 arg_transport = BUS_TRANSPORT_REMOTE;
1625 arg_host = optarg;
1626 break;
1627
1628 case 'M':
1629 arg_transport = BUS_TRANSPORT_MACHINE;
1630 arg_host = optarg;
1631 break;
1632
1633 case ARG_MAN:
1634 if (optarg) {
1635 r = parse_boolean(optarg);
1636 if (r < 0) {
1637 log_error("Failed to parse --man= argument.");
1638 return -EINVAL;
1639 }
1640
1641 arg_man = !!r;
1642 } else
1643 arg_man = true;
1644
1645 break;
1646
1647 case ARG_GENERATORS:
1648 if (optarg) {
1649 r = parse_boolean(optarg);
1650 if (r < 0) {
1651 log_error("Failed to parse --generators= argument.");
1652 return -EINVAL;
1653 }
1654
1655 arg_generators = !!r;
1656 } else
1657 arg_generators = true;
1658
1659 break;
1660
1661 case '?':
1662 return -EINVAL;
1663
1664 default:
1665 assert_not_reached("Unhandled option code.");
1666 }
1667
1668 return 1; /* work to do */
1669 }
1670
1671 int main(int argc, char *argv[]) {
1672 int r;
1673
1674 setlocale(LC_ALL, "");
1675 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1676 log_parse_environment();
1677 log_open();
1678
1679 r = parse_argv(argc, argv);
1680 if (r <= 0)
1681 goto finish;
1682
1683 if (streq_ptr(argv[optind], "verify"))
1684 r = verify_units(argv+optind+1,
1685 arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM,
1686 arg_man,
1687 arg_generators);
1688 else {
1689 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1690
1691 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1692 if (r < 0) {
1693 log_error_errno(r, "Failed to create bus connection: %m");
1694 goto finish;
1695 }
1696
1697 if (!argv[optind] || streq(argv[optind], "time"))
1698 r = analyze_time(bus);
1699 else if (streq(argv[optind], "blame"))
1700 r = analyze_blame(bus);
1701 else if (streq(argv[optind], "critical-chain"))
1702 r = analyze_critical_chain(bus, argv+optind+1);
1703 else if (streq(argv[optind], "plot"))
1704 r = analyze_plot(bus);
1705 else if (streq(argv[optind], "dot"))
1706 r = dot(bus, argv+optind+1);
1707 else if (streq(argv[optind], "dump"))
1708 r = dump(bus, argv+optind+1);
1709 else if (streq(argv[optind], "set-log-level"))
1710 r = set_log_level(bus, argv+optind+1);
1711 else if (streq(argv[optind], "get-log-level"))
1712 r = get_log_level(bus, argv+optind+1);
1713 else if (streq(argv[optind], "set-log-target"))
1714 r = set_log_target(bus, argv+optind+1);
1715 else if (streq(argv[optind], "get-log-target"))
1716 r = get_log_target(bus, argv+optind+1);
1717 else if (streq(argv[optind], "syscall-filter"))
1718 r = dump_syscall_filters(argv+optind+1);
1719 else if (streq(argv[optind], "calendar"))
1720 r = test_calendar(argv+optind+1);
1721 else
1722 log_error("Unknown operation '%s'.", argv[optind]);
1723 }
1724
1725 finish:
1726 pager_close();
1727
1728 strv_free(arg_dot_from_patterns);
1729 strv_free(arg_dot_to_patterns);
1730
1731 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1732 }