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