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