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