]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/analyze.c
Merge pull request #1190 from poettering/rework-virt
[thirdparty/systemd.git] / src / analyze / analyze.c
CommitLineData
2265fbf7
SP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
ffaa0e25
LP
6 Copyright 2010-2013 Lennart Poettering
7 Copyright 2013 Simon Peeters
2265fbf7
SP
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21***/
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <getopt.h>
26#include <locale.h>
2265fbf7 27
048ecf5b
TA
28#include "sd-bus.h"
29#include "bus-util.h"
30#include "bus-error.h"
2265fbf7 31#include "log.h"
2265fbf7
SP
32#include "build.h"
33#include "util.h"
34#include "strxcpyx.h"
e55933db 35#include "strv.h"
bb150966
HH
36#include "unit-name.h"
37#include "special.h"
38#include "hashmap.h"
9ea9d4cf 39#include "pager.h"
1d3bc017 40#include "analyze-verify.h"
288a74cc 41#include "terminal-util.h"
2265fbf7 42
2f6eb835 43#define SCALE_X (0.1 / 1000.0) /* pixels per us */
a213b7e9 44#define SCALE_Y (20.0)
2f6eb835 45
2265fbf7 46#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
c170f3a4 47
2265fbf7 48#define svg(...) printf(__VA_ARGS__)
c170f3a4
LP
49
50#define svg_bar(class, x1, x2, y) \
2265fbf7 51 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
c170f3a4 52 (class), \
2f6eb835
LP
53 SCALE_X * (x1), SCALE_Y * (y), \
54 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
c170f3a4
LP
55
56#define svg_text(b, x, y, format, ...) \
57 do { \
2f6eb835 58 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
c170f3a4
LP
59 svg(format, ## __VA_ARGS__); \
60 svg("</text>\n"); \
2265fbf7
SP
61 } while(false)
62
1700761b
SP
63static enum dot {
64 DEP_ALL,
65 DEP_ORDER,
66 DEP_REQUIRE
67} arg_dot = DEP_ALL;
e55933db
ŁS
68static char** arg_dot_from_patterns = NULL;
69static char** arg_dot_to_patterns = NULL;
9ea9d4cf
LP
70static usec_t arg_fuzz = 0;
71static bool arg_no_pager = false;
3cd26e7c
LP
72static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
73static char *arg_host = NULL;
74static bool arg_user = false;
1d3bc017 75static bool arg_man = true;
bb150966 76
2265fbf7 77struct boot_times {
c170f3a4
LP
78 usec_t firmware_time;
79 usec_t loader_time;
80 usec_t kernel_time;
81 usec_t kernel_done_time;
82 usec_t initrd_time;
83 usec_t userspace_time;
84 usec_t finish_time;
c2e0d600
TA
85 usec_t security_start_time;
86 usec_t security_finish_time;
518d10e9
UTL
87 usec_t generators_start_time;
88 usec_t generators_finish_time;
d9acfb71
TA
89 usec_t unitsload_start_time;
90 usec_t unitsload_finish_time;
06bef033
IS
91
92 /*
93 * If we're analyzing the user instance, all timestamps will be offset
94 * by its own start-up timestamp, which may be arbitrarily big.
95 * With "plot", this causes arbitrarily wide output SVG files which almost
96 * completely consist of empty space. Thus we cancel out this offset.
97 *
98 * This offset is subtracted from times above by acquire_boot_times(),
99 * but it still needs to be subtracted from unit-specific timestamps
100 * (so it is stored here for reference).
101 */
102 usec_t reverse_offset;
2265fbf7 103};
9ea9d4cf 104
2265fbf7
SP
105struct unit_times {
106 char *name;
cc27380c
TA
107 usec_t activating;
108 usec_t activated;
109 usec_t deactivated;
110 usec_t deactivating;
c170f3a4 111 usec_t time;
2265fbf7
SP
112};
113
7e690cef
DH
114struct host_info {
115 char *hostname;
116 char *kernel_name;
117 char *kernel_release;
118 char *kernel_version;
119 char *os_pretty_name;
120 char *virtualization;
121 char *architecture;
122};
123
9ea9d4cf
LP
124static void pager_open_if_enabled(void) {
125
126 if (arg_no_pager)
127 return;
128
129 pager_open(false);
130}
131
048ecf5b 132static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
048ecf5b 133 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
c170f3a4 134 int r;
2265fbf7 135
988b9df2
LP
136 assert(bus);
137 assert(path);
138 assert(interface);
139 assert(property);
140 assert(val);
141
a936124f
TA
142 r = sd_bus_get_property_trivial(
143 bus,
144 "org.freedesktop.systemd1",
145 path,
146 interface,
147 property,
148 &error,
149 't', val);
048ecf5b
TA
150
151 if (r < 0) {
152 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
2265fbf7 153 return r;
2265fbf7
SP
154 }
155
2265fbf7
SP
156 return 0;
157}
158
988b9df2
LP
159static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
160 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
161 int r;
162
163 assert(bus);
164 assert(path);
165 assert(property);
166 assert(strv);
167
168 r = sd_bus_get_property_strv(
169 bus,
170 "org.freedesktop.systemd1",
171 path,
172 "org.freedesktop.systemd1.Unit",
173 property,
174 &error,
175 strv);
176 if (r < 0) {
177 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
178 return r;
179 }
180
181 return 0;
182}
183
c170f3a4 184static int compare_unit_time(const void *a, const void *b) {
2265fbf7
SP
185 return compare(((struct unit_times *)b)->time,
186 ((struct unit_times *)a)->time);
187}
188
c170f3a4 189static int compare_unit_start(const void *a, const void *b) {
cc27380c
TA
190 return compare(((struct unit_times *)a)->activating,
191 ((struct unit_times *)b)->activating);
2265fbf7
SP
192}
193
c170f3a4
LP
194static void free_unit_times(struct unit_times *t, unsigned n) {
195 struct unit_times *p;
196
197 for (p = t; p < t + n; p++)
198 free(p->name);
199
200 free(t);
201}
202
06bef033
IS
203static void subtract_timestamp(usec_t *a, usec_t b) {
204 assert(a);
205
206 if (*a > 0) {
207 assert(*a >= b);
208 *a -= b;
209 }
210}
211
048ecf5b 212static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
2265fbf7
SP
213 static struct boot_times times;
214 static bool cached = false;
c170f3a4 215
2265fbf7 216 if (cached)
c170f3a4
LP
217 goto finish;
218
219 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
2265fbf7
SP
220
221 if (bus_get_uint64_property(bus,
222 "/org/freedesktop/systemd1",
223 "org.freedesktop.systemd1.Manager",
224 "FirmwareTimestampMonotonic",
225 &times.firmware_time) < 0 ||
226 bus_get_uint64_property(bus,
227 "/org/freedesktop/systemd1",
228 "org.freedesktop.systemd1.Manager",
229 "LoaderTimestampMonotonic",
230 &times.loader_time) < 0 ||
231 bus_get_uint64_property(bus,
232 "/org/freedesktop/systemd1",
233 "org.freedesktop.systemd1.Manager",
234 "KernelTimestamp",
235 &times.kernel_time) < 0 ||
236 bus_get_uint64_property(bus,
237 "/org/freedesktop/systemd1",
238 "org.freedesktop.systemd1.Manager",
239 "InitRDTimestampMonotonic",
240 &times.initrd_time) < 0 ||
241 bus_get_uint64_property(bus,
242 "/org/freedesktop/systemd1",
243 "org.freedesktop.systemd1.Manager",
244 "UserspaceTimestampMonotonic",
245 &times.userspace_time) < 0 ||
246 bus_get_uint64_property(bus,
247 "/org/freedesktop/systemd1",
248 "org.freedesktop.systemd1.Manager",
249 "FinishTimestampMonotonic",
518d10e9 250 &times.finish_time) < 0 ||
c2e0d600
TA
251 bus_get_uint64_property(bus,
252 "/org/freedesktop/systemd1",
253 "org.freedesktop.systemd1.Manager",
254 "SecurityStartTimestampMonotonic",
255 &times.security_start_time) < 0 ||
256 bus_get_uint64_property(bus,
257 "/org/freedesktop/systemd1",
258 "org.freedesktop.systemd1.Manager",
259 "SecurityFinishTimestampMonotonic",
260 &times.security_finish_time) < 0 ||
518d10e9
UTL
261 bus_get_uint64_property(bus,
262 "/org/freedesktop/systemd1",
263 "org.freedesktop.systemd1.Manager",
264 "GeneratorsStartTimestampMonotonic",
265 &times.generators_start_time) < 0 ||
266 bus_get_uint64_property(bus,
267 "/org/freedesktop/systemd1",
268 "org.freedesktop.systemd1.Manager",
269 "GeneratorsFinishTimestampMonotonic",
d9acfb71
TA
270 &times.generators_finish_time) < 0 ||
271 bus_get_uint64_property(bus,
272 "/org/freedesktop/systemd1",
273 "org.freedesktop.systemd1.Manager",
274 "UnitsLoadStartTimestampMonotonic",
275 &times.unitsload_start_time) < 0 ||
276 bus_get_uint64_property(bus,
277 "/org/freedesktop/systemd1",
278 "org.freedesktop.systemd1.Manager",
279 "UnitsLoadFinishTimestampMonotonic",
280 &times.unitsload_finish_time) < 0)
c170f3a4 281 return -EIO;
2265fbf7 282
c170f3a4 283 if (times.finish_time <= 0) {
2265fbf7 284 log_error("Bootup is not yet finished. Please try again later.");
988b9df2 285 return -EINPROGRESS;
2265fbf7
SP
286 }
287
06bef033
IS
288 if (arg_user) {
289 /*
290 * User-instance-specific timestamps processing
291 * (see comment to reverse_offset in struct boot_times).
292 */
293 times.reverse_offset = times.userspace_time;
294
295 times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
296 subtract_timestamp(&times.finish_time, times.reverse_offset);
297
298 subtract_timestamp(&times.security_start_time, times.reverse_offset);
299 subtract_timestamp(&times.security_finish_time, times.reverse_offset);
300
301 subtract_timestamp(&times.generators_start_time, times.reverse_offset);
302 subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
303
304 subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
305 subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
306 } else {
307 if (times.initrd_time)
308 times.kernel_done_time = times.initrd_time;
309 else
310 times.kernel_done_time = times.userspace_time;
311 }
2265fbf7
SP
312
313 cached = true;
c170f3a4
LP
314
315finish:
316 *bt = &times;
317 return 0;
2265fbf7
SP
318}
319
7e690cef
DH
320static void free_host_info(struct host_info *hi) {
321 free(hi->hostname);
322 free(hi->kernel_name);
323 free(hi->kernel_release);
324 free(hi->kernel_version);
325 free(hi->os_pretty_name);
326 free(hi->virtualization);
327 free(hi->architecture);
328 free(hi);
329}
330
29b8b5ce
IS
331static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
332 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
333 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
334 int r, c = 0;
06bef033 335 struct boot_times *boot_times = NULL;
29b8b5ce
IS
336 struct unit_times *unit_times = NULL;
337 size_t size = 0;
338 UnitInfo u;
339
06bef033
IS
340 r = acquire_boot_times(bus, &boot_times);
341 if (r < 0)
342 goto fail;
343
29b8b5ce
IS
344 r = sd_bus_call_method(
345 bus,
346 "org.freedesktop.systemd1",
347 "/org/freedesktop/systemd1",
348 "org.freedesktop.systemd1.Manager",
349 "ListUnits",
350 &error, &reply,
351 NULL);
352 if (r < 0) {
353 log_error("Failed to list units: %s", bus_error_message(&error, -r));
354 goto fail;
355 }
356
357 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
358 if (r < 0) {
359 bus_log_parse_error(r);
360 goto fail;
361 }
362
363 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
364 struct unit_times *t;
365
366 if (!GREEDY_REALLOC(unit_times, size, c+1)) {
367 r = log_oom();
368 goto fail;
369 }
370
371 t = unit_times+c;
372 t->name = NULL;
373
374 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
375
376 if (bus_get_uint64_property(bus, u.unit_path,
377 "org.freedesktop.systemd1.Unit",
378 "InactiveExitTimestampMonotonic",
379 &t->activating) < 0 ||
380 bus_get_uint64_property(bus, u.unit_path,
381 "org.freedesktop.systemd1.Unit",
382 "ActiveEnterTimestampMonotonic",
383 &t->activated) < 0 ||
384 bus_get_uint64_property(bus, u.unit_path,
385 "org.freedesktop.systemd1.Unit",
386 "ActiveExitTimestampMonotonic",
387 &t->deactivating) < 0 ||
388 bus_get_uint64_property(bus, u.unit_path,
389 "org.freedesktop.systemd1.Unit",
390 "InactiveEnterTimestampMonotonic",
391 &t->deactivated) < 0) {
392 r = -EIO;
393 goto fail;
394 }
395
06bef033
IS
396 subtract_timestamp(&t->activating, boot_times->reverse_offset);
397 subtract_timestamp(&t->activated, boot_times->reverse_offset);
398 subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
399 subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
400
29b8b5ce
IS
401 if (t->activated >= t->activating)
402 t->time = t->activated - t->activating;
403 else if (t->deactivated >= t->activating)
404 t->time = t->deactivated - t->activating;
405 else
406 t->time = 0;
407
408 if (t->activating == 0)
409 continue;
410
411 t->name = strdup(u.id);
412 if (t->name == NULL) {
413 r = log_oom();
414 goto fail;
415 }
416 c++;
417 }
418 if (r < 0) {
419 bus_log_parse_error(r);
420 goto fail;
421 }
422
423 *out = unit_times;
424 return c;
425
426fail:
427 if (unit_times)
428 free_unit_times(unit_times, (unsigned) c);
429 return r;
430}
431
7e690cef
DH
432static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
433 int r;
434 struct host_info *host;
435
436 static const struct bus_properties_map hostname_map[] = {
437 { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
438 { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
439 { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
440 { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
441 { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
442 {}
443 };
444
445 static const struct bus_properties_map manager_map[] = {
446 { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
447 { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
448 {}
449 };
450
451 host = new0(struct host_info, 1);
452 if (!host)
453 return log_oom();
454
455 r = bus_map_all_properties(bus,
456 "org.freedesktop.hostname1",
457 "/org/freedesktop/hostname1",
458 hostname_map,
459 host);
460 if (r < 0)
461 goto fail;
462
463 r = bus_map_all_properties(bus,
464 "org.freedesktop.systemd1",
465 "/org/freedesktop/systemd1",
466 manager_map,
467 host);
468 if (r < 0)
469 goto fail;
470
471 *hi = host;
472 return 0;
473fail:
474 free_host_info(host);
475 return r;
476}
477
048ecf5b 478static int pretty_boot_time(sd_bus *bus, char **_buf) {
c170f3a4 479 char ts[FORMAT_TIMESPAN_MAX];
2265fbf7 480 struct boot_times *t;
2265fbf7 481 static char buf[4096];
c170f3a4
LP
482 size_t size;
483 char *ptr;
484 int r;
485
486 r = acquire_boot_times(bus, &t);
487 if (r < 0)
488 return r;
2265fbf7 489
c170f3a4
LP
490 ptr = buf;
491 size = sizeof(buf);
2265fbf7
SP
492
493 size = strpcpyf(&ptr, size, "Startup finished in ");
494 if (t->firmware_time)
2fa4092c 495 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
2265fbf7 496 if (t->loader_time)
2fa4092c 497 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
2265fbf7 498 if (t->kernel_time)
2fa4092c 499 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
2265fbf7 500 if (t->initrd_time > 0)
2fa4092c 501 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
2265fbf7 502
2fa4092c 503 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
06bef033 504 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
2265fbf7 505
c170f3a4
LP
506 ptr = strdup(buf);
507 if (!ptr)
508 return log_oom();
509
510 *_buf = ptr;
511 return 0;
2265fbf7
SP
512}
513
c170f3a4
LP
514static void svg_graph_box(double height, double begin, double end) {
515 long long i;
516
2265fbf7
SP
517 /* outside box, fill */
518 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
2f6eb835 519 SCALE_X * (end - begin), SCALE_Y * height);
2265fbf7 520
c170f3a4 521 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
2265fbf7 522 /* lines for each second */
c170f3a4 523 if (i % 5000000 == 0)
2265fbf7
SP
524 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
525 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
2f6eb835 526 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
c170f3a4 527 else if (i % 1000000 == 0)
2265fbf7
SP
528 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
529 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
2f6eb835 530 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
2265fbf7
SP
531 else
532 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
2f6eb835 533 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
2265fbf7
SP
534 }
535}
536
048ecf5b 537static int analyze_plot(sd_bus *bus) {
2265fbf7
SP
538 struct unit_times *times;
539 struct boot_times *boot;
7e690cef 540 struct host_info *host = NULL;
2265fbf7
SP
541 int n, m = 1, y=0;
542 double width;
7e690cef 543 _cleanup_free_ char *pretty_times = NULL;
c170f3a4 544 struct unit_times *u;
2265fbf7 545
c170f3a4
LP
546 n = acquire_boot_times(bus, &boot);
547 if (n < 0)
548 return n;
2265fbf7 549
c170f3a4
LP
550 n = pretty_boot_time(bus, &pretty_times);
551 if (n < 0)
552 return n;
2265fbf7 553
7e690cef
DH
554 n = acquire_host_info(bus, &host);
555 if (n < 0)
556 return n;
2265fbf7
SP
557
558 n = acquire_time_data(bus, &times);
c170f3a4 559 if (n <= 0)
7e690cef 560 goto out;
2265fbf7
SP
561
562 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
563
2f6eb835 564 width = SCALE_X * (boot->firmware_time + boot->finish_time);
2265fbf7
SP
565 if (width < 800.0)
566 width = 800.0;
567
568 if (boot->firmware_time > boot->loader_time)
569 m++;
570 if (boot->loader_time) {
571 m++;
572 if (width < 1000.0)
573 width = 1000.0;
574 }
575 if (boot->initrd_time)
576 m++;
577 if (boot->kernel_time)
578 m++;
579
c170f3a4 580 for (u = times; u < times + n; u++) {
95168f7d 581 double text_start, text_width;
c170f3a4 582
cc27380c
TA
583 if (u->activating < boot->userspace_time ||
584 u->activating > boot->finish_time) {
2265fbf7
SP
585 free(u->name);
586 u->name = NULL;
587 continue;
588 }
95168f7d
TA
589
590 /* If the text cannot fit on the left side then
591 * increase the svg width so it fits on the right.
592 * TODO: calculate the text width more accurately */
593 text_width = 8.0 * strlen(u->name);
cc27380c 594 text_start = (boot->firmware_time + u->activating) * SCALE_X;
95168f7d
TA
595 if (text_width > text_start && text_width + text_start > width)
596 width = text_width + text_start;
2265fbf7 597
cc27380c
TA
598 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
599 && u->activated == 0 && u->deactivating == 0)
600 u->activated = u->deactivating = u->deactivated;
601 if (u->activated < u->activating || u->activated > boot->finish_time)
602 u->activated = boot->finish_time;
603 if (u->deactivating < u->activated || u->activated > boot->finish_time)
604 u->deactivating = boot->finish_time;
605 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
606 u->deactivated = boot->finish_time;
2265fbf7
SP
607 m++;
608 }
609
610 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
611 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
612 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
613
614 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
615 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
518d10e9 616 80.0 + width, 150.0 + (m * SCALE_Y) +
d9acfb71 617 5 * SCALE_Y /* legend */);
2265fbf7
SP
618
619 /* write some basic info as a comment, including some help */
620 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
621 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
622 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
623 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
624 "<!-- point your browser to this file. -->\n\n"
625 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
626
627 /* style sheet */
628 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
629 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
418e3750 630 " rect.background { fill: rgb(255,255,255); }\n"
2265fbf7
SP
631 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
632 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
633 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
634 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
635 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
636 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
637 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
638 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
c2e0d600 639 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
518d10e9 640 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
d9acfb71 641 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
2265fbf7
SP
642 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
643 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
644 "// line.sec1 { }\n"
645 " line.sec5 { stroke-width: 2; }\n"
646 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
2b7d6965
TA
647 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
648 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
649 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
650 " text.sec { font-size: 10px; }\n"
2265fbf7
SP
651 " ]]>\n </style>\n</defs>\n\n");
652
418e3750 653 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
2265fbf7 654 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
7e690cef
DH
655 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
656 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
657 isempty(host->hostname) ? "" : host->hostname,
658 isempty(host->kernel_name) ? "" : host->kernel_name,
659 isempty(host->kernel_release) ? "" : host->kernel_release,
660 isempty(host->kernel_version) ? "" : host->kernel_version,
661 isempty(host->architecture) ? "" : host->architecture,
662 isempty(host->virtualization) ? "" : host->virtualization);
2265fbf7 663
2f6eb835 664 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
b5cfa740 665 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
2265fbf7
SP
666
667 if (boot->firmware_time) {
c170f3a4
LP
668 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
669 svg_text(true, -(double) boot->firmware_time, y, "firmware");
2265fbf7
SP
670 y++;
671 }
672 if (boot->loader_time) {
c170f3a4
LP
673 svg_bar("loader", -(double) boot->loader_time, 0, y);
674 svg_text(true, -(double) boot->loader_time, y, "loader");
2265fbf7
SP
675 y++;
676 }
677 if (boot->kernel_time) {
678 svg_bar("kernel", 0, boot->kernel_done_time, y);
c170f3a4 679 svg_text(true, 0, y, "kernel");
2265fbf7
SP
680 y++;
681 }
682 if (boot->initrd_time) {
683 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
c170f3a4 684 svg_text(true, boot->initrd_time, y, "initrd");
2265fbf7
SP
685 y++;
686 }
518d10e9 687 svg_bar("active", boot->userspace_time, boot->finish_time, y);
c2e0d600 688 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
518d10e9 689 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
d9acfb71 690 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
95168f7d 691 svg_text(true, boot->userspace_time, y, "systemd");
2265fbf7
SP
692 y++;
693
c170f3a4
LP
694 for (u = times; u < times + n; u++) {
695 char ts[FORMAT_TIMESPAN_MAX];
fd4a5ec6 696 bool b;
c170f3a4 697
2265fbf7
SP
698 if (!u->name)
699 continue;
c170f3a4 700
cc27380c
TA
701 svg_bar("activating", u->activating, u->activated, y);
702 svg_bar("active", u->activated, u->deactivating, y);
703 svg_bar("deactivating", u->deactivating, u->deactivated, y);
c170f3a4 704
95168f7d 705 /* place the text on the left if we have passed the half of the svg width */
cc27380c 706 b = u->activating * SCALE_X < width / 2;
fd4a5ec6 707 if (u->time)
cc27380c 708 svg_text(b, u->activating, y, "%s (%s)",
2fa4092c 709 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
c170f3a4 710 else
cc27380c 711 svg_text(b, u->activating, y, "%s", u->name);
2265fbf7
SP
712 y++;
713 }
518d10e9 714
b5cfa740
TA
715 svg("</g>\n");
716
518d10e9 717 /* Legend */
b5cfa740 718 svg("<g transform=\"translate(20,100)\">\n");
518d10e9
UTL
719 y++;
720 svg_bar("activating", 0, 300000, y);
95168f7d 721 svg_text(true, 400000, y, "Activating");
518d10e9
UTL
722 y++;
723 svg_bar("active", 0, 300000, y);
95168f7d 724 svg_text(true, 400000, y, "Active");
518d10e9
UTL
725 y++;
726 svg_bar("deactivating", 0, 300000, y);
95168f7d 727 svg_text(true, 400000, y, "Deactivating");
518d10e9 728 y++;
c2e0d600
TA
729 svg_bar("security", 0, 300000, y);
730 svg_text(true, 400000, y, "Setting up security module");
731 y++;
518d10e9 732 svg_bar("generators", 0, 300000, y);
95168f7d 733 svg_text(true, 400000, y, "Generators");
518d10e9 734 y++;
d9acfb71 735 svg_bar("unitsload", 0, 300000, y);
95168f7d 736 svg_text(true, 400000, y, "Loading unit files");
d9acfb71 737 y++;
518d10e9 738
2265fbf7
SP
739 svg("</g>\n\n");
740
988b9df2 741 svg("</svg>\n");
c170f3a4
LP
742
743 free_unit_times(times, (unsigned) n);
744
7e690cef
DH
745 n = 0;
746out:
747 free_host_info(host);
748 return n;
2265fbf7
SP
749}
750
bb150966
HH
751static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
752 bool last, struct unit_times *times, struct boot_times *boot) {
753 unsigned int i;
754 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
755
756 for (i = level; i != 0; i--)
6b01f1d3 757 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE));
bb150966
HH
758
759 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
760
761 if (times) {
762 if (times->time)
763 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
cc27380c 764 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
bb150966 765 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
cc27380c
TA
766 else if (times->activated > boot->userspace_time)
767 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
768 else
769 printf("%s", name);
988b9df2
LP
770 } else
771 printf("%s", name);
bb150966
HH
772 printf("\n");
773
774 return 0;
775}
776
048ecf5b 777static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
988b9df2 778 _cleanup_free_ char *path = NULL;
bb150966
HH
779
780 assert(bus);
781 assert(name);
782 assert(deps);
783
784 path = unit_dbus_path_from_name(name);
6e6ca4a5 785 if (path == NULL)
988b9df2 786 return -ENOMEM;
bb150966 787
988b9df2 788 return bus_get_unit_property_strv(bus, path, "After", deps);
bb150966
HH
789}
790
791static Hashmap *unit_times_hashmap;
792
793static int list_dependencies_compare(const void *_a, const void *_b) {
794 const char **a = (const char**) _a, **b = (const char**) _b;
795 usec_t usa = 0, usb = 0;
796 struct unit_times *times;
797
798 times = hashmap_get(unit_times_hashmap, *a);
799 if (times)
cc27380c 800 usa = times->activated;
bb150966
HH
801 times = hashmap_get(unit_times_hashmap, *b);
802 if (times)
cc27380c 803 usb = times->activated;
bb150966
HH
804
805 return usb - usa;
806}
807
048ecf5b 808static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
bb150966
HH
809 unsigned int branches) {
810 _cleanup_strv_free_ char **deps = NULL;
811 char **c;
812 int r = 0;
813 usec_t service_longest = 0;
814 int to_print = 0;
815 struct unit_times *times;
816 struct boot_times *boot;
817
988b9df2 818 if (strv_extend(units, name))
bb150966
HH
819 return log_oom();
820
821 r = list_dependencies_get_dependencies(bus, name, &deps);
822 if (r < 0)
823 return r;
824
7ff7394d 825 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
bb150966
HH
826
827 r = acquire_boot_times(bus, &boot);
828 if (r < 0)
829 return r;
830
831 STRV_FOREACH(c, deps) {
832 times = hashmap_get(unit_times_hashmap, *c);
833 if (times
cc27380c
TA
834 && times->activated
835 && times->activated <= boot->finish_time
836 && (times->activated >= service_longest
bb150966 837 || service_longest == 0)) {
cc27380c 838 service_longest = times->activated;
bb150966
HH
839 break;
840 }
841 }
842
843 if (service_longest == 0 )
844 return r;
845
846 STRV_FOREACH(c, deps) {
847 times = hashmap_get(unit_times_hashmap, *c);
cc27380c
TA
848 if (times && times->activated
849 && times->activated <= boot->finish_time
850 && (service_longest - times->activated) <= arg_fuzz) {
bb150966
HH
851 to_print++;
852 }
853 }
854
f168c273 855 if (!to_print)
bb150966
HH
856 return r;
857
858 STRV_FOREACH(c, deps) {
859 times = hashmap_get(unit_times_hashmap, *c);
860 if (!times
cc27380c
TA
861 || !times->activated
862 || times->activated > boot->finish_time
863 || service_longest - times->activated > arg_fuzz)
bb150966
HH
864 continue;
865
866 to_print--;
867
868 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
869 if (r < 0)
870 return r;
871
872 if (strv_contains(*units, *c)) {
873 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
874 true, NULL, boot);
872c8faa
ZJS
875 if (r < 0)
876 return r;
bb150966
HH
877 continue;
878 }
879
880 r = list_dependencies_one(bus, *c, level + 1, units,
881 (branches << 1) | (to_print ? 1 : 0));
872c8faa 882 if (r < 0)
bb150966
HH
883 return r;
884
872c8faa 885 if (!to_print)
bb150966 886 break;
bb150966
HH
887 }
888 return 0;
889}
890
048ecf5b 891static int list_dependencies(sd_bus *bus, const char *name) {
bb150966
HH
892 _cleanup_strv_free_ char **units = NULL;
893 char ts[FORMAT_TIMESPAN_MAX];
894 struct unit_times *times;
895 int r;
0ee9613d
TA
896 const char *id;
897 _cleanup_free_ char *path = NULL;
048ecf5b
TA
898 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
899 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
bb150966
HH
900 struct boot_times *boot;
901
902 assert(bus);
903
805bf39c 904 path = unit_dbus_path_from_name(name);
bb150966 905 if (path == NULL)
988b9df2 906 return -ENOMEM;
bb150966 907
a936124f
TA
908 r = sd_bus_get_property(
909 bus,
910 "org.freedesktop.systemd1",
911 path,
912 "org.freedesktop.systemd1.Unit",
913 "Id",
914 &error,
915 &reply,
916 "s");
048ecf5b 917 if (r < 0) {
5b30bef8 918 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
bb150966 919 return r;
bb150966
HH
920 }
921
048ecf5b 922 r = sd_bus_message_read(reply, "s", &id);
5b30bef8
LP
923 if (r < 0)
924 return bus_log_parse_error(r);
bb150966 925
bb150966
HH
926 times = hashmap_get(unit_times_hashmap, id);
927
928 r = acquire_boot_times(bus, &boot);
929 if (r < 0)
930 return r;
931
932 if (times) {
933 if (times->time)
934 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
935 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
cc27380c
TA
936 else if (times->activated > boot->userspace_time)
937 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
938 else
939 printf("%s\n", id);
940 }
941
805bf39c 942 return list_dependencies_one(bus, name, 0, &units, 0);
bb150966
HH
943}
944
048ecf5b 945static int analyze_critical_chain(sd_bus *bus, char *names[]) {
bb150966 946 struct unit_times *times;
bb150966
HH
947 unsigned int i;
948 Hashmap *h;
988b9df2 949 int n, r;
bb150966
HH
950
951 n = acquire_time_data(bus, &times);
952 if (n <= 0)
953 return n;
954
d5099efc 955 h = hashmap_new(&string_hash_ops);
bb150966
HH
956 if (!h)
957 return -ENOMEM;
958
959 for (i = 0; i < (unsigned)n; i++) {
960 r = hashmap_put(h, times[i].name, &times[i]);
961 if (r < 0)
962 return r;
963 }
964 unit_times_hashmap = h;
965
9ea9d4cf
LP
966 pager_open_if_enabled();
967
bb150966
HH
968 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
969 "The time the unit takes to start is printed after the \"+\" character.\n");
970
805bf39c
GP
971 if (!strv_isempty(names)) {
972 char **name;
973 STRV_FOREACH(name, names)
974 list_dependencies(bus, *name);
9ea9d4cf 975 } else
805bf39c 976 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
bb150966
HH
977
978 hashmap_free(h);
979 free_unit_times(times, (unsigned) n);
980 return 0;
981}
982
048ecf5b 983static int analyze_blame(sd_bus *bus) {
2265fbf7 984 struct unit_times *times;
c170f3a4
LP
985 unsigned i;
986 int n;
987
988 n = acquire_time_data(bus, &times);
989 if (n <= 0)
2265fbf7
SP
990 return n;
991
992 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
993
9ea9d4cf
LP
994 pager_open_if_enabled();
995
c170f3a4
LP
996 for (i = 0; i < (unsigned) n; i++) {
997 char ts[FORMAT_TIMESPAN_MAX];
998
999 if (times[i].time > 0)
2fa4092c 1000 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
2265fbf7 1001 }
c170f3a4
LP
1002
1003 free_unit_times(times, (unsigned) n);
2265fbf7
SP
1004 return 0;
1005}
1006
048ecf5b 1007static int analyze_time(sd_bus *bus) {
c170f3a4
LP
1008 _cleanup_free_ char *buf = NULL;
1009 int r;
1010
1011 r = pretty_boot_time(bus, &buf);
1012 if (r < 0)
1013 return r;
1014
1015 puts(buf);
2265fbf7
SP
1016 return 0;
1017}
1018
f459b602 1019static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[]) {
048ecf5b 1020 _cleanup_strv_free_ char **units = NULL;
048ecf5b
TA
1021 char **unit;
1022 int r;
6ecb6cec 1023 bool match_patterns;
1700761b 1024
048ecf5b 1025 assert(u);
1700761b 1026 assert(prop);
048ecf5b
TA
1027 assert(color);
1028
2404701e 1029 match_patterns = strv_fnmatch(patterns, u->id, 0);
6ecb6cec
ZJS
1030
1031 if (!strv_isempty(arg_dot_from_patterns) &&
1032 !match_patterns &&
2404701e 1033 !strv_fnmatch(arg_dot_from_patterns, u->id, 0))
6ecb6cec
ZJS
1034 return 0;
1035
6e6ca4a5 1036 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
07d0eaa0 1037 if (r < 0)
988b9df2 1038 return r;
1700761b 1039
048ecf5b 1040 STRV_FOREACH(unit, units) {
6ecb6cec
ZJS
1041 bool match_patterns2;
1042
2404701e 1043 match_patterns2 = strv_fnmatch(patterns, *unit, 0);
816f25e8 1044
6ecb6cec
ZJS
1045 if (!strv_isempty(arg_dot_to_patterns) &&
1046 !match_patterns2 &&
2404701e 1047 !strv_fnmatch(arg_dot_to_patterns, *unit, 0))
bceccd5e 1048 continue;
e55933db 1049
6ecb6cec 1050 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
bceccd5e 1051 continue;
048ecf5b
TA
1052
1053 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1700761b
SP
1054 }
1055
1056 return 0;
1057}
1058
f459b602 1059static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) {
1700761b 1060 int r;
1700761b
SP
1061
1062 assert(bus);
1063 assert(u);
1064
048ecf5b
TA
1065 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
1066 r = graph_one_property(bus, u, "After", "green", patterns);
1067 if (r < 0)
1068 return r;
1700761b
SP
1069 }
1070
048ecf5b
TA
1071 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1072 r = graph_one_property(bus, u, "Requires", "black", patterns);
1073 if (r < 0)
1074 return r;
1075 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1076 if (r < 0)
1077 return r;
1078 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1079 if (r < 0)
1080 return r;
1081 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1082 if (r < 0)
1083 return r;
1084 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1085 if (r < 0)
1086 return r;
1087 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1700761b
SP
1088 if (r < 0)
1089 return r;
1090 }
1091
1092 return 0;
1093}
1094
83efb7c2
EV
1095static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1096 _cleanup_strv_free_ char **expanded_patterns = NULL;
1097 char **pattern;
1098 int r;
1099
1100 STRV_FOREACH(pattern, patterns) {
1101 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1102 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
1103
1104 if (strv_extend(&expanded_patterns, *pattern) < 0)
1105 return log_oom();
1106
1107 if (string_is_glob(*pattern))
1108 continue;
1109
1110 unit = unit_dbus_path_from_name(*pattern);
1111 if (!unit)
1112 return log_oom();
1113
1114 r = sd_bus_get_property_string(
1115 bus,
1116 "org.freedesktop.systemd1",
1117 unit,
1118 "org.freedesktop.systemd1.Unit",
1119 "Id",
1120 &error,
1121 &unit_id);
1122 if (r < 0)
1123 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1124
1125 if (!streq(*pattern, unit_id)) {
1126 if (strv_extend(&expanded_patterns, unit_id) < 0)
1127 return log_oom();
1128 }
1129 }
1130
1131 *ret = expanded_patterns;
1132 expanded_patterns = NULL; /* do not free */
1133
1134 return 0;
1135}
1136
048ecf5b
TA
1137static int dot(sd_bus *bus, char* patterns[]) {
1138 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1139 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
83efb7c2 1140 _cleanup_strv_free_ char **expanded_patterns = NULL;
1700761b 1141 int r;
f459b602 1142 UnitInfo u;
048ecf5b 1143
83efb7c2
EV
1144 r = expand_patterns(bus, patterns, &expanded_patterns);
1145 if (r < 0)
1146 return r;
1147
a936124f
TA
1148 r = sd_bus_call_method(
1149 bus,
1150 "org.freedesktop.systemd1",
1151 "/org/freedesktop/systemd1",
1152 "org.freedesktop.systemd1.Manager",
1153 "ListUnits",
1154 &error,
1155 &reply,
1156 "");
048ecf5b 1157 if (r < 0) {
988b9df2
LP
1158 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1159 return r;
048ecf5b 1160 }
1700761b 1161
048ecf5b 1162 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1700761b 1163 if (r < 0)
988b9df2 1164 return bus_log_parse_error(r);
1700761b
SP
1165
1166 printf("digraph systemd {\n");
1167
048ecf5b 1168 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
f459b602 1169
83efb7c2 1170 r = graph_one(bus, &u, expanded_patterns);
1700761b
SP
1171 if (r < 0)
1172 return r;
1173 }
f459b602
MAP
1174 if (r < 0)
1175 return bus_log_parse_error(r);
1700761b
SP
1176
1177 printf("}\n");
1178
1179 log_info(" Color legend: black = Requires\n"
1180 " dark blue = Requisite\n"
1181 " dark grey = Wants\n"
1182 " red = Conflicts\n"
1183 " green = After\n");
1184
1185 if (on_tty())
1186 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1187 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1188
1189 return 0;
1190}
c8a8806e 1191
048ecf5b
TA
1192static int dump(sd_bus *bus, char **args) {
1193 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1194 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1195 const char *text = NULL;
9ea9d4cf 1196 int r;
9ea9d4cf 1197
a65615ca
LP
1198 if (!strv_isempty(args)) {
1199 log_error("Too many arguments.");
1200 return -E2BIG;
1201 }
1202
9ea9d4cf
LP
1203 pager_open_if_enabled();
1204
a936124f
TA
1205 r = sd_bus_call_method(
1206 bus,
1207 "org.freedesktop.systemd1",
1208 "/org/freedesktop/systemd1",
1209 "org.freedesktop.systemd1.Manager",
1210 "Dump",
1211 &error,
1212 &reply,
1213 "");
048ecf5b 1214 if (r < 0) {
988b9df2 1215 log_error("Failed issue method call: %s", bus_error_message(&error, -r));
07d0eaa0 1216 return r;
048ecf5b 1217 }
9ea9d4cf 1218
048ecf5b 1219 r = sd_bus_message_read(reply, "s", &text);
5b30bef8
LP
1220 if (r < 0)
1221 return bus_log_parse_error(r);
9ea9d4cf
LP
1222
1223 fputs(text, stdout);
1224 return 0;
1225}
1226
048ecf5b
TA
1227static int set_log_level(sd_bus *bus, char **args) {
1228 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1229 int r;
a65615ca
LP
1230
1231 assert(bus);
1232 assert(args);
1233
1234 if (strv_length(args) != 1) {
1235 log_error("This command expects one argument only.");
1236 return -E2BIG;
1237 }
1238
a936124f
TA
1239 r = sd_bus_set_property(
1240 bus,
1241 "org.freedesktop.systemd1",
1242 "/org/freedesktop/systemd1",
1243 "org.freedesktop.systemd1.Manager",
1244 "LogLevel",
1245 &error,
1246 "s",
d3eba116 1247 args[0]);
048ecf5b
TA
1248 if (r < 0) {
1249 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
a65615ca
LP
1250 return -EIO;
1251 }
1252
1253 return 0;
1254}
1255
1d3bc017 1256static void help(void) {
9ea9d4cf
LP
1257
1258 pager_open_if_enabled();
1259
2265fbf7 1260 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1d3bc017 1261 "Profile systemd, show unit dependencies, check unit files.\n\n"
3cd26e7c
LP
1262 " -h --help Show this help\n"
1263 " --version Show package version\n"
a86a47ce 1264 " --no-pager Do not pipe output into a pager\n"
1d3bc017
ZJS
1265 " --system Operate on system systemd instance\n"
1266 " --user Operate on user systemd instance\n"
3cd26e7c
LP
1267 " -H --host=[USER@]HOST Operate on remote host\n"
1268 " -M --machine=CONTAINER Operate on local container\n"
b57b0625
ZJS
1269 " --order Show only order in the graph\n"
1270 " --require Show only requirement in the graph\n"
1271 " --from-pattern=GLOB Show only origins in the graph\n"
1272 " --to-pattern=GLOB Show only destinations in the graph\n"
1273 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1274 " earlier than the latest in the branch\n"
dad29dff 1275 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
2265fbf7 1276 "Commands:\n"
b57b0625 1277 " time Print time spent in the kernel\n"
3cd26e7c
LP
1278 " blame Print list of running units ordered by time to init\n"
1279 " critical-chain Print a tree of the time critical chain of units\n"
1280 " plot Output SVG graphic showing service initialization\n"
1281 " dot Output dependency graph in dot(1) format\n"
1282 " set-log-level LEVEL Set logging threshold for systemd\n"
1d3bc017 1283 " dump Output state serialization of service manager\n"
6a41c9e2 1284 " verify FILE... Check unit files for correctness\n"
1d3bc017 1285 , program_invocation_short_name);
96de7c04
ZJS
1286
1287 /* When updating this list, including descriptions, apply
1d3bc017
ZJS
1288 * changes to shell-completion/bash/systemd-analyze and
1289 * shell-completion/zsh/_systemd-analyze too. */
2265fbf7
SP
1290}
1291
9ea9d4cf 1292static int parse_argv(int argc, char *argv[]) {
2265fbf7
SP
1293 enum {
1294 ARG_VERSION = 0x100,
1700761b
SP
1295 ARG_ORDER,
1296 ARG_REQUIRE,
2265fbf7 1297 ARG_USER,
e55933db
ŁS
1298 ARG_SYSTEM,
1299 ARG_DOT_FROM_PATTERN,
bb150966 1300 ARG_DOT_TO_PATTERN,
9ea9d4cf 1301 ARG_FUZZ,
1d3bc017 1302 ARG_NO_PAGER,
dad29dff 1303 ARG_MAN,
2265fbf7
SP
1304 };
1305
1306 static const struct option options[] = {
9ea9d4cf
LP
1307 { "help", no_argument, NULL, 'h' },
1308 { "version", no_argument, NULL, ARG_VERSION },
1309 { "order", no_argument, NULL, ARG_ORDER },
1310 { "require", no_argument, NULL, ARG_REQUIRE },
1311 { "user", no_argument, NULL, ARG_USER },
1312 { "system", no_argument, NULL, ARG_SYSTEM },
1313 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1314 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1315 { "fuzz", required_argument, NULL, ARG_FUZZ },
1316 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
dad29dff 1317 { "man", optional_argument, NULL, ARG_MAN },
8fe12d88
LP
1318 { "host", required_argument, NULL, 'H' },
1319 { "machine", required_argument, NULL, 'M' },
eb9da376 1320 {}
2265fbf7
SP
1321 };
1322
eb9da376
LP
1323 int r, c;
1324
2265fbf7
SP
1325 assert(argc >= 0);
1326 assert(argv);
1327
601185b4 1328 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
eb9da376 1329 switch (c) {
c170f3a4
LP
1330
1331 case 'h':
1d3bc017
ZJS
1332 help();
1333 return 0;
c170f3a4
LP
1334
1335 case ARG_VERSION:
3cd26e7c
LP
1336 puts(PACKAGE_STRING);
1337 puts(SYSTEMD_FEATURES);
c170f3a4
LP
1338 return 0;
1339
1340 case ARG_USER:
3cd26e7c 1341 arg_user = true;
c170f3a4
LP
1342 break;
1343
1344 case ARG_SYSTEM:
3cd26e7c 1345 arg_user = false;
c170f3a4
LP
1346 break;
1347
1348 case ARG_ORDER:
1349 arg_dot = DEP_ORDER;
1350 break;
1351
1352 case ARG_REQUIRE:
1353 arg_dot = DEP_REQUIRE;
1354 break;
1355
e55933db 1356 case ARG_DOT_FROM_PATTERN:
903a0b07
LP
1357 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1358 return log_oom();
1359
e55933db
ŁS
1360 break;
1361
1362 case ARG_DOT_TO_PATTERN:
903a0b07
LP
1363 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1364 return log_oom();
1365
e55933db
ŁS
1366 break;
1367
bb150966
HH
1368 case ARG_FUZZ:
1369 r = parse_sec(optarg, &arg_fuzz);
1370 if (r < 0)
1371 return r;
1372 break;
1373
9ea9d4cf
LP
1374 case ARG_NO_PAGER:
1375 arg_no_pager = true;
1376 break;
1377
3cd26e7c
LP
1378 case 'H':
1379 arg_transport = BUS_TRANSPORT_REMOTE;
1380 arg_host = optarg;
1381 break;
1382
1383 case 'M':
de33fc62 1384 arg_transport = BUS_TRANSPORT_MACHINE;
3cd26e7c
LP
1385 arg_host = optarg;
1386 break;
1387
dad29dff
LP
1388 case ARG_MAN:
1389 if (optarg) {
1390 r = parse_boolean(optarg);
1391 if (r < 0) {
1392 log_error("Failed to parse --man= argument.");
1393 return -EINVAL;
1394 }
1395
1396 arg_man = !!r;
1397 } else
1398 arg_man = true;
1399
1d3bc017
ZJS
1400 break;
1401
c170f3a4
LP
1402 case '?':
1403 return -EINVAL;
1404
1405 default:
1d3bc017 1406 assert_not_reached("Unhandled option code.");
2265fbf7 1407 }
eb9da376 1408
1d3bc017 1409 return 1; /* work to do */
2265fbf7
SP
1410}
1411
1412int main(int argc, char *argv[]) {
5220a6f3 1413 int r;
2265fbf7
SP
1414
1415 setlocale(LC_ALL, "");
c170f3a4 1416 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
2265fbf7
SP
1417 log_parse_environment();
1418 log_open();
1419
1420 r = parse_argv(argc, argv);
9ea9d4cf
LP
1421 if (r <= 0)
1422 goto finish;
2265fbf7 1423
5b6f6ebd 1424 if (streq_ptr(argv[optind], "verify"))
1d3bc017 1425 r = verify_units(argv+optind+1,
b2c23da8 1426 arg_user ? MANAGER_USER : MANAGER_SYSTEM,
1d3bc017
ZJS
1427 arg_man);
1428 else {
03976f7b 1429 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
1d3bc017
ZJS
1430
1431 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1432 if (r < 0) {
da927ba9 1433 log_error_errno(r, "Failed to create bus connection: %m");
1d3bc017
ZJS
1434 goto finish;
1435 }
2265fbf7 1436
1d3bc017
ZJS
1437 if (!argv[optind] || streq(argv[optind], "time"))
1438 r = analyze_time(bus);
1439 else if (streq(argv[optind], "blame"))
1440 r = analyze_blame(bus);
1441 else if (streq(argv[optind], "critical-chain"))
1442 r = analyze_critical_chain(bus, argv+optind+1);
1443 else if (streq(argv[optind], "plot"))
1444 r = analyze_plot(bus);
1445 else if (streq(argv[optind], "dot"))
1446 r = dot(bus, argv+optind+1);
1447 else if (streq(argv[optind], "dump"))
1448 r = dump(bus, argv+optind+1);
1449 else if (streq(argv[optind], "set-log-level"))
1450 r = set_log_level(bus, argv+optind+1);
1451 else
1452 log_error("Unknown operation '%s'.", argv[optind]);
1453 }
2265fbf7 1454
9ea9d4cf
LP
1455finish:
1456 pager_close();
1457
e55933db
ŁS
1458 strv_free(arg_dot_from_patterns);
1459 strv_free(arg_dot_to_patterns);
c170f3a4
LP
1460
1461 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2265fbf7 1462}