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