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