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