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