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