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