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