]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/analyze.c
analyze: split out cat-config verb
[thirdparty/systemd.git] / src / analyze / analyze.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2265fbf7 2/***
96b2fb93 3 Copyright © 2013 Simon Peeters
2265fbf7
SP
4***/
5
2265fbf7 6#include <getopt.h>
3f1c1287 7#include <inttypes.h>
3f6fd1ba
LP
8#include <stdio.h>
9#include <stdlib.h>
ca78ad1d 10#include <unistd.h>
2265fbf7 11
048ecf5b 12#include "sd-bus.h"
3f6fd1ba 13
b5efdb8a 14#include "alloc-util.h"
30bddc06 15#include "analyze.h"
113dd9cb 16#include "analyze-blame.h"
5229b03c 17#include "analyze-calendar.h"
6488e844 18#include "analyze-capability.h"
c649b343 19#include "analyze-cat-config.h"
edfea9fe 20#include "analyze-condition.h"
cccd2af6 21#include "analyze-dot.h"
25eb70af 22#include "analyze-dump.h"
917e6554 23#include "analyze-elf.h"
73cb64c4 24#include "analyze-exit-status.h"
08e36480 25#include "analyze-filesystems.h"
ba474dad 26#include "analyze-plot.h"
ec16f3b6 27#include "analyze-security.h"
2b04e72c 28#include "analyze-service-watchdogs.h"
389638d3 29#include "analyze-syscall-filter.h"
113dd9cb 30#include "analyze-time-data.h"
30bddc06 31#include "analyze-timespan.h"
503ccaaa 32#include "analyze-timestamp.h"
3f6fd1ba 33#include "analyze-verify.h"
048ecf5b 34#include "bus-error.h"
9b71e4ab 35#include "bus-locator.h"
807542be 36#include "bus-map-properties.h"
20b16441 37#include "bus-unit-util.h"
6d86f4bd 38#include "calendarspec.h"
b2af819b
LP
39#include "cap-list.h"
40#include "capability-util.h"
854a42fb 41#include "conf-files.h"
c0a1bfac 42#include "copy.h"
90bea744 43#include "def.h"
76ed04d9 44#include "exit-status.h"
da845dab 45#include "extract-word.h"
c0a1bfac 46#include "fd-util.h"
cdf6258c 47#include "fileio.h"
b41711cd 48#include "filesystems.h"
d8bfdbe1 49#include "format-table.h"
7d50b32a 50#include "glob-util.h"
bb150966 51#include "hashmap.h"
8752c575 52#include "locale-util.h"
3f6fd1ba 53#include "log.h"
d665c7b2 54#include "main-func.h"
e5ea5c3a 55#include "mount-util.h"
d8b4d14d 56#include "nulstr-util.h"
9ea9d4cf 57#include "pager.h"
599c7c54 58#include "parse-argument.h"
6bedfcbb 59#include "parse-util.h"
854a42fb 60#include "path-util.h"
294bf0c3 61#include "pretty-print.h"
da845dab 62#include "rm-rf.h"
349cc4a5 63#if HAVE_SECCOMP
294bf0c3 64# include "seccomp-util.h"
0f734bdc 65#endif
760877e9 66#include "sort-util.h"
3f6fd1ba 67#include "special.h"
b41711cd 68#include "stat-util.h"
3cc3dc77 69#include "string-table.h"
3f6fd1ba
LP
70#include "strv.h"
71#include "strxcpyx.h"
288a74cc 72#include "terminal-util.h"
760877e9 73#include "time-util.h"
da845dab 74#include "tmpfile-util.h"
3f6fd1ba
LP
75#include "unit-name.h"
76#include "util.h"
a87b151a 77#include "verb-log-control.h"
a6bcef29 78#include "verbs.h"
47350c5f 79#include "version.h"
2265fbf7 80
cccd2af6
LP
81DotMode arg_dot = DEP_ALL;
82char **arg_dot_from_patterns = NULL, **arg_dot_to_patterns = NULL;
9ea9d4cf 83static usec_t arg_fuzz = 0;
08e36480 84PagerFlags arg_pager_flags = 0;
25eb70af 85BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
ba474dad 86const char *arg_host = NULL;
113dd9cb 87UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
3cc3dc77 88static RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES;
1d3bc017 89static bool arg_man = true;
641c0fd1 90static bool arg_generators = false;
c649b343 91char *arg_root = NULL;
e5ea5c3a 92static char *arg_image = NULL;
ecfd082b 93static char *arg_security_policy = NULL;
bb43d853 94static bool arg_offline = false;
dfbda879 95static unsigned arg_threshold = 100;
5229b03c
LP
96unsigned arg_iterations = 1;
97usec_t arg_base_time = USEC_INFINITY;
8de7929d 98static char *arg_unit = NULL;
4b4a8ef7 99static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
08e36480 100bool arg_quiet = false;
04469211 101static char *arg_profile = NULL;
bb150966 102
d665c7b2
YW
103STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
104STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
782671bc 105STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
e5ea5c3a 106STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
ecfd082b 107STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep);
8de7929d 108STATIC_DESTRUCTOR_REGISTER(arg_unit, freep);
04469211 109STATIC_DESTRUCTOR_REGISTER(arg_profile, freep);
d665c7b2 110
25eb70af 111int acquire_bus(sd_bus **bus, bool *use_full_bus) {
5c69b31c 112 bool user = arg_scope != UNIT_FILE_SYSTEM;
fb507898 113 int r;
5c69b31c 114
f7e29336 115 if (use_full_bus && *use_full_bus) {
fb507898
YW
116 r = bus_connect_transport(arg_transport, arg_host, user, bus);
117 if (IN_SET(r, 0, -EHOSTDOWN))
118 return r;
5c69b31c
GJ
119
120 *use_full_bus = false;
121 }
122
f7e29336 123 return bus_connect_transport_systemd(arg_transport, arg_host, user, bus);
bf0e0a4d
ZJS
124}
125
cccd2af6 126int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
4afd3348 127 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
988b9df2
LP
128 int r;
129
130 assert(bus);
131 assert(path);
132 assert(property);
133 assert(strv);
134
135 r = sd_bus_get_property_strv(
136 bus,
137 "org.freedesktop.systemd1",
138 path,
139 "org.freedesktop.systemd1.Unit",
140 property,
141 &error,
142 strv);
4ae25393 143 if (r < 0)
0ed3da7c 144 return log_error_errno(r, "Failed to get unit property %s: %s", property, bus_error_message(&error, r));
988b9df2
LP
145
146 return 0;
147}
148
da845dab
AB
149static int process_aliases(char *argv[], char *tempdir, char ***ret) {
150 _cleanup_strv_free_ char **filenames = NULL;
151 char **filename;
152 int r;
153
154 assert(argv);
155 assert(tempdir);
156 assert(ret);
157
158 STRV_FOREACH(filename, strv_skip(argv, 1)) {
437346c9
AB
159 _cleanup_free_ char *src = NULL, *dst = NULL, *base = NULL;
160 const char *parse_arg;
da845dab 161
437346c9
AB
162 parse_arg = *filename;
163 r = extract_first_word(&parse_arg, &src, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE);
da845dab
AB
164 if (r < 0)
165 return r;
166
167 if (!parse_arg) {
437346c9 168 r = strv_consume(&filenames, TAKE_PTR(src));
da845dab 169 if (r < 0)
437346c9 170 return r;
da845dab
AB
171
172 continue;
173 }
174
437346c9
AB
175 r = path_extract_filename(parse_arg, &base);
176 if (r < 0)
177 return r;
178
179 dst = path_join(tempdir, base);
da845dab
AB
180 if (!dst)
181 return -ENOMEM;
182
183 r = copy_file(src, dst, 0, 0644, 0, 0, COPY_REFLINK);
184 if (r < 0)
185 return r;
186
187 r = strv_consume(&filenames, TAKE_PTR(dst));
188 if (r < 0)
437346c9 189 return r;
da845dab
AB
190 }
191
192 *ret = TAKE_PTR(filenames);
193 return 0;
194}
195
1ace223c
SJ
196static int list_dependencies_print(
197 const char *name,
198 unsigned level,
199 unsigned branches,
200 bool last,
6aa601c5
ZJS
201 UnitTimes *times,
202 BootTimes *boot) {
1ace223c 203
6aa601c5 204 for (unsigned i = level; i != 0; i--)
9a6f746f 205 printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
bb150966 206
9a6f746f 207 printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH));
bb150966
HH
208
209 if (times) {
baa4880b 210 if (times->time > 0)
54f8c958 211 printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
5291f26d
ZJS
212 FORMAT_TIMESPAN(times->activating - boot->userspace_time, USEC_PER_MSEC),
213 FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal());
cc27380c 214 else if (times->activated > boot->userspace_time)
5291f26d 215 printf("%s @%s", name, FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
216 else
217 printf("%s", name);
988b9df2
LP
218 } else
219 printf("%s", name);
bb150966
HH
220 printf("\n");
221
222 return 0;
223}
224
048ecf5b 225static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
988b9df2 226 _cleanup_free_ char *path = NULL;
bb150966
HH
227
228 assert(bus);
229 assert(name);
230 assert(deps);
231
232 path = unit_dbus_path_from_name(name);
234519ae 233 if (!path)
988b9df2 234 return -ENOMEM;
bb150966 235
988b9df2 236 return bus_get_unit_property_strv(bus, path, "After", deps);
bb150966
HH
237}
238
239static Hashmap *unit_times_hashmap;
240
1ace223c 241static int list_dependencies_compare(char *const *a, char *const *b) {
bb150966 242 usec_t usa = 0, usb = 0;
6aa601c5 243 UnitTimes *times;
bb150966
HH
244
245 times = hashmap_get(unit_times_hashmap, *a);
246 if (times)
cc27380c 247 usa = times->activated;
bb150966
HH
248 times = hashmap_get(unit_times_hashmap, *b);
249 if (times)
cc27380c 250 usb = times->activated;
bb150966 251
93bab288 252 return CMP(usb, usa);
bb150966
HH
253}
254
6aa601c5 255static bool times_in_range(const UnitTimes *times, const BootTimes *boot) {
1ace223c 256 return times && times->activated > 0 && times->activated <= boot->finish_time;
230cc99a
ZJS
257}
258
1ace223c 259static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) {
bb150966
HH
260 _cleanup_strv_free_ char **deps = NULL;
261 char **c;
05f7a068 262 int r;
bb150966
HH
263 usec_t service_longest = 0;
264 int to_print = 0;
6aa601c5
ZJS
265 UnitTimes *times;
266 BootTimes *boot;
bb150966 267
988b9df2 268 if (strv_extend(units, name))
bb150966
HH
269 return log_oom();
270
271 r = list_dependencies_get_dependencies(bus, name, &deps);
272 if (r < 0)
273 return r;
274
93bab288 275 typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
bb150966
HH
276
277 r = acquire_boot_times(bus, &boot);
278 if (r < 0)
279 return r;
280
281 STRV_FOREACH(c, deps) {
33b7988d 282 times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */
1ace223c 283 if (times_in_range(times, boot) && times->activated >= service_longest)
cc27380c 284 service_longest = times->activated;
bb150966
HH
285 }
286
234519ae 287 if (service_longest == 0)
bb150966
HH
288 return r;
289
290 STRV_FOREACH(c, deps) {
33b7988d 291 times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */
1ace223c 292 if (times_in_range(times, boot) && service_longest - times->activated <= arg_fuzz)
bb150966 293 to_print++;
bb150966
HH
294 }
295
f168c273 296 if (!to_print)
bb150966
HH
297 return r;
298
299 STRV_FOREACH(c, deps) {
33b7988d 300 times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */
1ace223c 301 if (!times_in_range(times, boot) || service_longest - times->activated > arg_fuzz)
bb150966
HH
302 continue;
303
304 to_print--;
305
306 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
307 if (r < 0)
308 return r;
309
310 if (strv_contains(*units, *c)) {
311 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
312 true, NULL, boot);
872c8faa
ZJS
313 if (r < 0)
314 return r;
bb150966
HH
315 continue;
316 }
317
1ace223c 318 r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (to_print ? 1 : 0));
872c8faa 319 if (r < 0)
bb150966
HH
320 return r;
321
baa4880b 322 if (to_print == 0)
bb150966 323 break;
bb150966
HH
324 }
325 return 0;
326}
327
048ecf5b 328static int list_dependencies(sd_bus *bus, const char *name) {
bb150966 329 _cleanup_strv_free_ char **units = NULL;
6aa601c5 330 UnitTimes *times;
bb150966 331 int r;
0ee9613d
TA
332 const char *id;
333 _cleanup_free_ char *path = NULL;
4afd3348
LP
334 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
335 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
6aa601c5 336 BootTimes *boot;
bb150966
HH
337
338 assert(bus);
339
805bf39c 340 path = unit_dbus_path_from_name(name);
234519ae 341 if (!path)
988b9df2 342 return -ENOMEM;
bb150966 343
a936124f
TA
344 r = sd_bus_get_property(
345 bus,
346 "org.freedesktop.systemd1",
347 path,
348 "org.freedesktop.systemd1.Unit",
349 "Id",
350 &error,
351 &reply,
352 "s");
4ae25393 353 if (r < 0)
0ed3da7c 354 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
bb150966 355
048ecf5b 356 r = sd_bus_message_read(reply, "s", &id);
5b30bef8
LP
357 if (r < 0)
358 return bus_log_parse_error(r);
bb150966 359
bb150966
HH
360 times = hashmap_get(unit_times_hashmap, id);
361
362 r = acquire_boot_times(bus, &boot);
363 if (r < 0)
364 return r;
365
366 if (times) {
367 if (times->time)
54f8c958 368 printf("%s%s +%s%s\n", ansi_highlight_red(), id,
5291f26d 369 FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal());
cc27380c 370 else if (times->activated > boot->userspace_time)
5291f26d
ZJS
371 printf("%s @%s\n", id,
372 FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
373 else
374 printf("%s\n", id);
375 }
376
805bf39c 377 return list_dependencies_one(bus, name, 0, &units, 0);
bb150966
HH
378}
379
a6bcef29
LP
380static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
381 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
15567b3a 382 _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL;
bb150966 383 Hashmap *h;
988b9df2 384 int n, r;
bb150966 385
f7e29336 386 r = acquire_bus(&bus, NULL);
a6bcef29 387 if (r < 0)
10a7340a 388 return bus_log_connect_error(r, arg_transport);
a6bcef29 389
bb150966
HH
390 n = acquire_time_data(bus, &times);
391 if (n <= 0)
392 return n;
393
d5099efc 394 h = hashmap_new(&string_hash_ops);
bb150966 395 if (!h)
8efbce13 396 return log_oom();
bb150966 397
6aa601c5 398 for (UnitTimes *u = times; u->has_data; u++) {
df560cf6 399 r = hashmap_put(h, u->name, u);
bb150966 400 if (r < 0)
8efbce13 401 return log_error_errno(r, "Failed to add entry to hashmap: %m");
bb150966
HH
402 }
403 unit_times_hashmap = h;
404
384c2c32 405 pager_open(arg_pager_flags);
9ea9d4cf 406
2fffb93b
ZJS
407 puts("The time when unit became active or started is printed after the \"@\" character.\n"
408 "The time the unit took to start is printed after the \"+\" character.\n");
bb150966 409
a6bcef29 410 if (argc > 1) {
805bf39c 411 char **name;
a6bcef29 412 STRV_FOREACH(name, strv_skip(argv, 1))
805bf39c 413 list_dependencies(bus, *name);
9ea9d4cf 414 } else
805bf39c 415 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
bb150966 416
a6bcef29 417 h = hashmap_free(h);
bb150966
HH
418 return 0;
419}
420
a6bcef29
LP
421static int analyze_time(int argc, char *argv[], void *userdata) {
422 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
c170f3a4
LP
423 _cleanup_free_ char *buf = NULL;
424 int r;
425
f7e29336 426 r = acquire_bus(&bus, NULL);
a6bcef29 427 if (r < 0)
10a7340a 428 return bus_log_connect_error(r, arg_transport);
a6bcef29 429
c170f3a4
LP
430 r = pretty_boot_time(bus, &buf);
431 if (r < 0)
432 return r;
433
434 puts(buf);
2265fbf7
SP
435 return 0;
436}
437
a87b151a 438static int verb_log_control(int argc, char *argv[], void *userdata) {
a6bcef29 439 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2ca2a91c
LP
440 int r;
441
b98416e1 442 assert(IN_SET(argc, 1, 2));
2ca2a91c 443
f7e29336 444 r = acquire_bus(&bus, NULL);
a6bcef29 445 if (r < 0)
10a7340a 446 return bus_log_connect_error(r, arg_transport);
a65615ca 447
a87b151a 448 return verb_log_control_common(bus, "org.freedesktop.systemd1", argv[0], argc == 2 ? argv[1] : NULL);
90657286
YW
449}
450
e67cd21d
ZJS
451static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int flags) {
452 char **s;
453 STRV_FOREACH(s, strv)
454 if (strv_fnmatch_or_empty(patterns, *s, flags))
455 return true;
456
457 return false;
458}
459
460static int do_unit_files(int argc, char *argv[], void *userdata) {
461 _cleanup_(lookup_paths_free) LookupPaths lp = {};
462 _cleanup_hashmap_free_ Hashmap *unit_ids = NULL;
463 _cleanup_hashmap_free_ Hashmap *unit_names = NULL;
464 char **patterns = strv_skip(argv, 1);
e67cd21d
ZJS
465 const char *k, *dst;
466 char **v;
467 int r;
468
469 r = lookup_paths_init(&lp, arg_scope, 0, NULL);
470 if (r < 0)
471 return log_error_errno(r, "lookup_paths_init() failed: %m");
472
91e0ee5f 473 r = unit_file_build_name_map(&lp, NULL, &unit_ids, &unit_names, NULL);
e67cd21d
ZJS
474 if (r < 0)
475 return log_error_errno(r, "unit_file_build_name_map() failed: %m");
476
90e74a66 477 HASHMAP_FOREACH_KEY(dst, k, unit_ids) {
e67cd21d
ZJS
478 if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
479 !strv_fnmatch_or_empty(patterns, dst, FNM_NOESCAPE))
480 continue;
481
482 printf("ids: %s → %s\n", k, dst);
483 }
484
90e74a66 485 HASHMAP_FOREACH_KEY(v, k, unit_names) {
e67cd21d
ZJS
486 if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
487 !strv_fnmatch_strv_or_empty(patterns, v, FNM_NOESCAPE))
488 continue;
489
490 _cleanup_free_ char *j = strv_join(v, ", ");
491 printf("aliases: %s ← %s\n", k, j);
492 }
493
494 return 0;
495}
496
31a5924e 497static int dump_unit_paths(int argc, char *argv[], void *userdata) {
8e766630 498 _cleanup_(lookup_paths_free) LookupPaths paths = {};
31a5924e
ZJS
499 int r;
500 char **p;
501
502 r = lookup_paths_init(&paths, arg_scope, 0, NULL);
503 if (r < 0)
504 return log_error_errno(r, "lookup_paths_init() failed: %m");
505
506 STRV_FOREACH(p, paths.search_path)
507 puts(*p);
508
509 return 0;
510}
511
30bddc06 512void time_parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) {
c269607f
ZJS
513 if (calendar && calendar_spec_from_string(p, NULL) >= 0)
514 log_notice("Hint: this expression is a valid calendar specification. "
515 "Use 'systemd-analyze calendar \"%s\"' instead?", p);
516 if (timestamp && parse_timestamp(p, NULL) >= 0)
517 log_notice("Hint: this expression is a valid timestamp. "
518 "Use 'systemd-analyze timestamp \"%s\"' instead?", p);
519 if (timespan && parse_time(p, NULL, USEC_PER_SEC) >= 0)
520 log_notice("Hint: this expression is a valid timespan. "
521 "Use 'systemd-analyze timespan \"%s\"' instead?", p);
6d86f4bd
LP
522}
523
edfea9fe 524static int do_condition(int argc, char *argv[], void *userdata) {
8de7929d 525 return verify_conditions(strv_skip(argv, 1), arg_scope, arg_unit, arg_root);
edfea9fe
ZJS
526}
527
a6bcef29 528static int do_verify(int argc, char *argv[], void *userdata) {
da845dab
AB
529 _cleanup_strv_free_ char **filenames = NULL;
530 _cleanup_(rm_rf_physical_and_freep) char *tempdir = NULL;
531 int r;
532
533 r = mkdtemp_malloc("/tmp/systemd-analyze-XXXXXX", &tempdir);
534 if (r < 0)
535 return log_error_errno(r, "Failed to setup working directory: %m");
536
537 r = process_aliases(argv, tempdir, &filenames);
538 if (r < 0)
539 return log_error_errno(r, "Couldn't process aliases: %m");
540
541 return verify_units(filenames, arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root);
a6bcef29
LP
542}
543
ec16f3b6
LP
544static int do_security(int argc, char *argv[], void *userdata) {
545 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
ecfd082b 546 _cleanup_(json_variant_unrefp) JsonVariant *policy = NULL;
ec16f3b6 547 int r;
ecfd082b 548 unsigned line, column;
ec16f3b6 549
741c4c8d
LB
550 if (!arg_offline) {
551 r = acquire_bus(&bus, NULL);
552 if (r < 0)
553 return bus_log_connect_error(r, arg_transport);
554 }
ec16f3b6 555
384c2c32 556 pager_open(arg_pager_flags);
ec16f3b6 557
ecfd082b
MG
558 if (arg_security_policy) {
559 r = json_parse_file(/*f=*/ NULL, arg_security_policy, /*flags=*/ 0, &policy, &line, &column);
560 if (r < 0)
561 return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column);
562 } else {
563 _cleanup_fclose_ FILE *f = NULL;
564 _cleanup_free_ char *pp = NULL;
565
566 r = search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL, CONF_PATHS_NULSTR("systemd"), &f, &pp);
567 if (r < 0 && r != -ENOENT)
568 return r;
569
b98416e1 570 if (f) {
ecfd082b
MG
571 r = json_parse_file(f, pp, /*flags=*/ 0, &policy, &line, &column);
572 if (r < 0)
573 return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column);
574 }
575 }
576
577 return analyze_security(bus,
578 strv_skip(argv, 1),
579 policy,
580 arg_scope,
581 arg_man,
582 arg_generators,
583 arg_offline,
584 arg_threshold,
585 arg_root,
04469211 586 arg_profile,
4b4a8ef7
MG
587 arg_json_format_flags,
588 arg_pager_flags,
ecfd082b 589 /*flags=*/ 0);
ec16f3b6
LP
590}
591
917e6554
LB
592static int do_elf_inspection(int argc, char *argv[], void *userdata) {
593 pager_open(arg_pager_flags);
594
595 return analyze_elf(strv_skip(argv, 1), arg_json_format_flags);
596}
597
a6bcef29 598static int help(int argc, char *argv[], void *userdata) {
49139a5d 599 _cleanup_free_ char *link = NULL, *dot_link = NULL;
37ec0fdd 600 int r;
9ea9d4cf 601
384c2c32 602 pager_open(arg_pager_flags);
9ea9d4cf 603
37ec0fdd
LP
604 r = terminal_urlify_man("systemd-analyze", "1", &link);
605 if (r < 0)
606 return log_oom();
607
49139a5d
LP
608 /* Not using terminal_urlify_man() for this, since we don't want the "man page" text suffix in this case. */
609 r = terminal_urlify("man:dot(1)", "dot(1)", &dot_link);
610 if (r < 0)
611 return log_oom();
612
353b2baa
LP
613 printf("%s [OPTIONS...] COMMAND ...\n\n"
614 "%sProfile systemd, show unit dependencies, check unit files.%s\n"
20a51f6a 615 "\nCommands:\n"
3cc3dc77
MG
616 " [time] Print time required to boot the machine\n"
617 " blame Print list of running units ordered by\n"
618 " time to init\n"
619 " critical-chain [UNIT...] Print a tree of the time critical chain\n"
620 " of units\n"
621 " plot Output SVG graphic showing service\n"
622 " initialization\n"
623 " dot [UNIT...] Output dependency graph in %s format\n"
624 " dump Output state serialization of service\n"
625 " manager\n"
626 " cat-config Show configuration file and drop-ins\n"
627 " unit-files List files and symlinks for units\n"
628 " unit-paths List load directories for units\n"
629 " exit-status [STATUS...] List exit status definitions\n"
630 " capability [CAP...] List capability definitions\n"
52117f5a
ZJS
631 " syscall-filter [NAME...] List syscalls in seccomp filters\n"
632 " filesystems [NAME...] List known filesystems\n"
3cc3dc77
MG
633 " condition CONDITION... Evaluate conditions and asserts\n"
634 " verify FILE... Check unit files for correctness\n"
635 " calendar SPEC... Validate repetitive calendar time\n"
636 " events\n"
637 " timestamp TIMESTAMP... Validate a timestamp\n"
638 " timespan SPAN... Validate a time span\n"
639 " security [UNIT...] Analyze security of unit\n"
917e6554 640 " inspect-elf FILE... Parse and print ELF package metadata\n"
353b2baa 641 "\nOptions:\n"
3cc3dc77 642 " --recursive-errors=MODE Control which units are verified\n"
bb43d853 643 " --offline=BOOL Perform a security review on unit file(s)\n"
dfbda879
MG
644 " --threshold=N Exit with a non-zero status when overall\n"
645 " exposure level is over threshold value\n"
ecfd082b
MG
646 " --security-policy=PATH Use custom JSON security policy instead\n"
647 " of built-in one\n"
4b4a8ef7
MG
648 " --json=pretty|short|off Generate JSON output of the security\n"
649 " analysis table\n"
3cc3dc77
MG
650 " --no-pager Do not pipe output into a pager\n"
651 " --system Operate on system systemd instance\n"
652 " --user Operate on user systemd instance\n"
653 " --global Operate on global user configuration\n"
654 " -H --host=[USER@]HOST Operate on remote host\n"
655 " -M --machine=CONTAINER Operate on local container\n"
656 " --order Show only order in the graph\n"
657 " --require Show only requirement in the graph\n"
658 " --from-pattern=GLOB Show only origins in the graph\n"
659 " --to-pattern=GLOB Show only destinations in the graph\n"
660 " --fuzz=SECONDS Also print services which finished SECONDS\n"
661 " earlier than the latest in the branch\n"
662 " --man[=BOOL] Do [not] check for existence of man pages\n"
663 " --generators[=BOOL] Do [not] run unit generators\n"
664 " (requires privileges)\n"
665 " --iterations=N Show the specified number of iterations\n"
666 " --base-time=TIMESTAMP Calculate calendar times relative to\n"
667 " specified time\n"
04469211
LB
668 " --profile=name|PATH Include the specified profile in the\n"
669 " security review of the unit(s)\n"
52117f5a
ZJS
670 " -h --help Show this help\n"
671 " --version Show package version\n"
672 " -q --quiet Do not emit hints\n"
bc556335
DDM
673 "\nSee the %s for details.\n",
674 program_invocation_short_name,
675 ansi_highlight(),
676 ansi_normal(),
677 dot_link,
678 link);
96de7c04 679
1ace223c
SJ
680 /* When updating this list, including descriptions, apply changes to
681 * shell-completion/bash/systemd-analyze and shell-completion/zsh/_systemd-analyze too. */
a6bcef29
LP
682
683 return 0;
2265fbf7
SP
684}
685
9ea9d4cf 686static int parse_argv(int argc, char *argv[]) {
2265fbf7
SP
687 enum {
688 ARG_VERSION = 0x100,
1700761b
SP
689 ARG_ORDER,
690 ARG_REQUIRE,
46d8646a 691 ARG_ROOT,
e5ea5c3a 692 ARG_IMAGE,
e55933db 693 ARG_SYSTEM,
28b35ef2
ZJS
694 ARG_USER,
695 ARG_GLOBAL,
e55933db 696 ARG_DOT_FROM_PATTERN,
bb150966 697 ARG_DOT_TO_PATTERN,
9ea9d4cf 698 ARG_FUZZ,
1d3bc017 699 ARG_NO_PAGER,
dad29dff 700 ARG_MAN,
641c0fd1 701 ARG_GENERATORS,
f2ccf832 702 ARG_ITERATIONS,
985c1880 703 ARG_BASE_TIME,
3cc3dc77 704 ARG_RECURSIVE_ERRORS,
bb43d853 705 ARG_OFFLINE,
dfbda879 706 ARG_THRESHOLD,
ecfd082b 707 ARG_SECURITY_POLICY,
4b4a8ef7 708 ARG_JSON,
04469211 709 ARG_PROFILE,
2265fbf7
SP
710 };
711
712 static const struct option options[] = {
3cc3dc77
MG
713 { "help", no_argument, NULL, 'h' },
714 { "version", no_argument, NULL, ARG_VERSION },
52117f5a 715 { "quiet", no_argument, NULL, 'q' },
3cc3dc77
MG
716 { "order", no_argument, NULL, ARG_ORDER },
717 { "require", no_argument, NULL, ARG_REQUIRE },
718 { "root", required_argument, NULL, ARG_ROOT },
719 { "image", required_argument, NULL, ARG_IMAGE },
720 { "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
bb43d853 721 { "offline", required_argument, NULL, ARG_OFFLINE },
dfbda879 722 { "threshold", required_argument, NULL, ARG_THRESHOLD },
ecfd082b 723 { "security-policy", required_argument, NULL, ARG_SECURITY_POLICY },
3cc3dc77
MG
724 { "system", no_argument, NULL, ARG_SYSTEM },
725 { "user", no_argument, NULL, ARG_USER },
726 { "global", no_argument, NULL, ARG_GLOBAL },
727 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
728 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
729 { "fuzz", required_argument, NULL, ARG_FUZZ },
730 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
731 { "man", optional_argument, NULL, ARG_MAN },
732 { "generators", optional_argument, NULL, ARG_GENERATORS },
733 { "host", required_argument, NULL, 'H' },
734 { "machine", required_argument, NULL, 'M' },
735 { "iterations", required_argument, NULL, ARG_ITERATIONS },
736 { "base-time", required_argument, NULL, ARG_BASE_TIME },
8de7929d 737 { "unit", required_argument, NULL, 'U' },
4b4a8ef7 738 { "json", required_argument, NULL, ARG_JSON },
04469211 739 { "profile", required_argument, NULL, ARG_PROFILE },
eb9da376 740 {}
2265fbf7
SP
741 };
742
eb9da376
LP
743 int r, c;
744
2265fbf7
SP
745 assert(argc >= 0);
746 assert(argv);
747
8de7929d 748 while ((c = getopt_long(argc, argv, "hH:M:U:", options, NULL)) >= 0)
eb9da376 749 switch (c) {
c170f3a4
LP
750
751 case 'h':
a6bcef29 752 return help(0, NULL, NULL);
c170f3a4 753
52117f5a
ZJS
754 case ARG_VERSION:
755 return version();
756
757 case 'q':
758 arg_quiet = true;
759 break;
760
3cc3dc77
MG
761 case ARG_RECURSIVE_ERRORS:
762 if (streq(optarg, "help")) {
763 DUMP_STRING_TABLE(recursive_errors, RecursiveErrors, _RECURSIVE_ERRORS_MAX);
764 return 0;
765 }
766 r = recursive_errors_from_string(optarg);
767 if (r < 0)
768 return log_error_errno(r, "Unknown mode passed to --recursive-errors='%s'.", optarg);
769
770 arg_recursive_errors = r;
771 break;
772
46d8646a 773 case ARG_ROOT:
782671bc
MG
774 r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
775 if (r < 0)
776 return r;
46d8646a
ZJS
777 break;
778
e5ea5c3a
MG
779 case ARG_IMAGE:
780 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
781 if (r < 0)
782 return r;
783 break;
784
28b35ef2
ZJS
785 case ARG_SYSTEM:
786 arg_scope = UNIT_FILE_SYSTEM;
787 break;
788
c170f3a4 789 case ARG_USER:
28b35ef2 790 arg_scope = UNIT_FILE_USER;
c170f3a4
LP
791 break;
792
28b35ef2
ZJS
793 case ARG_GLOBAL:
794 arg_scope = UNIT_FILE_GLOBAL;
c170f3a4
LP
795 break;
796
797 case ARG_ORDER:
798 arg_dot = DEP_ORDER;
799 break;
800
801 case ARG_REQUIRE:
802 arg_dot = DEP_REQUIRE;
803 break;
804
e55933db 805 case ARG_DOT_FROM_PATTERN:
903a0b07
LP
806 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
807 return log_oom();
808
e55933db
ŁS
809 break;
810
811 case ARG_DOT_TO_PATTERN:
903a0b07
LP
812 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
813 return log_oom();
814
e55933db
ŁS
815 break;
816
bb150966
HH
817 case ARG_FUZZ:
818 r = parse_sec(optarg, &arg_fuzz);
819 if (r < 0)
820 return r;
821 break;
822
9ea9d4cf 823 case ARG_NO_PAGER:
0221d68a 824 arg_pager_flags |= PAGER_DISABLE;
9ea9d4cf
LP
825 break;
826
3cd26e7c
LP
827 case 'H':
828 arg_transport = BUS_TRANSPORT_REMOTE;
829 arg_host = optarg;
830 break;
831
832 case 'M':
de33fc62 833 arg_transport = BUS_TRANSPORT_MACHINE;
3cd26e7c
LP
834 arg_host = optarg;
835 break;
836
dad29dff 837 case ARG_MAN:
599c7c54
ZJS
838 r = parse_boolean_argument("--man", optarg, &arg_man);
839 if (r < 0)
840 return r;
1d3bc017
ZJS
841 break;
842
641c0fd1 843 case ARG_GENERATORS:
599c7c54
ZJS
844 r = parse_boolean_argument("--generators", optarg, &arg_generators);
845 if (r < 0)
846 return r;
641c0fd1
ZJS
847 break;
848
bb43d853
MG
849 case ARG_OFFLINE:
850 r = parse_boolean_argument("--offline", optarg, &arg_offline);
851 if (r < 0)
852 return r;
853 break;
854
dfbda879
MG
855 case ARG_THRESHOLD:
856 r = safe_atou(optarg, &arg_threshold);
857 if (r < 0 || arg_threshold > 100)
858 return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse threshold: %s", optarg);
859
860 break;
861
ecfd082b
MG
862 case ARG_SECURITY_POLICY:
863 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_security_policy);
864 if (r < 0)
865 return r;
866 break;
867
4b4a8ef7
MG
868 case ARG_JSON:
869 r = parse_json_argument(optarg, &arg_json_format_flags);
870 if (r <= 0)
871 return r;
872 break;
873
f2ccf832
LP
874 case ARG_ITERATIONS:
875 r = safe_atou(optarg, &arg_iterations);
876 if (r < 0)
877 return log_error_errno(r, "Failed to parse iterations: %s", optarg);
878
879 break;
880
985c1880
LP
881 case ARG_BASE_TIME:
882 r = parse_timestamp(optarg, &arg_base_time);
883 if (r < 0)
884 return log_error_errno(r, "Failed to parse --base-time= parameter: %s", optarg);
885
886 break;
887
04469211
LB
888 case ARG_PROFILE:
889 if (isempty(optarg))
890 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Profile file name is empty");
891
892 if (is_path(optarg)) {
893 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_profile);
894 if (r < 0)
895 return r;
896 if (!endswith(arg_profile, ".conf"))
897 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Profile file name must end with .conf: %s", arg_profile);
898 } else {
899 r = free_and_strdup(&arg_profile, optarg);
900 if (r < 0)
901 return log_oom();
902 }
903
904 break;
905
8de7929d
DDM
906 case 'U': {
907 _cleanup_free_ char *mangled = NULL;
908
909 r = unit_name_mangle(optarg, UNIT_NAME_MANGLE_WARN, &mangled);
910 if (r < 0)
911 return log_error_errno(r, "Failed to mangle unit name %s: %m", optarg);
912
913 free_and_replace(arg_unit, mangled);
914 break;
915 }
c170f3a4
LP
916 case '?':
917 return -EINVAL;
918
919 default:
04499a70 920 assert_not_reached();
2265fbf7 921 }
eb9da376 922
bb43d853
MG
923 if (arg_offline && !streq_ptr(argv[optind], "security"))
924 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
925 "Option --offline= is only supported for security right now.");
926
917e6554 927 if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf"))
4b4a8ef7 928 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
c0f65909 929 "Option --json= is only supported for security and inspect-elf right now.");
4b4a8ef7 930
dfbda879
MG
931 if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
932 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
933 "Option --threshold= is only supported for security right now.");
934
31a5924e 935 if (arg_scope == UNIT_FILE_GLOBAL &&
baaa35ad
ZJS
936 !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
937 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
938 "Option --global only makes sense with verbs dot, unit-paths, verify.");
31a5924e 939
f1d9d36a
ZJS
940 if (streq_ptr(argv[optind], "cat-config") && arg_scope == UNIT_FILE_USER)
941 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
942 "Option --user is not supported for cat-config right now.");
943
ecfd082b
MG
944 if (arg_security_policy && !streq_ptr(argv[optind], "security"))
945 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
946 "Option --security-policy= is only supported for security.");
947
8de7929d 948 if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify", "condition")) &&
bb43d853 949 (!(streq_ptr(argv[optind], "security") && arg_offline)))
baaa35ad 950 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
8de7929d 951 "Options --root= and --image= are only supported for cat-config, verify, condition and security when used with --offline= right now.");
e5ea5c3a
MG
952
953 /* Having both an image and a root is not supported by the code */
954 if (arg_root && arg_image)
955 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
46d8646a 956
8de7929d
DDM
957 if (arg_unit && !streq_ptr(argv[optind], "condition"))
958 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --unit= is only supported for condition");
959
960 if (streq_ptr(argv[optind], "condition") && !arg_unit && optind >= argc - 1)
961 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too few arguments for condition");
962
963 if (streq_ptr(argv[optind], "condition") && arg_unit && optind < argc - 1)
964 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No conditions can be passed if --unit= is used.");
965
1d3bc017 966 return 1; /* work to do */
2265fbf7
SP
967}
968
d665c7b2 969static int run(int argc, char *argv[]) {
e5ea5c3a
MG
970 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
971 _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
972 _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
a6bcef29
LP
973
974 static const Verb verbs[] = {
889d695d
JK
975 { "help", VERB_ANY, VERB_ANY, 0, help },
976 { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time },
977 { "blame", VERB_ANY, 1, 0, analyze_blame },
978 { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain },
979 { "plot", VERB_ANY, 1, 0, analyze_plot },
980 { "dot", VERB_ANY, VERB_ANY, 0, dot },
ab86ccba 981 /* ↓ The following seven verbs are deprecated, from here … ↓ */
a87b151a
DDM
982 { "log-level", VERB_ANY, 2, 0, verb_log_control },
983 { "log-target", VERB_ANY, 2, 0, verb_log_control },
984 { "set-log-level", 2, 2, 0, verb_log_control },
985 { "get-log-level", VERB_ANY, 1, 0, verb_log_control },
986 { "set-log-target", 2, 2, 0, verb_log_control },
987 { "get-log-target", VERB_ANY, 1, 0, verb_log_control },
26e1e973 988 { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
ab86ccba 989 /* ↑ … until here ↑ */
889d695d 990 { "dump", VERB_ANY, 1, 0, dump },
854a42fb 991 { "cat-config", 2, VERB_ANY, 0, cat_config },
e67cd21d 992 { "unit-files", VERB_ANY, VERB_ANY, 0, do_unit_files },
31a5924e 993 { "unit-paths", 1, 1, 0, dump_unit_paths },
5238d9a8 994 { "exit-status", VERB_ANY, VERB_ANY, 0, dump_exit_status },
889d695d 995 { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
b2af819b 996 { "capability", VERB_ANY, VERB_ANY, 0, dump_capabilities },
b41711cd 997 { "filesystems", VERB_ANY, VERB_ANY, 0, dump_filesystems },
8de7929d 998 { "condition", VERB_ANY, VERB_ANY, 0, do_condition },
889d695d
JK
999 { "verify", 2, VERB_ANY, 0, do_verify },
1000 { "calendar", 2, VERB_ANY, 0, test_calendar },
2cae4711 1001 { "timestamp", 2, VERB_ANY, 0, test_timestamp },
3f1c1287 1002 { "timespan", 2, VERB_ANY, 0, dump_timespan },
ec16f3b6 1003 { "security", VERB_ANY, VERB_ANY, 0, do_security },
917e6554 1004 { "inspect-elf", 2, VERB_ANY, 0, do_elf_inspection },
a6bcef29
LP
1005 {}
1006 };
1007
5220a6f3 1008 int r;
2265fbf7
SP
1009
1010 setlocale(LC_ALL, "");
c170f3a4 1011 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
a6bcef29 1012
d2acb93d 1013 log_setup();
2265fbf7
SP
1014
1015 r = parse_argv(argc, argv);
9ea9d4cf 1016 if (r <= 0)
d665c7b2 1017 return r;
c170f3a4 1018
e5ea5c3a
MG
1019 /* Open up and mount the image */
1020 if (arg_image) {
1021 assert(!arg_root);
1022
1023 r = mount_image_privately_interactively(
1024 arg_image,
1025 DISSECT_IMAGE_GENERIC_ROOT |
1026 DISSECT_IMAGE_RELAX_VAR_CHECK |
1027 DISSECT_IMAGE_READ_ONLY,
1028 &unlink_dir,
1029 &loop_device,
1030 &decrypted_image);
1031 if (r < 0)
1032 return r;
1033
1034 arg_root = strdup(unlink_dir);
1035 if (!arg_root)
1036 return log_oom();
1037 }
1038
d665c7b2 1039 return dispatch_verb(argc, argv, verbs, NULL);
2265fbf7 1040}
d665c7b2
YW
1041
1042DEFINE_MAIN_FUNCTION(run);