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