Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : /***
3 : Copyright © 2013 Simon Peeters
4 : ***/
5 :
6 : #include <getopt.h>
7 : #include <inttypes.h>
8 : #include <locale.h>
9 : #include <stdio.h>
10 : #include <stdlib.h>
11 : #include <unistd.h>
12 :
13 : #include "sd-bus.h"
14 :
15 : #include "alloc-util.h"
16 : #include "analyze-condition.h"
17 : #include "analyze-security.h"
18 : #include "analyze-verify.h"
19 : #include "build.h"
20 : #include "bus-error.h"
21 : #include "bus-unit-util.h"
22 : #include "bus-util.h"
23 : #include "calendarspec.h"
24 : #include "conf-files.h"
25 : #include "copy.h"
26 : #include "def.h"
27 : #include "exit-status.h"
28 : #include "fd-util.h"
29 : #include "fileio.h"
30 : #include "format-table.h"
31 : #include "glob-util.h"
32 : #include "hashmap.h"
33 : #include "locale-util.h"
34 : #include "log.h"
35 : #include "main-func.h"
36 : #include "nulstr-util.h"
37 : #include "pager.h"
38 : #include "parse-util.h"
39 : #include "path-util.h"
40 : #include "pretty-print.h"
41 : #if HAVE_SECCOMP
42 : # include "seccomp-util.h"
43 : #endif
44 : #include "sort-util.h"
45 : #include "special.h"
46 : #include "strv.h"
47 : #include "strxcpyx.h"
48 : #include "terminal-util.h"
49 : #include "time-util.h"
50 : #include "unit-name.h"
51 : #include "util.h"
52 : #include "verbs.h"
53 :
54 : #define SCALE_X (0.1 / 1000.0) /* pixels per us */
55 : #define SCALE_Y (20.0)
56 :
57 : #define svg(...) printf(__VA_ARGS__)
58 :
59 : #define svg_bar(class, x1, x2, y) \
60 : svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
61 : (class), \
62 : SCALE_X * (x1), SCALE_Y * (y), \
63 : SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
64 :
65 : #define svg_text(b, x, y, format, ...) \
66 : do { \
67 : svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
68 : svg(format, ## __VA_ARGS__); \
69 : svg("</text>\n"); \
70 : } while (false)
71 :
72 : static enum dot {
73 : DEP_ALL,
74 : DEP_ORDER,
75 : DEP_REQUIRE
76 : } arg_dot = DEP_ALL;
77 : static char **arg_dot_from_patterns = NULL;
78 : static char **arg_dot_to_patterns = NULL;
79 : static usec_t arg_fuzz = 0;
80 : static PagerFlags arg_pager_flags = 0;
81 : static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
82 : static const char *arg_host = NULL;
83 : static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
84 : static bool arg_man = true;
85 : static bool arg_generators = false;
86 : static const char *arg_root = NULL;
87 : static unsigned arg_iterations = 1;
88 :
89 4 : STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
90 4 : STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
91 :
92 : struct boot_times {
93 : usec_t firmware_time;
94 : usec_t loader_time;
95 : usec_t kernel_time;
96 : usec_t kernel_done_time;
97 : usec_t initrd_time;
98 : usec_t userspace_time;
99 : usec_t finish_time;
100 : usec_t security_start_time;
101 : usec_t security_finish_time;
102 : usec_t generators_start_time;
103 : usec_t generators_finish_time;
104 : usec_t unitsload_start_time;
105 : usec_t unitsload_finish_time;
106 : usec_t initrd_security_start_time;
107 : usec_t initrd_security_finish_time;
108 : usec_t initrd_generators_start_time;
109 : usec_t initrd_generators_finish_time;
110 : usec_t initrd_unitsload_start_time;
111 : usec_t initrd_unitsload_finish_time;
112 :
113 : /*
114 : * If we're analyzing the user instance, all timestamps will be offset
115 : * by its own start-up timestamp, which may be arbitrarily big.
116 : * With "plot", this causes arbitrarily wide output SVG files which almost
117 : * completely consist of empty space. Thus we cancel out this offset.
118 : *
119 : * This offset is subtracted from times above by acquire_boot_times(),
120 : * but it still needs to be subtracted from unit-specific timestamps
121 : * (so it is stored here for reference).
122 : */
123 : usec_t reverse_offset;
124 : };
125 :
126 : struct unit_times {
127 : bool has_data;
128 : char *name;
129 : usec_t activating;
130 : usec_t activated;
131 : usec_t deactivated;
132 : usec_t deactivating;
133 : usec_t time;
134 : };
135 :
136 : struct host_info {
137 : char *hostname;
138 : char *kernel_name;
139 : char *kernel_release;
140 : char *kernel_version;
141 : char *os_pretty_name;
142 : char *virtualization;
143 : char *architecture;
144 : };
145 :
146 0 : static int acquire_bus(sd_bus **bus, bool *use_full_bus) {
147 0 : bool user = arg_scope != UNIT_FILE_SYSTEM;
148 : int r;
149 :
150 0 : if (use_full_bus && *use_full_bus) {
151 0 : r = bus_connect_transport(arg_transport, arg_host, user, bus);
152 0 : if (IN_SET(r, 0, -EHOSTDOWN))
153 0 : return r;
154 :
155 0 : *use_full_bus = false;
156 : }
157 :
158 0 : return bus_connect_transport_systemd(arg_transport, arg_host, user, bus);
159 : }
160 :
161 0 : static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
162 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
163 : int r;
164 :
165 0 : assert(bus);
166 0 : assert(path);
167 0 : assert(interface);
168 0 : assert(property);
169 0 : assert(val);
170 :
171 0 : r = sd_bus_get_property_trivial(
172 : bus,
173 : "org.freedesktop.systemd1",
174 : path,
175 : interface,
176 : property,
177 : &error,
178 : 't', val);
179 :
180 0 : if (r < 0)
181 0 : return log_error_errno(r, "Failed to parse reply: %s", bus_error_message(&error, r));
182 :
183 0 : return 0;
184 : }
185 :
186 0 : static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
187 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
188 : int r;
189 :
190 0 : assert(bus);
191 0 : assert(path);
192 0 : assert(property);
193 0 : assert(strv);
194 :
195 0 : r = sd_bus_get_property_strv(
196 : bus,
197 : "org.freedesktop.systemd1",
198 : path,
199 : "org.freedesktop.systemd1.Unit",
200 : property,
201 : &error,
202 : strv);
203 0 : if (r < 0)
204 0 : return log_error_errno(r, "Failed to get unit property %s: %s", property, bus_error_message(&error, r));
205 :
206 0 : return 0;
207 : }
208 :
209 0 : static int compare_unit_start(const struct unit_times *a, const struct unit_times *b) {
210 0 : return CMP(a->activating, b->activating);
211 : }
212 :
213 0 : static void unit_times_free(struct unit_times *t) {
214 : struct unit_times *p;
215 :
216 0 : for (p = t; p->has_data; p++)
217 0 : free(p->name);
218 0 : free(t);
219 0 : }
220 :
221 0 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct unit_times *, unit_times_free);
222 :
223 0 : static void subtract_timestamp(usec_t *a, usec_t b) {
224 0 : assert(a);
225 :
226 0 : if (*a > 0) {
227 0 : assert(*a >= b);
228 0 : *a -= b;
229 : }
230 0 : }
231 :
232 0 : static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
233 : static const struct bus_properties_map property_map[] = {
234 : { "FirmwareTimestampMonotonic", "t", NULL, offsetof(struct boot_times, firmware_time) },
235 : { "LoaderTimestampMonotonic", "t", NULL, offsetof(struct boot_times, loader_time) },
236 : { "KernelTimestamp", "t", NULL, offsetof(struct boot_times, kernel_time) },
237 : { "InitRDTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_time) },
238 : { "UserspaceTimestampMonotonic", "t", NULL, offsetof(struct boot_times, userspace_time) },
239 : { "FinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, finish_time) },
240 : { "SecurityStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, security_start_time) },
241 : { "SecurityFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, security_finish_time) },
242 : { "GeneratorsStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, generators_start_time) },
243 : { "GeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, generators_finish_time) },
244 : { "UnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, unitsload_start_time) },
245 : { "UnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, unitsload_finish_time) },
246 : { "InitRDSecurityStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_security_start_time) },
247 : { "InitRDSecurityFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_security_finish_time) },
248 : { "InitRDGeneratorsStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_generators_start_time) },
249 : { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_generators_finish_time) },
250 : { "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_unitsload_start_time) },
251 : { "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_unitsload_finish_time) },
252 : {},
253 : };
254 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
255 : static struct boot_times times;
256 : static bool cached = false;
257 : int r;
258 :
259 0 : if (cached)
260 0 : goto finish;
261 :
262 : assert_cc(sizeof(usec_t) == sizeof(uint64_t));
263 :
264 0 : r = bus_map_all_properties(
265 : bus,
266 : "org.freedesktop.systemd1",
267 : "/org/freedesktop/systemd1",
268 : property_map,
269 : BUS_MAP_STRDUP,
270 : &error,
271 : NULL,
272 : ×);
273 0 : if (r < 0)
274 0 : return log_error_errno(r, "Failed to get timestamp properties: %s", bus_error_message(&error, r));
275 :
276 0 : if (times.finish_time <= 0)
277 0 : return log_error_errno(SYNTHETIC_ERRNO(EINPROGRESS),
278 : "Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n"
279 : "Please try again later.\n"
280 : "Hint: Use 'systemctl%s list-jobs' to see active jobs",
281 : times.finish_time,
282 : arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
283 :
284 0 : if (arg_scope == UNIT_FILE_SYSTEM && times.security_start_time > 0) {
285 : /* security_start_time is set when systemd is not running under container environment. */
286 0 : if (times.initrd_time > 0)
287 0 : times.kernel_done_time = times.initrd_time;
288 : else
289 0 : times.kernel_done_time = times.userspace_time;
290 : } else {
291 : /*
292 : * User-instance-specific or container-system-specific timestamps processing
293 : * (see comment to reverse_offset in struct boot_times).
294 : */
295 0 : times.reverse_offset = times.userspace_time;
296 :
297 0 : times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time =
298 0 : times.userspace_time = times.security_start_time = times.security_finish_time = 0;
299 :
300 0 : subtract_timestamp(×.finish_time, times.reverse_offset);
301 :
302 0 : subtract_timestamp(×.generators_start_time, times.reverse_offset);
303 0 : subtract_timestamp(×.generators_finish_time, times.reverse_offset);
304 :
305 0 : subtract_timestamp(×.unitsload_start_time, times.reverse_offset);
306 0 : subtract_timestamp(×.unitsload_finish_time, times.reverse_offset);
307 : }
308 :
309 0 : cached = true;
310 :
311 0 : finish:
312 0 : *bt = ×
313 0 : return 0;
314 : }
315 :
316 0 : static void free_host_info(struct host_info *hi) {
317 0 : if (!hi)
318 0 : return;
319 :
320 0 : free(hi->hostname);
321 0 : free(hi->kernel_name);
322 0 : free(hi->kernel_release);
323 0 : free(hi->kernel_version);
324 0 : free(hi->os_pretty_name);
325 0 : free(hi->virtualization);
326 0 : free(hi->architecture);
327 0 : free(hi);
328 : }
329 :
330 0 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info *, free_host_info);
331 :
332 0 : static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
333 : static const struct bus_properties_map property_map[] = {
334 : { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(struct unit_times, activating) },
335 : { "ActiveEnterTimestampMonotonic", "t", NULL, offsetof(struct unit_times, activated) },
336 : { "ActiveExitTimestampMonotonic", "t", NULL, offsetof(struct unit_times, deactivating) },
337 : { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(struct unit_times, deactivated) },
338 : {},
339 : };
340 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
341 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
342 0 : _cleanup_(unit_times_freep) struct unit_times *unit_times = NULL;
343 0 : struct boot_times *boot_times = NULL;
344 0 : size_t allocated = 0, c = 0;
345 : UnitInfo u;
346 : int r;
347 :
348 0 : r = acquire_boot_times(bus, &boot_times);
349 0 : if (r < 0)
350 0 : return r;
351 :
352 0 : r = sd_bus_call_method(
353 : bus,
354 : "org.freedesktop.systemd1",
355 : "/org/freedesktop/systemd1",
356 : "org.freedesktop.systemd1.Manager",
357 : "ListUnits",
358 : &error, &reply,
359 : NULL);
360 0 : if (r < 0)
361 0 : return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
362 :
363 0 : r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
364 0 : if (r < 0)
365 0 : return bus_log_parse_error(r);
366 :
367 0 : while ((r = bus_parse_unit_info(reply, &u)) > 0) {
368 : struct unit_times *t;
369 :
370 0 : if (!GREEDY_REALLOC(unit_times, allocated, c + 2))
371 0 : return log_oom();
372 :
373 0 : unit_times[c + 1].has_data = false;
374 0 : t = &unit_times[c];
375 0 : t->name = NULL;
376 :
377 : assert_cc(sizeof(usec_t) == sizeof(uint64_t));
378 :
379 0 : r = bus_map_all_properties(
380 : bus,
381 : "org.freedesktop.systemd1",
382 : u.unit_path,
383 : property_map,
384 : BUS_MAP_STRDUP,
385 : &error,
386 : NULL,
387 : t);
388 0 : if (r < 0)
389 0 : return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s",
390 : u.id, bus_error_message(&error, r));
391 :
392 0 : subtract_timestamp(&t->activating, boot_times->reverse_offset);
393 0 : subtract_timestamp(&t->activated, boot_times->reverse_offset);
394 0 : subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
395 0 : subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
396 :
397 0 : if (t->activated >= t->activating)
398 0 : t->time = t->activated - t->activating;
399 0 : else if (t->deactivated >= t->activating)
400 0 : t->time = t->deactivated - t->activating;
401 : else
402 0 : t->time = 0;
403 :
404 0 : if (t->activating == 0)
405 0 : continue;
406 :
407 0 : t->name = strdup(u.id);
408 0 : if (!t->name)
409 0 : return log_oom();
410 :
411 0 : t->has_data = true;
412 0 : c++;
413 : }
414 0 : if (r < 0)
415 0 : return bus_log_parse_error(r);
416 :
417 0 : *out = TAKE_PTR(unit_times);
418 0 : return c;
419 : }
420 :
421 0 : static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
422 : static const struct bus_properties_map hostname_map[] = {
423 : { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
424 : { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
425 : { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
426 : { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
427 : { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
428 : {}
429 : };
430 :
431 : static const struct bus_properties_map manager_map[] = {
432 : { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
433 : { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
434 : {}
435 : };
436 :
437 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
438 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL;
439 0 : _cleanup_(free_host_infop) struct host_info *host;
440 : int r;
441 :
442 0 : host = new0(struct host_info, 1);
443 0 : if (!host)
444 0 : return log_oom();
445 :
446 0 : if (arg_scope != UNIT_FILE_SYSTEM) {
447 0 : r = bus_connect_transport(arg_transport, arg_host, false, &system_bus);
448 0 : if (r < 0) {
449 0 : log_debug_errno(r, "Failed to connect to system bus, ignoring: %m");
450 0 : goto manager;
451 : }
452 : }
453 :
454 0 : r = bus_map_all_properties(
455 0 : system_bus ?: bus,
456 : "org.freedesktop.hostname1",
457 : "/org/freedesktop/hostname1",
458 : hostname_map,
459 : BUS_MAP_STRDUP,
460 : &error,
461 : NULL,
462 : host);
463 0 : if (r < 0) {
464 0 : log_debug_errno(r, "Failed to get host information from systemd-hostnamed, ignoring: %s",
465 : bus_error_message(&error, r));
466 0 : sd_bus_error_free(&error);
467 : }
468 :
469 0 : manager:
470 0 : r = bus_map_all_properties(
471 : bus,
472 : "org.freedesktop.systemd1",
473 : "/org/freedesktop/systemd1",
474 : manager_map,
475 : BUS_MAP_STRDUP,
476 : &error,
477 : NULL,
478 : host);
479 0 : if (r < 0)
480 0 : return log_error_errno(r, "Failed to get host information from systemd: %s",
481 : bus_error_message(&error, r));
482 :
483 0 : *hi = TAKE_PTR(host);
484 0 : return 0;
485 : }
486 :
487 0 : static int pretty_boot_time(sd_bus *bus, char **_buf) {
488 : char ts[FORMAT_TIMESPAN_MAX];
489 : struct boot_times *t;
490 : static char buf[4096];
491 : size_t size;
492 : char *ptr;
493 : int r;
494 0 : usec_t activated_time = USEC_INFINITY;
495 0 : _cleanup_free_ char *path = NULL, *unit_id = NULL;
496 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
497 :
498 0 : r = acquire_boot_times(bus, &t);
499 0 : if (r < 0)
500 0 : return r;
501 :
502 0 : path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
503 0 : if (!path)
504 0 : return log_oom();
505 :
506 0 : r = sd_bus_get_property_string(
507 : bus,
508 : "org.freedesktop.systemd1",
509 : path,
510 : "org.freedesktop.systemd1.Unit",
511 : "Id",
512 : &error,
513 : &unit_id);
514 0 : if (r < 0) {
515 0 : log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
516 0 : unit_id = NULL;
517 : }
518 :
519 0 : r = bus_get_uint64_property(bus, path,
520 : "org.freedesktop.systemd1.Unit",
521 : "ActiveEnterTimestampMonotonic",
522 : &activated_time);
523 0 : if (r < 0) {
524 0 : log_info_errno(r, "Could not get time to reach default.target, ignoring: %m");
525 0 : activated_time = USEC_INFINITY;
526 : }
527 :
528 0 : ptr = buf;
529 0 : size = sizeof(buf);
530 :
531 0 : size = strpcpyf(&ptr, size, "Startup finished in ");
532 0 : if (t->firmware_time > 0)
533 0 : size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
534 0 : if (t->loader_time > 0)
535 0 : size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
536 0 : if (t->kernel_done_time > 0)
537 0 : size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
538 0 : if (t->initrd_time > 0)
539 0 : size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
540 :
541 0 : size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
542 0 : if (t->kernel_done_time > 0)
543 0 : strpcpyf(&ptr, size, "= %s ", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
544 :
545 0 : if (unit_id && timestamp_is_set(activated_time)) {
546 0 : usec_t base = t->userspace_time > 0 ? t->userspace_time : t->reverse_offset;
547 :
548 0 : size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id,
549 : format_timespan(ts, sizeof(ts), activated_time - base, USEC_PER_MSEC));
550 0 : } else if (unit_id && activated_time == 0)
551 0 : size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id);
552 0 : else if (unit_id && activated_time == USEC_INFINITY)
553 0 : size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.", unit_id);
554 0 : else if (!unit_id)
555 0 : size = strpcpyf(&ptr, size, "\ncould not find default.target");
556 :
557 0 : ptr = strdup(buf);
558 0 : if (!ptr)
559 0 : return log_oom();
560 :
561 0 : *_buf = ptr;
562 0 : return 0;
563 : }
564 :
565 0 : static void svg_graph_box(double height, double begin, double end) {
566 : long long i;
567 :
568 : /* outside box, fill */
569 0 : svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
570 : SCALE_X * (end - begin),
571 : SCALE_Y * height);
572 :
573 0 : for (i = ((long long) (begin / 100000)) * 100000; i <= end; i += 100000) {
574 : /* lines for each second */
575 0 : if (i % 5000000 == 0)
576 0 : svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
577 : " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
578 : SCALE_X * i,
579 : SCALE_X * i,
580 : SCALE_Y * height,
581 : SCALE_X * i,
582 : -5.0,
583 : 0.000001 * i);
584 0 : else if (i % 1000000 == 0)
585 0 : svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
586 : " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
587 : SCALE_X * i,
588 : SCALE_X * i,
589 : SCALE_Y * height,
590 : SCALE_X * i,
591 : -5.0,
592 : 0.000001 * i);
593 : else
594 0 : svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
595 : SCALE_X * i,
596 : SCALE_X * i,
597 : SCALE_Y * height);
598 : }
599 0 : }
600 :
601 0 : static int plot_unit_times(struct unit_times *u, double width, int y) {
602 : char ts[FORMAT_TIMESPAN_MAX];
603 : bool b;
604 :
605 0 : if (!u->name)
606 0 : return 0;
607 :
608 0 : svg_bar("activating", u->activating, u->activated, y);
609 0 : svg_bar("active", u->activated, u->deactivating, y);
610 0 : svg_bar("deactivating", u->deactivating, u->deactivated, y);
611 :
612 : /* place the text on the left if we have passed the half of the svg width */
613 0 : b = u->activating * SCALE_X < width / 2;
614 0 : if (u->time)
615 0 : svg_text(b, u->activating, y, "%s (%s)",
616 : u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
617 : else
618 0 : svg_text(b, u->activating, y, "%s", u->name);
619 :
620 0 : return 1;
621 : }
622 :
623 0 : static int analyze_plot(int argc, char *argv[], void *userdata) {
624 0 : _cleanup_(free_host_infop) struct host_info *host = NULL;
625 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
626 0 : _cleanup_(unit_times_freep) struct unit_times *times = NULL;
627 0 : _cleanup_free_ char *pretty_times = NULL;
628 0 : bool use_full_bus = arg_scope == UNIT_FILE_SYSTEM;
629 : struct boot_times *boot;
630 : struct unit_times *u;
631 0 : int n, m = 1, y = 0, r;
632 : double width;
633 :
634 0 : r = acquire_bus(&bus, &use_full_bus);
635 0 : if (r < 0)
636 0 : return log_error_errno(r, "Failed to create bus connection: %m");
637 :
638 0 : n = acquire_boot_times(bus, &boot);
639 0 : if (n < 0)
640 0 : return n;
641 :
642 0 : n = pretty_boot_time(bus, &pretty_times);
643 0 : if (n < 0)
644 0 : return n;
645 :
646 0 : if (use_full_bus || arg_scope != UNIT_FILE_SYSTEM) {
647 0 : n = acquire_host_info(bus, &host);
648 0 : if (n < 0)
649 0 : return n;
650 : }
651 :
652 0 : n = acquire_time_data(bus, ×);
653 0 : if (n <= 0)
654 0 : return n;
655 :
656 0 : typesafe_qsort(times, n, compare_unit_start);
657 :
658 0 : width = SCALE_X * (boot->firmware_time + boot->finish_time);
659 0 : if (width < 800.0)
660 0 : width = 800.0;
661 :
662 0 : if (boot->firmware_time > boot->loader_time)
663 0 : m++;
664 0 : if (boot->loader_time > 0) {
665 0 : m++;
666 0 : if (width < 1000.0)
667 0 : width = 1000.0;
668 : }
669 0 : if (boot->initrd_time > 0)
670 0 : m++;
671 0 : if (boot->kernel_done_time > 0)
672 0 : m++;
673 :
674 0 : for (u = times; u->has_data; u++) {
675 : double text_start, text_width;
676 :
677 0 : if (u->activating > boot->finish_time) {
678 0 : u->name = mfree(u->name);
679 0 : continue;
680 : }
681 :
682 : /* If the text cannot fit on the left side then
683 : * increase the svg width so it fits on the right.
684 : * TODO: calculate the text width more accurately */
685 0 : text_width = 8.0 * strlen(u->name);
686 0 : text_start = (boot->firmware_time + u->activating) * SCALE_X;
687 0 : if (text_width > text_start && text_width + text_start > width)
688 0 : width = text_width + text_start;
689 :
690 0 : if (u->deactivated > u->activating &&
691 0 : u->deactivated <= boot->finish_time &&
692 0 : u->activated == 0 && u->deactivating == 0)
693 0 : u->activated = u->deactivating = u->deactivated;
694 0 : if (u->activated < u->activating || u->activated > boot->finish_time)
695 0 : u->activated = boot->finish_time;
696 0 : if (u->deactivating < u->activated || u->deactivating > boot->finish_time)
697 0 : u->deactivating = boot->finish_time;
698 0 : if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
699 0 : u->deactivated = boot->finish_time;
700 0 : m++;
701 : }
702 :
703 0 : svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
704 : "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
705 : "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
706 :
707 0 : svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
708 : "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
709 : 80.0 + width, 150.0 + (m * SCALE_Y) +
710 : 5 * SCALE_Y /* legend */);
711 :
712 : /* write some basic info as a comment, including some help */
713 0 : svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
714 : "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
715 : "<!-- that render these files properly but much slower are ImageMagick, -->\n"
716 : "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
717 : "<!-- point your browser to this file. -->\n\n"
718 : "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", GIT_VERSION);
719 :
720 : /* style sheet */
721 0 : svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
722 : " rect { stroke-width: 1; stroke-opacity: 0; }\n"
723 : " rect.background { fill: rgb(255,255,255); }\n"
724 : " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
725 : " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
726 : " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
727 : " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
728 : " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
729 : " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
730 : " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
731 : " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
732 : " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
733 : " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
734 : " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
735 : " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
736 : " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
737 : "// line.sec1 { }\n"
738 : " line.sec5 { stroke-width: 2; }\n"
739 : " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
740 : " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
741 : " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
742 : " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
743 : " text.sec { font-size: 10px; }\n"
744 : " ]]>\n </style>\n</defs>\n\n");
745 :
746 0 : svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
747 0 : svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
748 0 : if (host)
749 0 : svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
750 : isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
751 : strempty(host->hostname),
752 : strempty(host->kernel_name),
753 : strempty(host->kernel_release),
754 : strempty(host->kernel_version),
755 : strempty(host->architecture),
756 : strempty(host->virtualization));
757 :
758 0 : svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
759 0 : svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
760 :
761 0 : if (boot->firmware_time > 0) {
762 0 : svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
763 0 : svg_text(true, -(double) boot->firmware_time, y, "firmware");
764 0 : y++;
765 : }
766 0 : if (boot->loader_time > 0) {
767 0 : svg_bar("loader", -(double) boot->loader_time, 0, y);
768 0 : svg_text(true, -(double) boot->loader_time, y, "loader");
769 0 : y++;
770 : }
771 0 : if (boot->kernel_done_time > 0) {
772 0 : svg_bar("kernel", 0, boot->kernel_done_time, y);
773 0 : svg_text(true, 0, y, "kernel");
774 0 : y++;
775 : }
776 0 : if (boot->initrd_time > 0) {
777 0 : svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
778 0 : if (boot->initrd_security_start_time < boot->initrd_security_finish_time)
779 0 : svg_bar("security", boot->initrd_security_start_time, boot->initrd_security_finish_time, y);
780 0 : if (boot->initrd_generators_start_time < boot->initrd_generators_finish_time)
781 0 : svg_bar("generators", boot->initrd_generators_start_time, boot->initrd_generators_finish_time, y);
782 0 : if (boot->initrd_unitsload_start_time < boot->initrd_unitsload_finish_time)
783 0 : svg_bar("unitsload", boot->initrd_unitsload_start_time, boot->initrd_unitsload_finish_time, y);
784 0 : svg_text(true, boot->initrd_time, y, "initrd");
785 0 : y++;
786 : }
787 :
788 0 : for (u = times; u->has_data; u++) {
789 0 : if (u->activating >= boot->userspace_time)
790 0 : break;
791 :
792 0 : y += plot_unit_times(u, width, y);
793 : }
794 :
795 0 : svg_bar("active", boot->userspace_time, boot->finish_time, y);
796 0 : if (boot->security_start_time > 0)
797 0 : svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
798 0 : svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
799 0 : svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
800 0 : svg_text(true, boot->userspace_time, y, "systemd");
801 0 : y++;
802 :
803 0 : for (; u->has_data; u++)
804 0 : y += plot_unit_times(u, width, y);
805 :
806 0 : svg("</g>\n");
807 :
808 : /* Legend */
809 0 : svg("<g transform=\"translate(20,100)\">\n");
810 0 : y++;
811 0 : svg_bar("activating", 0, 300000, y);
812 0 : svg_text(true, 400000, y, "Activating");
813 0 : y++;
814 0 : svg_bar("active", 0, 300000, y);
815 0 : svg_text(true, 400000, y, "Active");
816 0 : y++;
817 0 : svg_bar("deactivating", 0, 300000, y);
818 0 : svg_text(true, 400000, y, "Deactivating");
819 0 : y++;
820 0 : if (boot->security_start_time > 0) {
821 0 : svg_bar("security", 0, 300000, y);
822 0 : svg_text(true, 400000, y, "Setting up security module");
823 0 : y++;
824 : }
825 0 : svg_bar("generators", 0, 300000, y);
826 0 : svg_text(true, 400000, y, "Generators");
827 0 : y++;
828 0 : svg_bar("unitsload", 0, 300000, y);
829 0 : svg_text(true, 400000, y, "Loading unit files");
830 0 : y++;
831 :
832 0 : svg("</g>\n\n");
833 :
834 0 : svg("</svg>\n");
835 :
836 0 : return 0;
837 : }
838 :
839 0 : static int list_dependencies_print(
840 : const char *name,
841 : unsigned level,
842 : unsigned branches,
843 : bool last,
844 : struct unit_times *times,
845 : struct boot_times *boot) {
846 :
847 : unsigned i;
848 : char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
849 :
850 0 : for (i = level; i != 0; i--)
851 0 : printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
852 :
853 0 : printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH));
854 :
855 0 : if (times) {
856 0 : if (times->time > 0)
857 0 : printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
858 0 : format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
859 : format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal());
860 0 : else if (times->activated > boot->userspace_time)
861 0 : printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
862 : else
863 0 : printf("%s", name);
864 : } else
865 0 : printf("%s", name);
866 0 : printf("\n");
867 :
868 0 : return 0;
869 : }
870 :
871 0 : static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
872 0 : _cleanup_free_ char *path = NULL;
873 :
874 0 : assert(bus);
875 0 : assert(name);
876 0 : assert(deps);
877 :
878 0 : path = unit_dbus_path_from_name(name);
879 0 : if (!path)
880 0 : return -ENOMEM;
881 :
882 0 : return bus_get_unit_property_strv(bus, path, "After", deps);
883 : }
884 :
885 : static Hashmap *unit_times_hashmap;
886 :
887 0 : static int list_dependencies_compare(char *const *a, char *const *b) {
888 0 : usec_t usa = 0, usb = 0;
889 : struct unit_times *times;
890 :
891 0 : times = hashmap_get(unit_times_hashmap, *a);
892 0 : if (times)
893 0 : usa = times->activated;
894 0 : times = hashmap_get(unit_times_hashmap, *b);
895 0 : if (times)
896 0 : usb = times->activated;
897 :
898 0 : return CMP(usb, usa);
899 : }
900 :
901 0 : static bool times_in_range(const struct unit_times *times, const struct boot_times *boot) {
902 0 : return times && times->activated > 0 && times->activated <= boot->finish_time;
903 : }
904 :
905 0 : static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) {
906 0 : _cleanup_strv_free_ char **deps = NULL;
907 : char **c;
908 0 : int r = 0;
909 0 : usec_t service_longest = 0;
910 0 : int to_print = 0;
911 : struct unit_times *times;
912 : struct boot_times *boot;
913 :
914 0 : if (strv_extend(units, name))
915 0 : return log_oom();
916 :
917 0 : r = list_dependencies_get_dependencies(bus, name, &deps);
918 0 : if (r < 0)
919 0 : return r;
920 :
921 0 : typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
922 :
923 0 : r = acquire_boot_times(bus, &boot);
924 0 : if (r < 0)
925 0 : return r;
926 :
927 0 : STRV_FOREACH(c, deps) {
928 0 : times = hashmap_get(unit_times_hashmap, *c);
929 0 : if (times_in_range(times, boot) && times->activated >= service_longest)
930 0 : service_longest = times->activated;
931 : }
932 :
933 0 : if (service_longest == 0)
934 0 : return r;
935 :
936 0 : STRV_FOREACH(c, deps) {
937 0 : times = hashmap_get(unit_times_hashmap, *c);
938 0 : if (times_in_range(times, boot) && service_longest - times->activated <= arg_fuzz)
939 0 : to_print++;
940 : }
941 :
942 0 : if (!to_print)
943 0 : return r;
944 :
945 0 : STRV_FOREACH(c, deps) {
946 0 : times = hashmap_get(unit_times_hashmap, *c);
947 0 : if (!times_in_range(times, boot) || service_longest - times->activated > arg_fuzz)
948 0 : continue;
949 :
950 0 : to_print--;
951 :
952 0 : r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
953 0 : if (r < 0)
954 0 : return r;
955 :
956 0 : if (strv_contains(*units, *c)) {
957 0 : r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
958 : true, NULL, boot);
959 0 : if (r < 0)
960 0 : return r;
961 0 : continue;
962 : }
963 :
964 0 : r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (to_print ? 1 : 0));
965 0 : if (r < 0)
966 0 : return r;
967 :
968 0 : if (to_print == 0)
969 0 : break;
970 : }
971 0 : return 0;
972 : }
973 :
974 0 : static int list_dependencies(sd_bus *bus, const char *name) {
975 0 : _cleanup_strv_free_ char **units = NULL;
976 : char ts[FORMAT_TIMESPAN_MAX];
977 : struct unit_times *times;
978 : int r;
979 : const char *id;
980 0 : _cleanup_free_ char *path = NULL;
981 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
982 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
983 : struct boot_times *boot;
984 :
985 0 : assert(bus);
986 :
987 0 : path = unit_dbus_path_from_name(name);
988 0 : if (!path)
989 0 : return -ENOMEM;
990 :
991 0 : r = sd_bus_get_property(
992 : bus,
993 : "org.freedesktop.systemd1",
994 : path,
995 : "org.freedesktop.systemd1.Unit",
996 : "Id",
997 : &error,
998 : &reply,
999 : "s");
1000 0 : if (r < 0)
1001 0 : return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1002 :
1003 0 : r = sd_bus_message_read(reply, "s", &id);
1004 0 : if (r < 0)
1005 0 : return bus_log_parse_error(r);
1006 :
1007 0 : times = hashmap_get(unit_times_hashmap, id);
1008 :
1009 0 : r = acquire_boot_times(bus, &boot);
1010 0 : if (r < 0)
1011 0 : return r;
1012 :
1013 0 : if (times) {
1014 0 : if (times->time)
1015 0 : printf("%s%s +%s%s\n", ansi_highlight_red(), id,
1016 : format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal());
1017 0 : else if (times->activated > boot->userspace_time)
1018 0 : printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
1019 : else
1020 0 : printf("%s\n", id);
1021 : }
1022 :
1023 0 : return list_dependencies_one(bus, name, 0, &units, 0);
1024 : }
1025 :
1026 0 : static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
1027 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1028 0 : _cleanup_(unit_times_freep) struct unit_times *times = NULL;
1029 : struct unit_times *u;
1030 : Hashmap *h;
1031 : int n, r;
1032 :
1033 0 : r = acquire_bus(&bus, NULL);
1034 0 : if (r < 0)
1035 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1036 :
1037 0 : n = acquire_time_data(bus, ×);
1038 0 : if (n <= 0)
1039 0 : return n;
1040 :
1041 0 : h = hashmap_new(&string_hash_ops);
1042 0 : if (!h)
1043 0 : return log_oom();
1044 :
1045 0 : for (u = times; u->has_data; u++) {
1046 0 : r = hashmap_put(h, u->name, u);
1047 0 : if (r < 0)
1048 0 : return log_error_errno(r, "Failed to add entry to hashmap: %m");
1049 : }
1050 0 : unit_times_hashmap = h;
1051 :
1052 0 : (void) pager_open(arg_pager_flags);
1053 :
1054 0 : puts("The time when unit became active or started is printed after the \"@\" character.\n"
1055 : "The time the unit took to start is printed after the \"+\" character.\n");
1056 :
1057 0 : if (argc > 1) {
1058 : char **name;
1059 0 : STRV_FOREACH(name, strv_skip(argv, 1))
1060 0 : list_dependencies(bus, *name);
1061 : } else
1062 0 : list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
1063 :
1064 0 : h = hashmap_free(h);
1065 0 : return 0;
1066 : }
1067 :
1068 0 : static int analyze_blame(int argc, char *argv[], void *userdata) {
1069 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1070 0 : _cleanup_(unit_times_freep) struct unit_times *times = NULL;
1071 0 : _cleanup_(table_unrefp) Table *table = NULL;
1072 : struct unit_times *u;
1073 : TableCell *cell;
1074 : int n, r;
1075 :
1076 0 : r = acquire_bus(&bus, NULL);
1077 0 : if (r < 0)
1078 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1079 :
1080 0 : n = acquire_time_data(bus, ×);
1081 0 : if (n <= 0)
1082 0 : return n;
1083 :
1084 0 : table = table_new("time", "unit");
1085 0 : if (!table)
1086 0 : return log_oom();
1087 :
1088 0 : table_set_header(table, false);
1089 :
1090 0 : assert_se(cell = table_get_cell(table, 0, 0));
1091 0 : r = table_set_ellipsize_percent(table, cell, 100);
1092 0 : if (r < 0)
1093 0 : return r;
1094 :
1095 0 : r = table_set_align_percent(table, cell, 100);
1096 0 : if (r < 0)
1097 0 : return r;
1098 :
1099 0 : assert_se(cell = table_get_cell(table, 0, 1));
1100 0 : r = table_set_ellipsize_percent(table, cell, 100);
1101 0 : if (r < 0)
1102 0 : return r;
1103 :
1104 0 : r = table_set_sort(table, 0, SIZE_MAX);
1105 0 : if (r < 0)
1106 0 : return r;
1107 :
1108 0 : r = table_set_reverse(table, 0, true);
1109 0 : if (r < 0)
1110 0 : return r;
1111 :
1112 0 : for (u = times; u->has_data; u++) {
1113 0 : if (u->time <= 0)
1114 0 : continue;
1115 :
1116 0 : r = table_add_cell(table, NULL, TABLE_TIMESPAN_MSEC, &u->time);
1117 0 : if (r < 0)
1118 0 : return r;
1119 :
1120 0 : r = table_add_cell(table, NULL, TABLE_STRING, u->name);
1121 0 : if (r < 0)
1122 0 : return r;
1123 : }
1124 :
1125 0 : (void) pager_open(arg_pager_flags);
1126 :
1127 0 : return table_print(table, NULL);
1128 : }
1129 :
1130 0 : static int analyze_time(int argc, char *argv[], void *userdata) {
1131 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1132 0 : _cleanup_free_ char *buf = NULL;
1133 : int r;
1134 :
1135 0 : r = acquire_bus(&bus, NULL);
1136 0 : if (r < 0)
1137 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1138 :
1139 0 : r = pretty_boot_time(bus, &buf);
1140 0 : if (r < 0)
1141 0 : return r;
1142 :
1143 0 : puts(buf);
1144 0 : return 0;
1145 : }
1146 :
1147 0 : static int graph_one_property(
1148 : sd_bus *bus,
1149 : const UnitInfo *u,
1150 : const char *prop,
1151 : const char *color,
1152 : char *patterns[],
1153 : char *from_patterns[],
1154 : char *to_patterns[]) {
1155 :
1156 0 : _cleanup_strv_free_ char **units = NULL;
1157 : char **unit;
1158 : int r;
1159 : bool match_patterns;
1160 :
1161 0 : assert(u);
1162 0 : assert(prop);
1163 0 : assert(color);
1164 :
1165 0 : match_patterns = strv_fnmatch(patterns, u->id, 0);
1166 :
1167 0 : if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id, 0))
1168 0 : return 0;
1169 :
1170 0 : r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
1171 0 : if (r < 0)
1172 0 : return r;
1173 :
1174 0 : STRV_FOREACH(unit, units) {
1175 : bool match_patterns2;
1176 :
1177 0 : match_patterns2 = strv_fnmatch(patterns, *unit, 0);
1178 :
1179 0 : if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit, 0))
1180 0 : continue;
1181 :
1182 0 : if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
1183 0 : continue;
1184 :
1185 0 : printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1186 : }
1187 :
1188 0 : return 0;
1189 : }
1190 :
1191 0 : static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
1192 : int r;
1193 :
1194 0 : assert(bus);
1195 0 : assert(u);
1196 :
1197 0 : if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
1198 0 : r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
1199 0 : if (r < 0)
1200 0 : return r;
1201 : }
1202 :
1203 0 : if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
1204 0 : r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
1205 0 : if (r < 0)
1206 0 : return r;
1207 0 : r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
1208 0 : if (r < 0)
1209 0 : return r;
1210 0 : r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
1211 0 : if (r < 0)
1212 0 : return r;
1213 0 : r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
1214 0 : if (r < 0)
1215 0 : return r;
1216 : }
1217 :
1218 0 : return 0;
1219 : }
1220 :
1221 0 : static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1222 0 : _cleanup_strv_free_ char **expanded_patterns = NULL;
1223 : char **pattern;
1224 : int r;
1225 :
1226 0 : STRV_FOREACH(pattern, patterns) {
1227 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1228 0 : _cleanup_free_ char *unit = NULL, *unit_id = NULL;
1229 :
1230 0 : if (strv_extend(&expanded_patterns, *pattern) < 0)
1231 0 : return log_oom();
1232 :
1233 0 : if (string_is_glob(*pattern))
1234 0 : continue;
1235 :
1236 0 : unit = unit_dbus_path_from_name(*pattern);
1237 0 : if (!unit)
1238 0 : return log_oom();
1239 :
1240 0 : r = sd_bus_get_property_string(
1241 : bus,
1242 : "org.freedesktop.systemd1",
1243 : unit,
1244 : "org.freedesktop.systemd1.Unit",
1245 : "Id",
1246 : &error,
1247 : &unit_id);
1248 0 : if (r < 0)
1249 0 : return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1250 :
1251 0 : if (!streq(*pattern, unit_id)) {
1252 0 : if (strv_extend(&expanded_patterns, unit_id) < 0)
1253 0 : return log_oom();
1254 : }
1255 : }
1256 :
1257 0 : *ret = expanded_patterns;
1258 0 : expanded_patterns = NULL; /* do not free */
1259 :
1260 0 : return 0;
1261 : }
1262 :
1263 0 : static int dot(int argc, char *argv[], void *userdata) {
1264 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1265 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1266 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1267 0 : _cleanup_strv_free_ char **expanded_patterns = NULL;
1268 0 : _cleanup_strv_free_ char **expanded_from_patterns = NULL;
1269 0 : _cleanup_strv_free_ char **expanded_to_patterns = NULL;
1270 : int r;
1271 : UnitInfo u;
1272 :
1273 0 : r = acquire_bus(&bus, NULL);
1274 0 : if (r < 0)
1275 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1276 :
1277 0 : r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
1278 0 : if (r < 0)
1279 0 : return r;
1280 :
1281 0 : r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1282 0 : if (r < 0)
1283 0 : return r;
1284 :
1285 0 : r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1286 0 : if (r < 0)
1287 0 : return r;
1288 :
1289 0 : r = sd_bus_call_method(
1290 : bus,
1291 : "org.freedesktop.systemd1",
1292 : "/org/freedesktop/systemd1",
1293 : "org.freedesktop.systemd1.Manager",
1294 : "ListUnits",
1295 : &error,
1296 : &reply,
1297 : "");
1298 0 : if (r < 0)
1299 0 : log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
1300 :
1301 0 : r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1302 0 : if (r < 0)
1303 0 : return bus_log_parse_error(r);
1304 :
1305 0 : printf("digraph systemd {\n");
1306 :
1307 0 : while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1308 :
1309 0 : r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1310 0 : if (r < 0)
1311 0 : return r;
1312 : }
1313 0 : if (r < 0)
1314 0 : return bus_log_parse_error(r);
1315 :
1316 0 : printf("}\n");
1317 :
1318 0 : log_info(" Color legend: black = Requires\n"
1319 : " dark blue = Requisite\n"
1320 : " dark grey = Wants\n"
1321 : " red = Conflicts\n"
1322 : " green = After\n");
1323 :
1324 0 : if (on_tty())
1325 0 : log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1326 : "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1327 :
1328 0 : return 0;
1329 : }
1330 :
1331 0 : static int dump_fallback(sd_bus *bus) {
1332 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1333 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1334 0 : const char *text = NULL;
1335 : int r;
1336 :
1337 0 : assert(bus);
1338 :
1339 0 : r = sd_bus_call_method(
1340 : bus,
1341 : "org.freedesktop.systemd1",
1342 : "/org/freedesktop/systemd1",
1343 : "org.freedesktop.systemd1.Manager",
1344 : "Dump",
1345 : &error,
1346 : &reply,
1347 : NULL);
1348 0 : if (r < 0)
1349 0 : return log_error_errno(r, "Failed to issue method call Dump: %s", bus_error_message(&error, r));
1350 :
1351 0 : r = sd_bus_message_read(reply, "s", &text);
1352 0 : if (r < 0)
1353 0 : return bus_log_parse_error(r);
1354 :
1355 0 : fputs(text, stdout);
1356 0 : return 0;
1357 : }
1358 :
1359 0 : static int dump(int argc, char *argv[], void *userdata) {
1360 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1361 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1362 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1363 0 : int fd = -1;
1364 : int r;
1365 :
1366 0 : r = acquire_bus(&bus, NULL);
1367 0 : if (r < 0)
1368 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1369 :
1370 0 : (void) pager_open(arg_pager_flags);
1371 :
1372 0 : if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD))
1373 0 : return dump_fallback(bus);
1374 :
1375 0 : r = sd_bus_call_method(
1376 : bus,
1377 : "org.freedesktop.systemd1",
1378 : "/org/freedesktop/systemd1",
1379 : "org.freedesktop.systemd1.Manager",
1380 : "DumpByFileDescriptor",
1381 : &error,
1382 : &reply,
1383 : NULL);
1384 0 : if (r < 0) {
1385 : /* fall back to Dump if DumpByFileDescriptor is not supported */
1386 0 : if (!IN_SET(r, -EACCES, -EBADR))
1387 0 : return log_error_errno(r, "Failed to issue method call DumpByFileDescriptor: %s",
1388 : bus_error_message(&error, r));
1389 :
1390 0 : return dump_fallback(bus);
1391 : }
1392 :
1393 0 : r = sd_bus_message_read(reply, "h", &fd);
1394 0 : if (r < 0)
1395 0 : return bus_log_parse_error(r);
1396 :
1397 0 : fflush(stdout);
1398 0 : return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, 0);
1399 : }
1400 :
1401 0 : static int cat_config(int argc, char *argv[], void *userdata) {
1402 : char **arg, **list;
1403 : int r;
1404 :
1405 0 : (void) pager_open(arg_pager_flags);
1406 :
1407 0 : list = strv_skip(argv, 1);
1408 0 : STRV_FOREACH(arg, list) {
1409 0 : const char *t = NULL;
1410 :
1411 0 : if (arg != list)
1412 0 : print_separator();
1413 :
1414 0 : if (path_is_absolute(*arg)) {
1415 : const char *dir;
1416 :
1417 0 : NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
1418 0 : t = path_startswith(*arg, dir);
1419 0 : if (t)
1420 0 : break;
1421 : }
1422 :
1423 0 : if (!t)
1424 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1425 : "Path %s does not start with any known prefix.", *arg);
1426 : } else
1427 0 : t = *arg;
1428 :
1429 0 : r = conf_files_cat(arg_root, t);
1430 0 : if (r < 0)
1431 0 : return r;
1432 : }
1433 :
1434 0 : return 0;
1435 : }
1436 :
1437 0 : static int set_log_level(int argc, char *argv[], void *userdata) {
1438 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1439 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1440 : int r;
1441 :
1442 0 : assert(argc == 2);
1443 0 : assert(argv);
1444 :
1445 0 : r = acquire_bus(&bus, NULL);
1446 0 : if (r < 0)
1447 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1448 :
1449 0 : r = sd_bus_set_property(
1450 : bus,
1451 : "org.freedesktop.systemd1",
1452 : "/org/freedesktop/systemd1",
1453 : "org.freedesktop.systemd1.Manager",
1454 : "LogLevel",
1455 : &error,
1456 : "s",
1457 0 : argv[1]);
1458 0 : if (r < 0)
1459 0 : return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1460 :
1461 0 : return 0;
1462 : }
1463 :
1464 0 : static int get_log_level(int argc, char *argv[], void *userdata) {
1465 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1466 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1467 0 : _cleanup_free_ char *level = NULL;
1468 : int r;
1469 :
1470 0 : r = acquire_bus(&bus, NULL);
1471 0 : if (r < 0)
1472 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1473 :
1474 0 : r = sd_bus_get_property_string(
1475 : bus,
1476 : "org.freedesktop.systemd1",
1477 : "/org/freedesktop/systemd1",
1478 : "org.freedesktop.systemd1.Manager",
1479 : "LogLevel",
1480 : &error,
1481 : &level);
1482 0 : if (r < 0)
1483 0 : return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
1484 :
1485 0 : puts(level);
1486 0 : return 0;
1487 : }
1488 :
1489 0 : static int get_or_set_log_level(int argc, char *argv[], void *userdata) {
1490 0 : return (argc == 1) ? get_log_level(argc, argv, userdata) : set_log_level(argc, argv, userdata);
1491 : }
1492 :
1493 0 : static int set_log_target(int argc, char *argv[], void *userdata) {
1494 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1495 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1496 : int r;
1497 :
1498 0 : assert(argc == 2);
1499 0 : assert(argv);
1500 :
1501 0 : r = acquire_bus(&bus, NULL);
1502 0 : if (r < 0)
1503 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1504 :
1505 0 : r = sd_bus_set_property(
1506 : bus,
1507 : "org.freedesktop.systemd1",
1508 : "/org/freedesktop/systemd1",
1509 : "org.freedesktop.systemd1.Manager",
1510 : "LogTarget",
1511 : &error,
1512 : "s",
1513 0 : argv[1]);
1514 0 : if (r < 0)
1515 0 : return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1516 :
1517 0 : return 0;
1518 : }
1519 :
1520 0 : static int get_log_target(int argc, char *argv[], void *userdata) {
1521 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1522 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1523 0 : _cleanup_free_ char *target = NULL;
1524 : int r;
1525 :
1526 0 : r = acquire_bus(&bus, NULL);
1527 0 : if (r < 0)
1528 0 : return log_error_errno(r, "Failed to create bus connection: %m");
1529 :
1530 0 : r = sd_bus_get_property_string(
1531 : bus,
1532 : "org.freedesktop.systemd1",
1533 : "/org/freedesktop/systemd1",
1534 : "org.freedesktop.systemd1.Manager",
1535 : "LogTarget",
1536 : &error,
1537 : &target);
1538 0 : if (r < 0)
1539 0 : return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
1540 :
1541 0 : puts(target);
1542 0 : return 0;
1543 : }
1544 :
1545 0 : static int get_or_set_log_target(int argc, char *argv[], void *userdata) {
1546 0 : return (argc == 1) ? get_log_target(argc, argv, userdata) : set_log_target(argc, argv, userdata);
1547 : }
1548 :
1549 0 : static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int flags) {
1550 : char **s;
1551 0 : STRV_FOREACH(s, strv)
1552 0 : if (strv_fnmatch_or_empty(patterns, *s, flags))
1553 0 : return true;
1554 :
1555 0 : return false;
1556 : }
1557 :
1558 0 : static int do_unit_files(int argc, char *argv[], void *userdata) {
1559 0 : _cleanup_(lookup_paths_free) LookupPaths lp = {};
1560 0 : _cleanup_hashmap_free_ Hashmap *unit_ids = NULL;
1561 0 : _cleanup_hashmap_free_ Hashmap *unit_names = NULL;
1562 0 : char **patterns = strv_skip(argv, 1);
1563 : Iterator i;
1564 : const char *k, *dst;
1565 : char **v;
1566 : int r;
1567 :
1568 0 : r = lookup_paths_init(&lp, arg_scope, 0, NULL);
1569 0 : if (r < 0)
1570 0 : return log_error_errno(r, "lookup_paths_init() failed: %m");
1571 :
1572 0 : r = unit_file_build_name_map(&lp, NULL, &unit_ids, &unit_names, NULL);
1573 0 : if (r < 0)
1574 0 : return log_error_errno(r, "unit_file_build_name_map() failed: %m");
1575 :
1576 0 : HASHMAP_FOREACH_KEY(dst, k, unit_ids, i) {
1577 0 : if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
1578 0 : !strv_fnmatch_or_empty(patterns, dst, FNM_NOESCAPE))
1579 0 : continue;
1580 :
1581 0 : printf("ids: %s → %s\n", k, dst);
1582 : }
1583 :
1584 0 : HASHMAP_FOREACH_KEY(v, k, unit_names, i) {
1585 0 : if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
1586 0 : !strv_fnmatch_strv_or_empty(patterns, v, FNM_NOESCAPE))
1587 0 : continue;
1588 :
1589 0 : _cleanup_free_ char *j = strv_join(v, ", ");
1590 0 : printf("aliases: %s ← %s\n", k, j);
1591 : }
1592 :
1593 0 : return 0;
1594 : }
1595 :
1596 0 : static int dump_unit_paths(int argc, char *argv[], void *userdata) {
1597 0 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
1598 : int r;
1599 : char **p;
1600 :
1601 0 : r = lookup_paths_init(&paths, arg_scope, 0, NULL);
1602 0 : if (r < 0)
1603 0 : return log_error_errno(r, "lookup_paths_init() failed: %m");
1604 :
1605 0 : STRV_FOREACH(p, paths.search_path)
1606 0 : puts(*p);
1607 :
1608 0 : return 0;
1609 : }
1610 :
1611 0 : static int dump_exit_status(int argc, char *argv[], void *userdata) {
1612 0 : _cleanup_(table_unrefp) Table *table = NULL;
1613 : int r;
1614 :
1615 0 : table = table_new("name", "status", "class");
1616 0 : if (!table)
1617 0 : return log_oom();
1618 :
1619 0 : r = table_set_align_percent(table, table_get_cell(table, 0, 1), 100);
1620 0 : if (r < 0)
1621 0 : return log_error_errno(r, "Failed to right-align status: %m");
1622 :
1623 0 : if (strv_isempty(strv_skip(argv, 1)))
1624 0 : for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++) {
1625 0 : if (!exit_status_mappings[i].name)
1626 0 : continue;
1627 :
1628 0 : r = table_add_many(table,
1629 : TABLE_STRING, exit_status_mappings[i].name,
1630 : TABLE_INT, (int) i,
1631 : TABLE_STRING, exit_status_class(i));
1632 0 : if (r < 0)
1633 0 : return r;
1634 : }
1635 : else
1636 0 : for (int i = 1; i < argc; i++) {
1637 : int status;
1638 :
1639 0 : status = exit_status_from_string(argv[i]);
1640 0 : if (status < 0)
1641 0 : return log_error_errno(r, "Invalid exit status \"%s\": %m", argv[i]);
1642 :
1643 0 : assert(status >= 0 && (size_t) status < ELEMENTSOF(exit_status_mappings));
1644 0 : r = table_add_many(table,
1645 : TABLE_STRING, exit_status_mappings[status].name ?: "-",
1646 : TABLE_INT, status,
1647 : TABLE_STRING, exit_status_class(status) ?: "-");
1648 0 : if (r < 0)
1649 0 : return r;
1650 : }
1651 :
1652 0 : (void) pager_open(arg_pager_flags);
1653 :
1654 0 : return table_print(table, NULL);
1655 : }
1656 :
1657 : #if HAVE_SECCOMP
1658 :
1659 0 : static int load_kernel_syscalls(Set **ret) {
1660 0 : _cleanup_(set_free_freep) Set *syscalls = NULL;
1661 0 : _cleanup_fclose_ FILE *f = NULL;
1662 : int r;
1663 :
1664 : /* Let's read the available system calls from the list of available tracing events. Slightly dirty,
1665 : * but good enough for analysis purposes. */
1666 :
1667 0 : f = fopen("/sys/kernel/tracing/available_events", "re");
1668 0 : if (!f) {
1669 : /* We tried the non-debugfs mount point and that didn't work. If it wasn't mounted, maybe the
1670 : * old debugfs mount point works? */
1671 0 : f = fopen("/sys/kernel/debug/tracing/available_events", "re");
1672 0 : if (!f)
1673 0 : return log_full_errno(IN_SET(errno, EPERM, EACCES, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
1674 : "Can't read open tracefs' available_events file: %m");
1675 : }
1676 :
1677 0 : for (;;) {
1678 0 : _cleanup_free_ char *line = NULL;
1679 : const char *e;
1680 :
1681 0 : r = read_line(f, LONG_LINE_MAX, &line);
1682 0 : if (r < 0)
1683 0 : return log_error_errno(r, "Failed to read system call list: %m");
1684 0 : if (r == 0)
1685 0 : break;
1686 :
1687 0 : e = startswith(line, "syscalls:sys_enter_");
1688 0 : if (!e)
1689 0 : continue;
1690 :
1691 : /* These are named differently inside the kernel than their external name for historical
1692 : * reasons. Let's hide them here. */
1693 0 : if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl"))
1694 0 : continue;
1695 :
1696 0 : r = set_ensure_allocated(&syscalls, &string_hash_ops);
1697 0 : if (r < 0)
1698 0 : return log_oom();
1699 :
1700 0 : r = set_put_strdup(syscalls, e);
1701 0 : if (r < 0)
1702 0 : return log_error_errno(r, "Failed to add system call to list: %m");
1703 : }
1704 :
1705 0 : *ret = TAKE_PTR(syscalls);
1706 0 : return 0;
1707 : }
1708 :
1709 0 : static void kernel_syscalls_remove(Set *s, const SyscallFilterSet *set) {
1710 : const char *syscall;
1711 :
1712 0 : NULSTR_FOREACH(syscall, set->value) {
1713 0 : if (syscall[0] == '@')
1714 0 : continue;
1715 :
1716 0 : (void) set_remove(s, syscall);
1717 : }
1718 0 : }
1719 :
1720 0 : static void dump_syscall_filter(const SyscallFilterSet *set) {
1721 : const char *syscall;
1722 :
1723 0 : printf("%s%s%s\n"
1724 : " # %s\n",
1725 : ansi_highlight(),
1726 : set->name,
1727 : ansi_normal(),
1728 : set->help);
1729 :
1730 0 : NULSTR_FOREACH(syscall, set->value)
1731 0 : printf(" %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal());
1732 0 : }
1733 :
1734 0 : static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
1735 0 : bool first = true;
1736 :
1737 0 : (void) pager_open(arg_pager_flags);
1738 :
1739 0 : if (strv_isempty(strv_skip(argv, 1))) {
1740 0 : _cleanup_(set_free_freep) Set *kernel = NULL;
1741 : int i, k;
1742 :
1743 0 : k = load_kernel_syscalls(&kernel);
1744 :
1745 0 : for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
1746 0 : const SyscallFilterSet *set = syscall_filter_sets + i;
1747 0 : if (!first)
1748 0 : puts("");
1749 :
1750 0 : dump_syscall_filter(set);
1751 0 : kernel_syscalls_remove(kernel, set);
1752 0 : first = false;
1753 : }
1754 :
1755 0 : if (k < 0) {
1756 0 : fputc('\n', stdout);
1757 0 : fflush(stdout);
1758 0 : log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
1759 0 : } else if (!set_isempty(kernel)) {
1760 : const char *syscall;
1761 : Iterator j;
1762 :
1763 0 : printf("\n"
1764 : "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n",
1765 : ansi_highlight(), ansi_normal());
1766 :
1767 0 : SET_FOREACH(syscall, kernel, j)
1768 0 : printf("# %s\n", syscall);
1769 : }
1770 : } else {
1771 : char **name;
1772 :
1773 0 : STRV_FOREACH(name, strv_skip(argv, 1)) {
1774 : const SyscallFilterSet *set;
1775 :
1776 0 : if (!first)
1777 0 : puts("");
1778 :
1779 0 : set = syscall_filter_set_find(*name);
1780 0 : if (!set) {
1781 : /* make sure the error appears below normal output */
1782 0 : fflush(stdout);
1783 :
1784 0 : return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
1785 : "Filter set \"%s\" not found.", *name);
1786 : }
1787 :
1788 0 : dump_syscall_filter(set);
1789 0 : first = false;
1790 : }
1791 : }
1792 :
1793 0 : return 0;
1794 : }
1795 :
1796 : #else
1797 : static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
1798 : return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with syscall filters, sorry.");
1799 : }
1800 : #endif
1801 :
1802 0 : static void parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) {
1803 0 : if (calendar && calendar_spec_from_string(p, NULL) >= 0)
1804 0 : log_notice("Hint: this expression is a valid calendar specification. "
1805 : "Use 'systemd-analyze calendar \"%s\"' instead?", p);
1806 0 : if (timestamp && parse_timestamp(p, NULL) >= 0)
1807 0 : log_notice("Hint: this expression is a valid timestamp. "
1808 : "Use 'systemd-analyze timestamp \"%s\"' instead?", p);
1809 0 : if (timespan && parse_time(p, NULL, USEC_PER_SEC) >= 0)
1810 0 : log_notice("Hint: this expression is a valid timespan. "
1811 : "Use 'systemd-analyze timespan \"%s\"' instead?", p);
1812 0 : }
1813 :
1814 0 : static int dump_timespan(int argc, char *argv[], void *userdata) {
1815 : char **input_timespan;
1816 :
1817 0 : STRV_FOREACH(input_timespan, strv_skip(argv, 1)) {
1818 0 : _cleanup_(table_unrefp) Table *table = NULL;
1819 : usec_t output_usecs;
1820 : TableCell *cell;
1821 : int r;
1822 :
1823 0 : r = parse_time(*input_timespan, &output_usecs, USEC_PER_SEC);
1824 0 : if (r < 0) {
1825 0 : log_error_errno(r, "Failed to parse time span '%s': %m", *input_timespan);
1826 0 : parsing_hint(*input_timespan, true, true, false);
1827 0 : return r;
1828 : }
1829 :
1830 0 : table = table_new("name", "value");
1831 0 : if (!table)
1832 0 : return log_oom();
1833 :
1834 0 : table_set_header(table, false);
1835 :
1836 0 : assert_se(cell = table_get_cell(table, 0, 0));
1837 0 : r = table_set_ellipsize_percent(table, cell, 100);
1838 0 : if (r < 0)
1839 0 : return r;
1840 :
1841 0 : r = table_set_align_percent(table, cell, 100);
1842 0 : if (r < 0)
1843 0 : return r;
1844 :
1845 0 : assert_se(cell = table_get_cell(table, 0, 1));
1846 0 : r = table_set_ellipsize_percent(table, cell, 100);
1847 0 : if (r < 0)
1848 0 : return r;
1849 :
1850 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Original:");
1851 0 : if (r < 0)
1852 0 : return r;
1853 :
1854 0 : r = table_add_cell(table, NULL, TABLE_STRING, *input_timespan);
1855 0 : if (r < 0)
1856 0 : return r;
1857 :
1858 0 : r = table_add_cell_stringf(table, NULL, "%ss:", special_glyph(SPECIAL_GLYPH_MU));
1859 0 : if (r < 0)
1860 0 : return r;
1861 :
1862 0 : r = table_add_cell(table, NULL, TABLE_UINT64, &output_usecs);
1863 0 : if (r < 0)
1864 0 : return r;
1865 :
1866 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Human:");
1867 0 : if (r < 0)
1868 0 : return r;
1869 :
1870 0 : r = table_add_cell(table, &cell, TABLE_TIMESPAN, &output_usecs);
1871 0 : if (r < 0)
1872 0 : return r;
1873 :
1874 0 : r = table_set_color(table, cell, ansi_highlight());
1875 0 : if (r < 0)
1876 0 : return r;
1877 :
1878 0 : r = table_print(table, NULL);
1879 0 : if (r < 0)
1880 0 : return r;
1881 :
1882 0 : if (input_timespan[1])
1883 0 : putchar('\n');
1884 : }
1885 :
1886 0 : return EXIT_SUCCESS;
1887 : }
1888 :
1889 0 : static int test_timestamp_one(const char *p) {
1890 0 : _cleanup_(table_unrefp) Table *table = NULL;
1891 : TableCell *cell;
1892 : usec_t usec;
1893 : int r;
1894 :
1895 0 : r = parse_timestamp(p, &usec);
1896 0 : if (r < 0) {
1897 0 : log_error_errno(r, "Failed to parse \"%s\": %m", p);
1898 0 : parsing_hint(p, true, false, true);
1899 0 : return r;
1900 : }
1901 :
1902 0 : table = table_new("name", "value");
1903 0 : if (!table)
1904 0 : return log_oom();
1905 :
1906 0 : table_set_header(table, false);
1907 :
1908 0 : assert_se(cell = table_get_cell(table, 0, 0));
1909 0 : r = table_set_ellipsize_percent(table, cell, 100);
1910 0 : if (r < 0)
1911 0 : return r;
1912 :
1913 0 : r = table_set_align_percent(table, cell, 100);
1914 0 : if (r < 0)
1915 0 : return r;
1916 :
1917 0 : assert_se(cell = table_get_cell(table, 0, 1));
1918 0 : r = table_set_ellipsize_percent(table, cell, 100);
1919 0 : if (r < 0)
1920 0 : return r;
1921 :
1922 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Original form:");
1923 0 : if (r < 0)
1924 0 : return r;
1925 :
1926 0 : r = table_add_cell(table, NULL, TABLE_STRING, p);
1927 0 : if (r < 0)
1928 0 : return r;
1929 :
1930 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Normalized form:");
1931 0 : if (r < 0)
1932 0 : return r;
1933 :
1934 0 : r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &usec);
1935 0 : if (r < 0)
1936 0 : return r;
1937 :
1938 0 : r = table_set_color(table, cell, ansi_highlight_blue());
1939 0 : if (r < 0)
1940 0 : return r;
1941 :
1942 0 : if (!in_utc_timezone()) {
1943 0 : r = table_add_cell(table, NULL, TABLE_STRING, "(in UTC):");
1944 0 : if (r < 0)
1945 0 : return r;
1946 :
1947 0 : r = table_add_cell(table, &cell, TABLE_TIMESTAMP_UTC, &usec);
1948 0 : if (r < 0)
1949 0 : return r;
1950 : }
1951 :
1952 0 : r = table_add_cell(table, NULL, TABLE_STRING, "UNIX seconds:");
1953 0 : if (r < 0)
1954 0 : return r;
1955 :
1956 0 : if (usec % USEC_PER_SEC == 0)
1957 0 : r = table_add_cell_stringf(table, &cell, "@%"PRI_USEC,
1958 : usec / USEC_PER_SEC);
1959 : else
1960 0 : r = table_add_cell_stringf(table, &cell, "@%"PRI_USEC".%06"PRI_USEC"",
1961 : usec / USEC_PER_SEC,
1962 : usec % USEC_PER_SEC);
1963 0 : if (r < 0)
1964 0 : return r;
1965 :
1966 0 : r = table_add_cell(table, NULL, TABLE_STRING, "From now:");
1967 0 : if (r < 0)
1968 0 : return r;
1969 :
1970 0 : r = table_add_cell(table, &cell, TABLE_TIMESTAMP_RELATIVE, &usec);
1971 0 : if (r < 0)
1972 0 : return r;
1973 :
1974 0 : return table_print(table, NULL);
1975 : }
1976 :
1977 0 : static int test_timestamp(int argc, char *argv[], void *userdata) {
1978 0 : int ret = 0, r;
1979 : char **p;
1980 :
1981 0 : STRV_FOREACH(p, strv_skip(argv, 1)) {
1982 0 : r = test_timestamp_one(*p);
1983 0 : if (ret == 0 && r < 0)
1984 0 : ret = r;
1985 :
1986 0 : if (*(p + 1))
1987 0 : putchar('\n');
1988 : }
1989 :
1990 0 : return ret;
1991 : }
1992 :
1993 0 : static int test_calendar_one(usec_t n, const char *p) {
1994 0 : _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
1995 0 : _cleanup_(table_unrefp) Table *table = NULL;
1996 0 : _cleanup_free_ char *t = NULL;
1997 : TableCell *cell;
1998 : int r;
1999 :
2000 0 : r = calendar_spec_from_string(p, &spec);
2001 0 : if (r < 0) {
2002 0 : log_error_errno(r, "Failed to parse calendar specification '%s': %m", p);
2003 0 : parsing_hint(p, false, true, true);
2004 0 : return r;
2005 : }
2006 :
2007 0 : r = calendar_spec_to_string(spec, &t);
2008 0 : if (r < 0)
2009 0 : return log_error_errno(r, "Failed to format calendar specification '%s': %m", p);
2010 :
2011 0 : table = table_new("name", "value");
2012 0 : if (!table)
2013 0 : return log_oom();
2014 :
2015 0 : table_set_header(table, false);
2016 :
2017 0 : assert_se(cell = table_get_cell(table, 0, 0));
2018 0 : r = table_set_ellipsize_percent(table, cell, 100);
2019 0 : if (r < 0)
2020 0 : return r;
2021 :
2022 0 : r = table_set_align_percent(table, cell, 100);
2023 0 : if (r < 0)
2024 0 : return r;
2025 :
2026 0 : assert_se(cell = table_get_cell(table, 0, 1));
2027 0 : r = table_set_ellipsize_percent(table, cell, 100);
2028 0 : if (r < 0)
2029 0 : return r;
2030 :
2031 0 : if (!streq(t, p)) {
2032 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Original form:");
2033 0 : if (r < 0)
2034 0 : return r;
2035 :
2036 0 : r = table_add_cell(table, NULL, TABLE_STRING, p);
2037 0 : if (r < 0)
2038 0 : return r;
2039 : }
2040 :
2041 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Normalized form:");
2042 0 : if (r < 0)
2043 0 : return r;
2044 :
2045 0 : r = table_add_cell(table, NULL, TABLE_STRING, t);
2046 0 : if (r < 0)
2047 0 : return r;
2048 :
2049 0 : for (unsigned i = 0; i < arg_iterations; i++) {
2050 : usec_t next;
2051 :
2052 0 : r = calendar_spec_next_usec(spec, n, &next);
2053 0 : if (r == -ENOENT) {
2054 0 : if (i == 0) {
2055 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Next elapse:");
2056 0 : if (r < 0)
2057 0 : return r;
2058 :
2059 0 : r = table_add_cell(table, &cell, TABLE_STRING, "never");
2060 0 : if (r < 0)
2061 0 : return r;
2062 :
2063 0 : r = table_set_color(table, cell, ansi_highlight_yellow());
2064 0 : if (r < 0)
2065 0 : return r;
2066 : }
2067 0 : break;
2068 : }
2069 0 : if (r < 0)
2070 0 : return log_error_errno(r, "Failed to determine next elapse for '%s': %m", p);
2071 :
2072 0 : if (i == 0) {
2073 0 : r = table_add_cell(table, NULL, TABLE_STRING, "Next elapse:");
2074 0 : if (r < 0)
2075 0 : return r;
2076 :
2077 0 : r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &next);
2078 0 : if (r < 0)
2079 0 : return r;
2080 :
2081 0 : r = table_set_color(table, cell, ansi_highlight_blue());
2082 0 : if (r < 0)
2083 0 : return r;
2084 : } else {
2085 0 : int k = DECIMAL_STR_WIDTH(i + 1);
2086 :
2087 0 : if (k < 8)
2088 0 : k = 8 - k;
2089 : else
2090 0 : k = 0;
2091 :
2092 0 : r = table_add_cell_stringf(table, NULL, "Iter. #%u:", i+1);
2093 0 : if (r < 0)
2094 0 : return r;
2095 :
2096 0 : r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &next);
2097 0 : if (r < 0)
2098 0 : return r;
2099 :
2100 0 : r = table_set_color(table, cell, ansi_highlight_blue());
2101 0 : if (r < 0)
2102 0 : return r;
2103 : }
2104 :
2105 0 : if (!in_utc_timezone()) {
2106 0 : r = table_add_cell(table, NULL, TABLE_STRING, "(in UTC):");
2107 0 : if (r < 0)
2108 0 : return r;
2109 :
2110 0 : r = table_add_cell(table, NULL, TABLE_TIMESTAMP_UTC, &next);
2111 0 : if (r < 0)
2112 0 : return r;
2113 : }
2114 :
2115 0 : r = table_add_cell(table, NULL, TABLE_STRING, "From now:");
2116 0 : if (r < 0)
2117 0 : return r;
2118 :
2119 0 : r = table_add_cell(table, NULL, TABLE_TIMESTAMP_RELATIVE, &next);
2120 0 : if (r < 0)
2121 0 : return r;
2122 :
2123 0 : n = next;
2124 : }
2125 :
2126 0 : return table_print(table, NULL);
2127 : }
2128 :
2129 0 : static int test_calendar(int argc, char *argv[], void *userdata) {
2130 0 : int ret = 0, r;
2131 : char **p;
2132 : usec_t n;
2133 :
2134 0 : n = now(CLOCK_REALTIME); /* We want to use the same "base" for all expressions */
2135 :
2136 0 : STRV_FOREACH(p, strv_skip(argv, 1)) {
2137 0 : r = test_calendar_one(n, *p);
2138 0 : if (ret == 0 && r < 0)
2139 0 : ret = r;
2140 :
2141 0 : if (*(p + 1))
2142 0 : putchar('\n');
2143 : }
2144 :
2145 0 : return ret;
2146 : }
2147 :
2148 0 : static int service_watchdogs(int argc, char *argv[], void *userdata) {
2149 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2150 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2151 : int b, r;
2152 :
2153 0 : assert(IN_SET(argc, 1, 2));
2154 0 : assert(argv);
2155 :
2156 0 : r = acquire_bus(&bus, NULL);
2157 0 : if (r < 0)
2158 0 : return log_error_errno(r, "Failed to create bus connection: %m");
2159 :
2160 : /* get ServiceWatchdogs */
2161 0 : if (argc == 1) {
2162 0 : r = sd_bus_get_property_trivial(
2163 : bus,
2164 : "org.freedesktop.systemd1",
2165 : "/org/freedesktop/systemd1",
2166 : "org.freedesktop.systemd1.Manager",
2167 : "ServiceWatchdogs",
2168 : &error,
2169 : 'b',
2170 : &b);
2171 0 : if (r < 0)
2172 0 : return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
2173 :
2174 0 : printf("%s\n", yes_no(!!b));
2175 :
2176 0 : return 0;
2177 : }
2178 :
2179 : /* set ServiceWatchdogs */
2180 0 : b = parse_boolean(argv[1]);
2181 0 : if (b < 0) {
2182 0 : log_error("Failed to parse service-watchdogs argument.");
2183 0 : return -EINVAL;
2184 : }
2185 :
2186 0 : r = sd_bus_set_property(
2187 : bus,
2188 : "org.freedesktop.systemd1",
2189 : "/org/freedesktop/systemd1",
2190 : "org.freedesktop.systemd1.Manager",
2191 : "ServiceWatchdogs",
2192 : &error,
2193 : "b",
2194 : b);
2195 0 : if (r < 0)
2196 0 : return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
2197 :
2198 0 : return 0;
2199 : }
2200 :
2201 0 : static int do_condition(int argc, char *argv[], void *userdata) {
2202 0 : return verify_conditions(strv_skip(argv, 1), arg_scope);
2203 : }
2204 :
2205 0 : static int do_verify(int argc, char *argv[], void *userdata) {
2206 0 : return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
2207 : }
2208 :
2209 0 : static int do_security(int argc, char *argv[], void *userdata) {
2210 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2211 : int r;
2212 :
2213 0 : r = acquire_bus(&bus, NULL);
2214 0 : if (r < 0)
2215 0 : return log_error_errno(r, "Failed to create bus connection: %m");
2216 :
2217 0 : (void) pager_open(arg_pager_flags);
2218 :
2219 0 : return analyze_security(bus, strv_skip(argv, 1), 0);
2220 : }
2221 :
2222 3 : static int help(int argc, char *argv[], void *userdata) {
2223 3 : _cleanup_free_ char *link = NULL, *dot_link = NULL;
2224 : int r;
2225 :
2226 3 : (void) pager_open(arg_pager_flags);
2227 :
2228 3 : r = terminal_urlify_man("systemd-analyze", "1", &link);
2229 3 : if (r < 0)
2230 0 : return log_oom();
2231 :
2232 : /* Not using terminal_urlify_man() for this, since we don't want the "man page" text suffix in this case. */
2233 3 : r = terminal_urlify("man:dot(1)", "dot(1)", &dot_link);
2234 3 : if (r < 0)
2235 0 : return log_oom();
2236 :
2237 3 : printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2238 : "Profile systemd, show unit dependencies, check unit files.\n\n"
2239 : " -h --help Show this help\n"
2240 : " --version Show package version\n"
2241 : " --no-pager Do not pipe output into a pager\n"
2242 : " --system Operate on system systemd instance\n"
2243 : " --user Operate on user systemd instance\n"
2244 : " --global Operate on global user configuration\n"
2245 : " -H --host=[USER@]HOST Operate on remote host\n"
2246 : " -M --machine=CONTAINER Operate on local container\n"
2247 : " --order Show only order in the graph\n"
2248 : " --require Show only requirement in the graph\n"
2249 : " --from-pattern=GLOB Show only origins in the graph\n"
2250 : " --to-pattern=GLOB Show only destinations in the graph\n"
2251 : " --fuzz=SECONDS Also print services which finished SECONDS earlier\n"
2252 : " than the latest in the branch\n"
2253 : " --man[=BOOL] Do [not] check for existence of man pages\n"
2254 : " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n"
2255 : " --iterations=N Show the specified number of iterations\n"
2256 : "\nCommands:\n"
2257 : " time Print time spent in the kernel\n"
2258 : " blame Print list of running units ordered by time to init\n"
2259 : " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
2260 : " plot Output SVG graphic showing service initialization\n"
2261 : " dot [UNIT...] Output dependency graph in %s format\n"
2262 : " log-level [LEVEL] Get/set logging threshold for manager\n"
2263 : " log-target [TARGET] Get/set logging target for manager\n"
2264 : " dump Output state serialization of service manager\n"
2265 : " cat-config Show configuration file and drop-ins\n"
2266 : " unit-files List files and symlinks for units\n"
2267 : " unit-paths List load directories for units\n"
2268 : " exit-status [STATUS...] List exit status definitions\n"
2269 : " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
2270 : " condition CONDITION... Evaluate conditions and asserts\n"
2271 : " verify FILE... Check unit files for correctness\n"
2272 : " service-watchdogs [BOOL] Get/set service watchdog state\n"
2273 : " calendar SPEC... Validate repetitive calendar time events\n"
2274 : " timestamp TIMESTAMP... Validate a timestamp\n"
2275 : " timespan SPAN... Validate a time span\n"
2276 : " security [UNIT...] Analyze security of unit\n"
2277 : "\nSee the %s for details.\n"
2278 : , program_invocation_short_name
2279 : , dot_link
2280 : , link
2281 : );
2282 :
2283 : /* When updating this list, including descriptions, apply changes to
2284 : * shell-completion/bash/systemd-analyze and shell-completion/zsh/_systemd-analyze too. */
2285 :
2286 3 : return 0;
2287 : }
2288 :
2289 4 : static int parse_argv(int argc, char *argv[]) {
2290 : enum {
2291 : ARG_VERSION = 0x100,
2292 : ARG_ORDER,
2293 : ARG_REQUIRE,
2294 : ARG_ROOT,
2295 : ARG_SYSTEM,
2296 : ARG_USER,
2297 : ARG_GLOBAL,
2298 : ARG_DOT_FROM_PATTERN,
2299 : ARG_DOT_TO_PATTERN,
2300 : ARG_FUZZ,
2301 : ARG_NO_PAGER,
2302 : ARG_MAN,
2303 : ARG_GENERATORS,
2304 : ARG_ITERATIONS,
2305 : };
2306 :
2307 : static const struct option options[] = {
2308 : { "help", no_argument, NULL, 'h' },
2309 : { "version", no_argument, NULL, ARG_VERSION },
2310 : { "order", no_argument, NULL, ARG_ORDER },
2311 : { "require", no_argument, NULL, ARG_REQUIRE },
2312 : { "root", required_argument, NULL, ARG_ROOT },
2313 : { "system", no_argument, NULL, ARG_SYSTEM },
2314 : { "user", no_argument, NULL, ARG_USER },
2315 : { "global", no_argument, NULL, ARG_GLOBAL },
2316 : { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
2317 : { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
2318 : { "fuzz", required_argument, NULL, ARG_FUZZ },
2319 : { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2320 : { "man", optional_argument, NULL, ARG_MAN },
2321 : { "generators", optional_argument, NULL, ARG_GENERATORS },
2322 : { "host", required_argument, NULL, 'H' },
2323 : { "machine", required_argument, NULL, 'M' },
2324 : { "iterations", required_argument, NULL, ARG_ITERATIONS },
2325 : {}
2326 : };
2327 :
2328 : int r, c;
2329 :
2330 4 : assert(argc >= 0);
2331 4 : assert(argv);
2332 :
2333 4 : while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
2334 4 : switch (c) {
2335 :
2336 3 : case 'h':
2337 3 : return help(0, NULL, NULL);
2338 :
2339 0 : case ARG_VERSION:
2340 0 : return version();
2341 :
2342 0 : case ARG_ROOT:
2343 0 : arg_root = optarg;
2344 0 : break;
2345 :
2346 0 : case ARG_SYSTEM:
2347 0 : arg_scope = UNIT_FILE_SYSTEM;
2348 0 : break;
2349 :
2350 0 : case ARG_USER:
2351 0 : arg_scope = UNIT_FILE_USER;
2352 0 : break;
2353 :
2354 0 : case ARG_GLOBAL:
2355 0 : arg_scope = UNIT_FILE_GLOBAL;
2356 0 : break;
2357 :
2358 0 : case ARG_ORDER:
2359 0 : arg_dot = DEP_ORDER;
2360 0 : break;
2361 :
2362 0 : case ARG_REQUIRE:
2363 0 : arg_dot = DEP_REQUIRE;
2364 0 : break;
2365 :
2366 0 : case ARG_DOT_FROM_PATTERN:
2367 0 : if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
2368 0 : return log_oom();
2369 :
2370 0 : break;
2371 :
2372 0 : case ARG_DOT_TO_PATTERN:
2373 0 : if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
2374 0 : return log_oom();
2375 :
2376 0 : break;
2377 :
2378 0 : case ARG_FUZZ:
2379 0 : r = parse_sec(optarg, &arg_fuzz);
2380 0 : if (r < 0)
2381 0 : return r;
2382 0 : break;
2383 :
2384 0 : case ARG_NO_PAGER:
2385 0 : arg_pager_flags |= PAGER_DISABLE;
2386 0 : break;
2387 :
2388 0 : case 'H':
2389 0 : arg_transport = BUS_TRANSPORT_REMOTE;
2390 0 : arg_host = optarg;
2391 0 : break;
2392 :
2393 0 : case 'M':
2394 0 : arg_transport = BUS_TRANSPORT_MACHINE;
2395 0 : arg_host = optarg;
2396 0 : break;
2397 :
2398 0 : case ARG_MAN:
2399 0 : if (optarg) {
2400 0 : r = parse_boolean(optarg);
2401 0 : if (r < 0)
2402 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2403 : "Failed to parse --man= argument.");
2404 :
2405 0 : arg_man = r;
2406 : } else
2407 0 : arg_man = true;
2408 :
2409 0 : break;
2410 :
2411 0 : case ARG_GENERATORS:
2412 0 : if (optarg) {
2413 0 : r = parse_boolean(optarg);
2414 0 : if (r < 0)
2415 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2416 : "Failed to parse --generators= argument.");
2417 :
2418 0 : arg_generators = r;
2419 : } else
2420 0 : arg_generators = true;
2421 :
2422 0 : break;
2423 :
2424 0 : case ARG_ITERATIONS:
2425 0 : r = safe_atou(optarg, &arg_iterations);
2426 0 : if (r < 0)
2427 0 : return log_error_errno(r, "Failed to parse iterations: %s", optarg);
2428 :
2429 0 : break;
2430 :
2431 1 : case '?':
2432 1 : return -EINVAL;
2433 :
2434 0 : default:
2435 0 : assert_not_reached("Unhandled option code.");
2436 : }
2437 :
2438 0 : if (arg_scope == UNIT_FILE_GLOBAL &&
2439 0 : !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
2440 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2441 : "Option --global only makes sense with verbs dot, unit-paths, verify.");
2442 :
2443 0 : if (streq_ptr(argv[optind], "cat-config") && arg_scope == UNIT_FILE_USER)
2444 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2445 : "Option --user is not supported for cat-config right now.");
2446 :
2447 0 : if (arg_root && !streq_ptr(argv[optind], "cat-config"))
2448 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2449 : "Option --root is only supported for cat-config right now.");
2450 :
2451 0 : return 1; /* work to do */
2452 : }
2453 :
2454 4 : static int run(int argc, char *argv[]) {
2455 :
2456 : static const Verb verbs[] = {
2457 : { "help", VERB_ANY, VERB_ANY, 0, help },
2458 : { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time },
2459 : { "blame", VERB_ANY, 1, 0, analyze_blame },
2460 : { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain },
2461 : { "plot", VERB_ANY, 1, 0, analyze_plot },
2462 : { "dot", VERB_ANY, VERB_ANY, 0, dot },
2463 : { "log-level", VERB_ANY, 2, 0, get_or_set_log_level },
2464 : { "log-target", VERB_ANY, 2, 0, get_or_set_log_target },
2465 : /* The following four verbs are deprecated aliases */
2466 : { "set-log-level", 2, 2, 0, set_log_level },
2467 : { "get-log-level", VERB_ANY, 1, 0, get_log_level },
2468 : { "set-log-target", 2, 2, 0, set_log_target },
2469 : { "get-log-target", VERB_ANY, 1, 0, get_log_target },
2470 :
2471 : { "dump", VERB_ANY, 1, 0, dump },
2472 : { "cat-config", 2, VERB_ANY, 0, cat_config },
2473 : { "unit-files", VERB_ANY, VERB_ANY, 0, do_unit_files },
2474 : { "unit-paths", 1, 1, 0, dump_unit_paths },
2475 : { "exit-status", VERB_ANY, VERB_ANY, 0, dump_exit_status },
2476 : { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
2477 : { "condition", 2, VERB_ANY, 0, do_condition },
2478 : { "verify", 2, VERB_ANY, 0, do_verify },
2479 : { "calendar", 2, VERB_ANY, 0, test_calendar },
2480 : { "timestamp", 2, VERB_ANY, 0, test_timestamp },
2481 : { "timespan", 2, VERB_ANY, 0, dump_timespan },
2482 : { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
2483 : { "security", VERB_ANY, VERB_ANY, 0, do_security },
2484 : {}
2485 : };
2486 :
2487 : int r;
2488 :
2489 4 : setlocale(LC_ALL, "");
2490 4 : setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
2491 :
2492 4 : log_show_color(true);
2493 4 : log_parse_environment();
2494 4 : log_open();
2495 :
2496 4 : r = parse_argv(argc, argv);
2497 4 : if (r <= 0)
2498 4 : return r;
2499 :
2500 0 : return dispatch_verb(argc, argv, verbs, NULL);
2501 : }
2502 :
2503 4 : DEFINE_MAIN_FUNCTION(run);
|