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