]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/analyze.c
Merge pull request #1465 from teg/siphash24
[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
2265fbf7
SP
23#include <getopt.h>
24#include <locale.h>
3f6fd1ba
LP
25#include <stdio.h>
26#include <stdlib.h>
2265fbf7 27
048ecf5b 28#include "sd-bus.h"
3f6fd1ba
LP
29
30#include "analyze-verify.h"
048ecf5b 31#include "bus-error.h"
3f6fd1ba 32#include "bus-util.h"
bb150966 33#include "hashmap.h"
3f6fd1ba 34#include "log.h"
9ea9d4cf 35#include "pager.h"
3f6fd1ba
LP
36#include "special.h"
37#include "strv.h"
38#include "strxcpyx.h"
288a74cc 39#include "terminal-util.h"
3f6fd1ba
LP
40#include "unit-name.h"
41#include "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) {
a1e58e8e 585 u->name = mfree(u->name);
2265fbf7
SP
586 continue;
587 }
95168f7d
TA
588
589 /* If the text cannot fit on the left side then
590 * increase the svg width so it fits on the right.
591 * TODO: calculate the text width more accurately */
592 text_width = 8.0 * strlen(u->name);
cc27380c 593 text_start = (boot->firmware_time + u->activating) * SCALE_X;
95168f7d
TA
594 if (text_width > text_start && text_width + text_start > width)
595 width = text_width + text_start;
2265fbf7 596
cc27380c
TA
597 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
598 && u->activated == 0 && u->deactivating == 0)
599 u->activated = u->deactivating = u->deactivated;
600 if (u->activated < u->activating || u->activated > boot->finish_time)
601 u->activated = boot->finish_time;
602 if (u->deactivating < u->activated || u->activated > boot->finish_time)
603 u->deactivating = boot->finish_time;
604 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
605 u->deactivated = boot->finish_time;
2265fbf7
SP
606 m++;
607 }
608
609 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
610 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
611 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
612
613 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
614 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
518d10e9 615 80.0 + width, 150.0 + (m * SCALE_Y) +
d9acfb71 616 5 * SCALE_Y /* legend */);
2265fbf7
SP
617
618 /* write some basic info as a comment, including some help */
619 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
620 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
621 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
622 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
623 "<!-- point your browser to this file. -->\n\n"
624 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
625
626 /* style sheet */
627 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
628 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
418e3750 629 " rect.background { fill: rgb(255,255,255); }\n"
2265fbf7
SP
630 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
631 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
632 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
633 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
634 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
635 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
636 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
637 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
c2e0d600 638 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
518d10e9 639 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
d9acfb71 640 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
2265fbf7
SP
641 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
642 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
643 "// line.sec1 { }\n"
644 " line.sec5 { stroke-width: 2; }\n"
645 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
2b7d6965
TA
646 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
647 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
648 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
649 " text.sec { font-size: 10px; }\n"
2265fbf7
SP
650 " ]]>\n </style>\n</defs>\n\n");
651
418e3750 652 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
2265fbf7 653 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
7e690cef
DH
654 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
655 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
656 isempty(host->hostname) ? "" : host->hostname,
657 isempty(host->kernel_name) ? "" : host->kernel_name,
658 isempty(host->kernel_release) ? "" : host->kernel_release,
659 isempty(host->kernel_version) ? "" : host->kernel_version,
660 isempty(host->architecture) ? "" : host->architecture,
661 isempty(host->virtualization) ? "" : host->virtualization);
2265fbf7 662
2f6eb835 663 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
b5cfa740 664 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
2265fbf7
SP
665
666 if (boot->firmware_time) {
c170f3a4
LP
667 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
668 svg_text(true, -(double) boot->firmware_time, y, "firmware");
2265fbf7
SP
669 y++;
670 }
671 if (boot->loader_time) {
c170f3a4
LP
672 svg_bar("loader", -(double) boot->loader_time, 0, y);
673 svg_text(true, -(double) boot->loader_time, y, "loader");
2265fbf7
SP
674 y++;
675 }
676 if (boot->kernel_time) {
677 svg_bar("kernel", 0, boot->kernel_done_time, y);
c170f3a4 678 svg_text(true, 0, y, "kernel");
2265fbf7
SP
679 y++;
680 }
681 if (boot->initrd_time) {
682 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
c170f3a4 683 svg_text(true, boot->initrd_time, y, "initrd");
2265fbf7
SP
684 y++;
685 }
518d10e9 686 svg_bar("active", boot->userspace_time, boot->finish_time, y);
c2e0d600 687 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
518d10e9 688 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
d9acfb71 689 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
95168f7d 690 svg_text(true, boot->userspace_time, y, "systemd");
2265fbf7
SP
691 y++;
692
c170f3a4
LP
693 for (u = times; u < times + n; u++) {
694 char ts[FORMAT_TIMESPAN_MAX];
fd4a5ec6 695 bool b;
c170f3a4 696
2265fbf7
SP
697 if (!u->name)
698 continue;
c170f3a4 699
cc27380c
TA
700 svg_bar("activating", u->activating, u->activated, y);
701 svg_bar("active", u->activated, u->deactivating, y);
702 svg_bar("deactivating", u->deactivating, u->deactivated, y);
c170f3a4 703
95168f7d 704 /* place the text on the left if we have passed the half of the svg width */
cc27380c 705 b = u->activating * SCALE_X < width / 2;
fd4a5ec6 706 if (u->time)
cc27380c 707 svg_text(b, u->activating, y, "%s (%s)",
2fa4092c 708 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
c170f3a4 709 else
cc27380c 710 svg_text(b, u->activating, y, "%s", u->name);
2265fbf7
SP
711 y++;
712 }
518d10e9 713
b5cfa740
TA
714 svg("</g>\n");
715
518d10e9 716 /* Legend */
b5cfa740 717 svg("<g transform=\"translate(20,100)\">\n");
518d10e9
UTL
718 y++;
719 svg_bar("activating", 0, 300000, y);
95168f7d 720 svg_text(true, 400000, y, "Activating");
518d10e9
UTL
721 y++;
722 svg_bar("active", 0, 300000, y);
95168f7d 723 svg_text(true, 400000, y, "Active");
518d10e9
UTL
724 y++;
725 svg_bar("deactivating", 0, 300000, y);
95168f7d 726 svg_text(true, 400000, y, "Deactivating");
518d10e9 727 y++;
c2e0d600
TA
728 svg_bar("security", 0, 300000, y);
729 svg_text(true, 400000, y, "Setting up security module");
730 y++;
518d10e9 731 svg_bar("generators", 0, 300000, y);
95168f7d 732 svg_text(true, 400000, y, "Generators");
518d10e9 733 y++;
d9acfb71 734 svg_bar("unitsload", 0, 300000, y);
95168f7d 735 svg_text(true, 400000, y, "Loading unit files");
d9acfb71 736 y++;
518d10e9 737
2265fbf7
SP
738 svg("</g>\n\n");
739
988b9df2 740 svg("</svg>\n");
c170f3a4
LP
741
742 free_unit_times(times, (unsigned) n);
743
7e690cef
DH
744 n = 0;
745out:
746 free_host_info(host);
747 return n;
2265fbf7
SP
748}
749
bb150966
HH
750static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
751 bool last, struct unit_times *times, struct boot_times *boot) {
752 unsigned int i;
753 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
754
755 for (i = level; i != 0; i--)
6b01f1d3 756 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE));
bb150966
HH
757
758 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
759
760 if (times) {
761 if (times->time)
1fc464f6 762 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED, name,
cc27380c 763 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
1fc464f6 764 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_NORMAL);
cc27380c
TA
765 else if (times->activated > boot->userspace_time)
766 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
767 else
768 printf("%s", name);
988b9df2
LP
769 } else
770 printf("%s", name);
bb150966
HH
771 printf("\n");
772
773 return 0;
774}
775
048ecf5b 776static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
988b9df2 777 _cleanup_free_ char *path = NULL;
bb150966
HH
778
779 assert(bus);
780 assert(name);
781 assert(deps);
782
783 path = unit_dbus_path_from_name(name);
6e6ca4a5 784 if (path == NULL)
988b9df2 785 return -ENOMEM;
bb150966 786
988b9df2 787 return bus_get_unit_property_strv(bus, path, "After", deps);
bb150966
HH
788}
789
790static Hashmap *unit_times_hashmap;
791
792static int list_dependencies_compare(const void *_a, const void *_b) {
793 const char **a = (const char**) _a, **b = (const char**) _b;
794 usec_t usa = 0, usb = 0;
795 struct unit_times *times;
796
797 times = hashmap_get(unit_times_hashmap, *a);
798 if (times)
cc27380c 799 usa = times->activated;
bb150966
HH
800 times = hashmap_get(unit_times_hashmap, *b);
801 if (times)
cc27380c 802 usb = times->activated;
bb150966
HH
803
804 return usb - usa;
805}
806
048ecf5b 807static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
bb150966
HH
808 unsigned int branches) {
809 _cleanup_strv_free_ char **deps = NULL;
810 char **c;
811 int r = 0;
812 usec_t service_longest = 0;
813 int to_print = 0;
814 struct unit_times *times;
815 struct boot_times *boot;
816
988b9df2 817 if (strv_extend(units, name))
bb150966
HH
818 return log_oom();
819
820 r = list_dependencies_get_dependencies(bus, name, &deps);
821 if (r < 0)
822 return r;
823
7ff7394d 824 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
bb150966
HH
825
826 r = acquire_boot_times(bus, &boot);
827 if (r < 0)
828 return r;
829
830 STRV_FOREACH(c, deps) {
831 times = hashmap_get(unit_times_hashmap, *c);
832 if (times
cc27380c
TA
833 && times->activated
834 && times->activated <= boot->finish_time
835 && (times->activated >= service_longest
bb150966 836 || service_longest == 0)) {
cc27380c 837 service_longest = times->activated;
bb150966
HH
838 break;
839 }
840 }
841
842 if (service_longest == 0 )
843 return r;
844
845 STRV_FOREACH(c, deps) {
846 times = hashmap_get(unit_times_hashmap, *c);
ece174c5 847 if (times && times->activated && times->activated <= boot->finish_time && (service_longest - times->activated) <= arg_fuzz)
bb150966 848 to_print++;
bb150966
HH
849 }
850
f168c273 851 if (!to_print)
bb150966
HH
852 return r;
853
854 STRV_FOREACH(c, deps) {
855 times = hashmap_get(unit_times_hashmap, *c);
856 if (!times
cc27380c
TA
857 || !times->activated
858 || times->activated > boot->finish_time
859 || service_longest - times->activated > arg_fuzz)
bb150966
HH
860 continue;
861
862 to_print--;
863
864 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
865 if (r < 0)
866 return r;
867
868 if (strv_contains(*units, *c)) {
869 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
870 true, NULL, boot);
872c8faa
ZJS
871 if (r < 0)
872 return r;
bb150966
HH
873 continue;
874 }
875
876 r = list_dependencies_one(bus, *c, level + 1, units,
877 (branches << 1) | (to_print ? 1 : 0));
872c8faa 878 if (r < 0)
bb150966
HH
879 return r;
880
872c8faa 881 if (!to_print)
bb150966 882 break;
bb150966
HH
883 }
884 return 0;
885}
886
048ecf5b 887static int list_dependencies(sd_bus *bus, const char *name) {
bb150966
HH
888 _cleanup_strv_free_ char **units = NULL;
889 char ts[FORMAT_TIMESPAN_MAX];
890 struct unit_times *times;
891 int r;
0ee9613d
TA
892 const char *id;
893 _cleanup_free_ char *path = NULL;
048ecf5b
TA
894 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
895 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
bb150966
HH
896 struct boot_times *boot;
897
898 assert(bus);
899
805bf39c 900 path = unit_dbus_path_from_name(name);
bb150966 901 if (path == NULL)
988b9df2 902 return -ENOMEM;
bb150966 903
a936124f
TA
904 r = sd_bus_get_property(
905 bus,
906 "org.freedesktop.systemd1",
907 path,
908 "org.freedesktop.systemd1.Unit",
909 "Id",
910 &error,
911 &reply,
912 "s");
048ecf5b 913 if (r < 0) {
5b30bef8 914 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
bb150966 915 return r;
bb150966
HH
916 }
917
048ecf5b 918 r = sd_bus_message_read(reply, "s", &id);
5b30bef8
LP
919 if (r < 0)
920 return bus_log_parse_error(r);
bb150966 921
bb150966
HH
922 times = hashmap_get(unit_times_hashmap, id);
923
924 r = acquire_boot_times(bus, &boot);
925 if (r < 0)
926 return r;
927
928 if (times) {
929 if (times->time)
1fc464f6
LP
930 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED, id,
931 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_NORMAL);
cc27380c
TA
932 else if (times->activated > boot->userspace_time)
933 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
934 else
935 printf("%s\n", id);
936 }
937
805bf39c 938 return list_dependencies_one(bus, name, 0, &units, 0);
bb150966
HH
939}
940
048ecf5b 941static int analyze_critical_chain(sd_bus *bus, char *names[]) {
bb150966 942 struct unit_times *times;
bb150966
HH
943 unsigned int i;
944 Hashmap *h;
988b9df2 945 int n, r;
bb150966
HH
946
947 n = acquire_time_data(bus, &times);
948 if (n <= 0)
949 return n;
950
d5099efc 951 h = hashmap_new(&string_hash_ops);
bb150966
HH
952 if (!h)
953 return -ENOMEM;
954
955 for (i = 0; i < (unsigned)n; i++) {
956 r = hashmap_put(h, times[i].name, &times[i]);
957 if (r < 0)
958 return r;
959 }
960 unit_times_hashmap = h;
961
9ea9d4cf
LP
962 pager_open_if_enabled();
963
bb150966
HH
964 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
965 "The time the unit takes to start is printed after the \"+\" character.\n");
966
805bf39c
GP
967 if (!strv_isempty(names)) {
968 char **name;
969 STRV_FOREACH(name, names)
970 list_dependencies(bus, *name);
9ea9d4cf 971 } else
805bf39c 972 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
bb150966
HH
973
974 hashmap_free(h);
975 free_unit_times(times, (unsigned) n);
976 return 0;
977}
978
048ecf5b 979static int analyze_blame(sd_bus *bus) {
2265fbf7 980 struct unit_times *times;
c170f3a4
LP
981 unsigned i;
982 int n;
983
984 n = acquire_time_data(bus, &times);
985 if (n <= 0)
2265fbf7
SP
986 return n;
987
988 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
989
9ea9d4cf
LP
990 pager_open_if_enabled();
991
c170f3a4
LP
992 for (i = 0; i < (unsigned) n; i++) {
993 char ts[FORMAT_TIMESPAN_MAX];
994
995 if (times[i].time > 0)
2fa4092c 996 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
2265fbf7 997 }
c170f3a4
LP
998
999 free_unit_times(times, (unsigned) n);
2265fbf7
SP
1000 return 0;
1001}
1002
048ecf5b 1003static int analyze_time(sd_bus *bus) {
c170f3a4
LP
1004 _cleanup_free_ char *buf = NULL;
1005 int r;
1006
1007 r = pretty_boot_time(bus, &buf);
1008 if (r < 0)
1009 return r;
1010
1011 puts(buf);
2265fbf7
SP
1012 return 0;
1013}
1014
4387795e 1015static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[], char* from_patterns[], char* to_patterns[]) {
048ecf5b 1016 _cleanup_strv_free_ char **units = NULL;
048ecf5b
TA
1017 char **unit;
1018 int r;
6ecb6cec 1019 bool match_patterns;
1700761b 1020
048ecf5b 1021 assert(u);
1700761b 1022 assert(prop);
048ecf5b
TA
1023 assert(color);
1024
2404701e 1025 match_patterns = strv_fnmatch(patterns, u->id, 0);
6ecb6cec 1026
4387795e 1027 if (!strv_isempty(from_patterns) &&
6ecb6cec 1028 !match_patterns &&
4387795e 1029 !strv_fnmatch(from_patterns, u->id, 0))
6ecb6cec
ZJS
1030 return 0;
1031
6e6ca4a5 1032 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
07d0eaa0 1033 if (r < 0)
988b9df2 1034 return r;
1700761b 1035
048ecf5b 1036 STRV_FOREACH(unit, units) {
6ecb6cec
ZJS
1037 bool match_patterns2;
1038
2404701e 1039 match_patterns2 = strv_fnmatch(patterns, *unit, 0);
816f25e8 1040
4387795e 1041 if (!strv_isempty(to_patterns) &&
6ecb6cec 1042 !match_patterns2 &&
4387795e 1043 !strv_fnmatch(to_patterns, *unit, 0))
bceccd5e 1044 continue;
e55933db 1045
6ecb6cec 1046 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
bceccd5e 1047 continue;
048ecf5b
TA
1048
1049 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1700761b
SP
1050 }
1051
1052 return 0;
1053}
1054
4387795e 1055static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
1700761b 1056 int r;
1700761b
SP
1057
1058 assert(bus);
1059 assert(u);
1060
048ecf5b 1061 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
4387795e 1062 r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
048ecf5b
TA
1063 if (r < 0)
1064 return r;
1700761b
SP
1065 }
1066
048ecf5b 1067 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
4387795e 1068 r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
048ecf5b
TA
1069 if (r < 0)
1070 return r;
4387795e 1071 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns, from_patterns, to_patterns);
048ecf5b
TA
1072 if (r < 0)
1073 return r;
4387795e 1074 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns, from_patterns, to_patterns);
048ecf5b
TA
1075 if (r < 0)
1076 return r;
4387795e 1077 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
048ecf5b
TA
1078 if (r < 0)
1079 return r;
4387795e 1080 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
048ecf5b
TA
1081 if (r < 0)
1082 return r;
4387795e 1083 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns, from_patterns, to_patterns);
1700761b
SP
1084 if (r < 0)
1085 return r;
1086 }
1087
1088 return 0;
1089}
1090
83efb7c2
EV
1091static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1092 _cleanup_strv_free_ char **expanded_patterns = NULL;
1093 char **pattern;
1094 int r;
1095
1096 STRV_FOREACH(pattern, patterns) {
1097 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1098 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
1099
1100 if (strv_extend(&expanded_patterns, *pattern) < 0)
1101 return log_oom();
1102
1103 if (string_is_glob(*pattern))
1104 continue;
1105
1106 unit = unit_dbus_path_from_name(*pattern);
1107 if (!unit)
1108 return log_oom();
1109
1110 r = sd_bus_get_property_string(
1111 bus,
1112 "org.freedesktop.systemd1",
1113 unit,
1114 "org.freedesktop.systemd1.Unit",
1115 "Id",
1116 &error,
1117 &unit_id);
1118 if (r < 0)
1119 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1120
1121 if (!streq(*pattern, unit_id)) {
1122 if (strv_extend(&expanded_patterns, unit_id) < 0)
1123 return log_oom();
1124 }
1125 }
1126
1127 *ret = expanded_patterns;
1128 expanded_patterns = NULL; /* do not free */
1129
1130 return 0;
1131}
1132
048ecf5b
TA
1133static int dot(sd_bus *bus, char* patterns[]) {
1134 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1135 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
83efb7c2 1136 _cleanup_strv_free_ char **expanded_patterns = NULL;
4387795e
EV
1137 _cleanup_strv_free_ char **expanded_from_patterns = NULL;
1138 _cleanup_strv_free_ char **expanded_to_patterns = NULL;
1700761b 1139 int r;
f459b602 1140 UnitInfo u;
048ecf5b 1141
83efb7c2
EV
1142 r = expand_patterns(bus, patterns, &expanded_patterns);
1143 if (r < 0)
1144 return r;
1145
4387795e
EV
1146 r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1147 if (r < 0)
1148 return r;
1149
1150 r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1151 if (r < 0)
1152 return r;
1153
a936124f
TA
1154 r = sd_bus_call_method(
1155 bus,
1156 "org.freedesktop.systemd1",
1157 "/org/freedesktop/systemd1",
1158 "org.freedesktop.systemd1.Manager",
1159 "ListUnits",
1160 &error,
1161 &reply,
1162 "");
048ecf5b 1163 if (r < 0) {
988b9df2
LP
1164 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1165 return r;
048ecf5b 1166 }
1700761b 1167
048ecf5b 1168 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1700761b 1169 if (r < 0)
988b9df2 1170 return bus_log_parse_error(r);
1700761b
SP
1171
1172 printf("digraph systemd {\n");
1173
048ecf5b 1174 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
f459b602 1175
4387795e 1176 r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1700761b
SP
1177 if (r < 0)
1178 return r;
1179 }
f459b602
MAP
1180 if (r < 0)
1181 return bus_log_parse_error(r);
1700761b
SP
1182
1183 printf("}\n");
1184
1185 log_info(" Color legend: black = Requires\n"
1186 " dark blue = Requisite\n"
1187 " dark grey = Wants\n"
1188 " red = Conflicts\n"
1189 " green = After\n");
1190
1191 if (on_tty())
1192 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1193 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1194
1195 return 0;
1196}
c8a8806e 1197
048ecf5b
TA
1198static int dump(sd_bus *bus, char **args) {
1199 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1200 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1201 const char *text = NULL;
9ea9d4cf 1202 int r;
9ea9d4cf 1203
a65615ca
LP
1204 if (!strv_isempty(args)) {
1205 log_error("Too many arguments.");
1206 return -E2BIG;
1207 }
1208
9ea9d4cf
LP
1209 pager_open_if_enabled();
1210
a936124f
TA
1211 r = sd_bus_call_method(
1212 bus,
1213 "org.freedesktop.systemd1",
1214 "/org/freedesktop/systemd1",
1215 "org.freedesktop.systemd1.Manager",
1216 "Dump",
1217 &error,
1218 &reply,
1219 "");
2ca2a91c
LP
1220 if (r < 0)
1221 return log_error_errno(r, "Failed issue method call: %s", bus_error_message(&error, r));
9ea9d4cf 1222
048ecf5b 1223 r = sd_bus_message_read(reply, "s", &text);
5b30bef8
LP
1224 if (r < 0)
1225 return bus_log_parse_error(r);
9ea9d4cf
LP
1226
1227 fputs(text, stdout);
1228 return 0;
1229}
1230
048ecf5b
TA
1231static int set_log_level(sd_bus *bus, char **args) {
1232 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1233 int r;
a65615ca
LP
1234
1235 assert(bus);
1236 assert(args);
1237
1238 if (strv_length(args) != 1) {
1239 log_error("This command expects one argument only.");
1240 return -E2BIG;
1241 }
1242
a936124f
TA
1243 r = sd_bus_set_property(
1244 bus,
1245 "org.freedesktop.systemd1",
1246 "/org/freedesktop/systemd1",
1247 "org.freedesktop.systemd1.Manager",
1248 "LogLevel",
1249 &error,
1250 "s",
d3eba116 1251 args[0]);
2ca2a91c
LP
1252 if (r < 0)
1253 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1254
1255 return 0;
1256}
1257
1258static int set_log_target(sd_bus *bus, char **args) {
1259 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1260 int r;
1261
1262 assert(bus);
1263 assert(args);
1264
1265 if (strv_length(args) != 1) {
1266 log_error("This command expects one argument only.");
1267 return -E2BIG;
a65615ca
LP
1268 }
1269
2ca2a91c
LP
1270 r = sd_bus_set_property(
1271 bus,
1272 "org.freedesktop.systemd1",
1273 "/org/freedesktop/systemd1",
1274 "org.freedesktop.systemd1.Manager",
1275 "LogTarget",
1276 &error,
1277 "s",
1278 args[0]);
1279 if (r < 0)
1280 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1281
a65615ca
LP
1282 return 0;
1283}
1284
1d3bc017 1285static void help(void) {
9ea9d4cf
LP
1286
1287 pager_open_if_enabled();
1288
2265fbf7 1289 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1d3bc017 1290 "Profile systemd, show unit dependencies, check unit files.\n\n"
3cd26e7c
LP
1291 " -h --help Show this help\n"
1292 " --version Show package version\n"
a86a47ce 1293 " --no-pager Do not pipe output into a pager\n"
1d3bc017
ZJS
1294 " --system Operate on system systemd instance\n"
1295 " --user Operate on user systemd instance\n"
3cd26e7c
LP
1296 " -H --host=[USER@]HOST Operate on remote host\n"
1297 " -M --machine=CONTAINER Operate on local container\n"
b57b0625
ZJS
1298 " --order Show only order in the graph\n"
1299 " --require Show only requirement in the graph\n"
1300 " --from-pattern=GLOB Show only origins in the graph\n"
1301 " --to-pattern=GLOB Show only destinations in the graph\n"
1302 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1303 " earlier than the latest in the branch\n"
dad29dff 1304 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
2265fbf7 1305 "Commands:\n"
b57b0625 1306 " time Print time spent in the kernel\n"
3cd26e7c
LP
1307 " blame Print list of running units ordered by time to init\n"
1308 " critical-chain Print a tree of the time critical chain of units\n"
1309 " plot Output SVG graphic showing service initialization\n"
1310 " dot Output dependency graph in dot(1) format\n"
2ca2a91c
LP
1311 " set-log-level LEVEL Set logging threshold for manager\n"
1312 " set-log-target TARGET Set logging target for manager\n"
1d3bc017 1313 " dump Output state serialization of service manager\n"
6a41c9e2 1314 " verify FILE... Check unit files for correctness\n"
1d3bc017 1315 , program_invocation_short_name);
96de7c04
ZJS
1316
1317 /* When updating this list, including descriptions, apply
1d3bc017
ZJS
1318 * changes to shell-completion/bash/systemd-analyze and
1319 * shell-completion/zsh/_systemd-analyze too. */
2265fbf7
SP
1320}
1321
9ea9d4cf 1322static int parse_argv(int argc, char *argv[]) {
2265fbf7
SP
1323 enum {
1324 ARG_VERSION = 0x100,
1700761b
SP
1325 ARG_ORDER,
1326 ARG_REQUIRE,
2265fbf7 1327 ARG_USER,
e55933db
ŁS
1328 ARG_SYSTEM,
1329 ARG_DOT_FROM_PATTERN,
bb150966 1330 ARG_DOT_TO_PATTERN,
9ea9d4cf 1331 ARG_FUZZ,
1d3bc017 1332 ARG_NO_PAGER,
dad29dff 1333 ARG_MAN,
2265fbf7
SP
1334 };
1335
1336 static const struct option options[] = {
9ea9d4cf
LP
1337 { "help", no_argument, NULL, 'h' },
1338 { "version", no_argument, NULL, ARG_VERSION },
1339 { "order", no_argument, NULL, ARG_ORDER },
1340 { "require", no_argument, NULL, ARG_REQUIRE },
1341 { "user", no_argument, NULL, ARG_USER },
1342 { "system", no_argument, NULL, ARG_SYSTEM },
1343 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1344 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1345 { "fuzz", required_argument, NULL, ARG_FUZZ },
1346 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
dad29dff 1347 { "man", optional_argument, NULL, ARG_MAN },
8fe12d88
LP
1348 { "host", required_argument, NULL, 'H' },
1349 { "machine", required_argument, NULL, 'M' },
eb9da376 1350 {}
2265fbf7
SP
1351 };
1352
eb9da376
LP
1353 int r, c;
1354
2265fbf7
SP
1355 assert(argc >= 0);
1356 assert(argv);
1357
601185b4 1358 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
eb9da376 1359 switch (c) {
c170f3a4
LP
1360
1361 case 'h':
1d3bc017
ZJS
1362 help();
1363 return 0;
c170f3a4
LP
1364
1365 case ARG_VERSION:
3f6fd1ba 1366 return version();
c170f3a4
LP
1367
1368 case ARG_USER:
3cd26e7c 1369 arg_user = true;
c170f3a4
LP
1370 break;
1371
1372 case ARG_SYSTEM:
3cd26e7c 1373 arg_user = false;
c170f3a4
LP
1374 break;
1375
1376 case ARG_ORDER:
1377 arg_dot = DEP_ORDER;
1378 break;
1379
1380 case ARG_REQUIRE:
1381 arg_dot = DEP_REQUIRE;
1382 break;
1383
e55933db 1384 case ARG_DOT_FROM_PATTERN:
903a0b07
LP
1385 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1386 return log_oom();
1387
e55933db
ŁS
1388 break;
1389
1390 case ARG_DOT_TO_PATTERN:
903a0b07
LP
1391 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1392 return log_oom();
1393
e55933db
ŁS
1394 break;
1395
bb150966
HH
1396 case ARG_FUZZ:
1397 r = parse_sec(optarg, &arg_fuzz);
1398 if (r < 0)
1399 return r;
1400 break;
1401
9ea9d4cf
LP
1402 case ARG_NO_PAGER:
1403 arg_no_pager = true;
1404 break;
1405
3cd26e7c
LP
1406 case 'H':
1407 arg_transport = BUS_TRANSPORT_REMOTE;
1408 arg_host = optarg;
1409 break;
1410
1411 case 'M':
de33fc62 1412 arg_transport = BUS_TRANSPORT_MACHINE;
3cd26e7c
LP
1413 arg_host = optarg;
1414 break;
1415
dad29dff
LP
1416 case ARG_MAN:
1417 if (optarg) {
1418 r = parse_boolean(optarg);
1419 if (r < 0) {
1420 log_error("Failed to parse --man= argument.");
1421 return -EINVAL;
1422 }
1423
1424 arg_man = !!r;
1425 } else
1426 arg_man = true;
1427
1d3bc017
ZJS
1428 break;
1429
c170f3a4
LP
1430 case '?':
1431 return -EINVAL;
1432
1433 default:
1d3bc017 1434 assert_not_reached("Unhandled option code.");
2265fbf7 1435 }
eb9da376 1436
1d3bc017 1437 return 1; /* work to do */
2265fbf7
SP
1438}
1439
1440int main(int argc, char *argv[]) {
5220a6f3 1441 int r;
2265fbf7
SP
1442
1443 setlocale(LC_ALL, "");
c170f3a4 1444 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
2265fbf7
SP
1445 log_parse_environment();
1446 log_open();
1447
1448 r = parse_argv(argc, argv);
9ea9d4cf
LP
1449 if (r <= 0)
1450 goto finish;
2265fbf7 1451
5b6f6ebd 1452 if (streq_ptr(argv[optind], "verify"))
1d3bc017 1453 r = verify_units(argv+optind+1,
b2c23da8 1454 arg_user ? MANAGER_USER : MANAGER_SYSTEM,
1d3bc017
ZJS
1455 arg_man);
1456 else {
03976f7b 1457 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
1d3bc017 1458
266f3e26 1459 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1d3bc017 1460 if (r < 0) {
da927ba9 1461 log_error_errno(r, "Failed to create bus connection: %m");
1d3bc017
ZJS
1462 goto finish;
1463 }
2265fbf7 1464
1d3bc017
ZJS
1465 if (!argv[optind] || streq(argv[optind], "time"))
1466 r = analyze_time(bus);
1467 else if (streq(argv[optind], "blame"))
1468 r = analyze_blame(bus);
1469 else if (streq(argv[optind], "critical-chain"))
1470 r = analyze_critical_chain(bus, argv+optind+1);
1471 else if (streq(argv[optind], "plot"))
1472 r = analyze_plot(bus);
1473 else if (streq(argv[optind], "dot"))
1474 r = dot(bus, argv+optind+1);
1475 else if (streq(argv[optind], "dump"))
1476 r = dump(bus, argv+optind+1);
1477 else if (streq(argv[optind], "set-log-level"))
1478 r = set_log_level(bus, argv+optind+1);
2ca2a91c
LP
1479 else if (streq(argv[optind], "set-log-target"))
1480 r = set_log_target(bus, argv+optind+1);
1d3bc017
ZJS
1481 else
1482 log_error("Unknown operation '%s'.", argv[optind]);
1483 }
2265fbf7 1484
9ea9d4cf
LP
1485finish:
1486 pager_close();
1487
e55933db
ŁS
1488 strv_free(arg_dot_from_patterns);
1489 strv_free(arg_dot_to_patterns);
c170f3a4
LP
1490
1491 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2265fbf7 1492}