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