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