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