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