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