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