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