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