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