1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3   Copyright © 2013 Simon Peeters
4 ***/
5 
6 #include <getopt.h>
7 #include <inttypes.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 
12 #include "sd-bus.h"
13 
14 #include "alloc-util.h"
15 #include "analyze.h"
16 #include "analyze-blame.h"
17 #include "analyze-calendar.h"
18 #include "analyze-capability.h"
19 #include "analyze-cat-config.h"
20 #include "analyze-condition.h"
21 #include "analyze-critical-chain.h"
22 #include "analyze-dot.h"
23 #include "analyze-dump.h"
24 #include "analyze-exit-status.h"
25 #include "analyze-filesystems.h"
26 #include "analyze-inspect-elf.h"
27 #include "analyze-log-control.h"
28 #include "analyze-plot.h"
29 #include "analyze-security.h"
30 #include "analyze-service-watchdogs.h"
31 #include "analyze-syscall-filter.h"
32 #include "analyze-time.h"
33 #include "analyze-time-data.h"
34 #include "analyze-timespan.h"
35 #include "analyze-timestamp.h"
36 #include "analyze-unit-files.h"
37 #include "analyze-unit-paths.h"
38 #include "analyze-verify.h"
39 #include "bus-error.h"
40 #include "bus-locator.h"
41 #include "bus-map-properties.h"
42 #include "bus-unit-util.h"
43 #include "calendarspec.h"
44 #include "cap-list.h"
45 #include "capability-util.h"
46 #include "conf-files.h"
47 #include "copy.h"
48 #include "def.h"
49 #include "exit-status.h"
50 #include "extract-word.h"
51 #include "fd-util.h"
52 #include "fileio.h"
53 #include "filesystems.h"
54 #include "format-table.h"
55 #include "glob-util.h"
56 #include "hashmap.h"
57 #include "locale-util.h"
58 #include "log.h"
59 #include "main-func.h"
60 #include "mount-util.h"
61 #include "nulstr-util.h"
62 #include "pager.h"
63 #include "parse-argument.h"
64 #include "parse-util.h"
65 #include "path-util.h"
66 #include "pretty-print.h"
67 #include "rm-rf.h"
68 #if HAVE_SECCOMP
69 #  include "seccomp-util.h"
70 #endif
71 #include "sort-util.h"
72 #include "special.h"
73 #include "stat-util.h"
74 #include "string-table.h"
75 #include "strv.h"
76 #include "strxcpyx.h"
77 #include "terminal-util.h"
78 #include "time-util.h"
79 #include "tmpfile-util.h"
80 #include "unit-name.h"
81 #include "util.h"
82 #include "verb-log-control.h"
83 #include "verbs.h"
84 #include "version.h"
85 
86 DotMode arg_dot = DEP_ALL;
87 char **arg_dot_from_patterns = NULL, **arg_dot_to_patterns = NULL;
88 usec_t arg_fuzz = 0;
89 PagerFlags arg_pager_flags = 0;
90 BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
91 const char *arg_host = NULL;
92 LookupScope arg_scope = LOOKUP_SCOPE_SYSTEM;
93 RecursiveErrors arg_recursive_errors = _RECURSIVE_ERRORS_INVALID;
94 bool arg_man = true;
95 bool arg_generators = false;
96 char *arg_root = NULL;
97 static char *arg_image = NULL;
98 char *arg_security_policy = NULL;
99 bool arg_offline = false;
100 unsigned arg_threshold = 100;
101 unsigned arg_iterations = 1;
102 usec_t arg_base_time = USEC_INFINITY;
103 char *arg_unit = NULL;
104 JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
105 bool arg_quiet = false;
106 char *arg_profile = NULL;
107 
108 STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
109 STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
110 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
111 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
112 STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep);
113 STATIC_DESTRUCTOR_REGISTER(arg_unit, freep);
114 STATIC_DESTRUCTOR_REGISTER(arg_profile, freep);
115 
acquire_bus(sd_bus ** bus,bool * use_full_bus)116 int acquire_bus(sd_bus **bus, bool *use_full_bus) {
117         bool user = arg_scope != LOOKUP_SCOPE_SYSTEM;
118         int r;
119 
120         if (use_full_bus && *use_full_bus) {
121                 r = bus_connect_transport(arg_transport, arg_host, user, bus);
122                 if (IN_SET(r, 0, -EHOSTDOWN))
123                         return r;
124 
125                 *use_full_bus = false;
126         }
127 
128         return bus_connect_transport_systemd(arg_transport, arg_host, user, bus);
129 }
130 
bus_get_unit_property_strv(sd_bus * bus,const char * path,const char * property,char *** strv)131 int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
132         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
133         int r;
134 
135         assert(bus);
136         assert(path);
137         assert(property);
138         assert(strv);
139 
140         r = sd_bus_get_property_strv(
141                         bus,
142                         "org.freedesktop.systemd1",
143                         path,
144                         "org.freedesktop.systemd1.Unit",
145                         property,
146                         &error,
147                         strv);
148         if (r < 0)
149                 return log_error_errno(r, "Failed to get unit property %s: %s", property, bus_error_message(&error, r));
150 
151         return 0;
152 }
153 
time_parsing_hint(const char * p,bool calendar,bool timestamp,bool timespan)154 void time_parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) {
155         if (calendar && calendar_spec_from_string(p, NULL) >= 0)
156                 log_notice("Hint: this expression is a valid calendar specification. "
157                            "Use 'systemd-analyze calendar \"%s\"' instead?", p);
158         if (timestamp && parse_timestamp(p, NULL) >= 0)
159                 log_notice("Hint: this expression is a valid timestamp. "
160                            "Use 'systemd-analyze timestamp \"%s\"' instead?", p);
161         if (timespan && parse_time(p, NULL, USEC_PER_SEC) >= 0)
162                 log_notice("Hint: this expression is a valid timespan. "
163                            "Use 'systemd-analyze timespan \"%s\"' instead?", p);
164 }
165 
help(int argc,char * argv[],void * userdata)166 static int help(int argc, char *argv[], void *userdata) {
167         _cleanup_free_ char *link = NULL, *dot_link = NULL;
168         int r;
169 
170         pager_open(arg_pager_flags);
171 
172         r = terminal_urlify_man("systemd-analyze", "1", &link);
173         if (r < 0)
174                 return log_oom();
175 
176         /* Not using terminal_urlify_man() for this, since we don't want the "man page" text suffix in this case. */
177         r = terminal_urlify("man:dot(1)", "dot(1)", &dot_link);
178         if (r < 0)
179                 return log_oom();
180 
181         printf("%s [OPTIONS...] COMMAND ...\n\n"
182                "%sProfile systemd, show unit dependencies, check unit files.%s\n"
183                "\nCommands:\n"
184                "  [time]                     Print time required to boot the machine\n"
185                "  blame                      Print list of running units ordered by\n"
186                "                             time to init\n"
187                "  critical-chain [UNIT...]   Print a tree of the time critical chain\n"
188                "                             of units\n"
189                "  plot                       Output SVG graphic showing service\n"
190                "                             initialization\n"
191                "  dot [UNIT...]              Output dependency graph in %s format\n"
192                "  dump                       Output state serialization of service\n"
193                "                             manager\n"
194                "  cat-config                 Show configuration file and drop-ins\n"
195                "  unit-files                 List files and symlinks for units\n"
196                "  unit-paths                 List load directories for units\n"
197                "  exit-status [STATUS...]    List exit status definitions\n"
198                "  capability [CAP...]        List capability definitions\n"
199                "  syscall-filter [NAME...]   List syscalls in seccomp filters\n"
200                "  filesystems [NAME...]      List known filesystems\n"
201                "  condition CONDITION...     Evaluate conditions and asserts\n"
202                "  verify FILE...             Check unit files for correctness\n"
203                "  calendar SPEC...           Validate repetitive calendar time\n"
204                "                             events\n"
205                "  timestamp TIMESTAMP...     Validate a timestamp\n"
206                "  timespan SPAN...           Validate a time span\n"
207                "  security [UNIT...]         Analyze security of unit\n"
208                "  inspect-elf FILE...        Parse and print ELF package metadata\n"
209                "\nOptions:\n"
210                "     --recursive-errors=MODE Control which units are verified\n"
211                "     --offline=BOOL          Perform a security review on unit file(s)\n"
212                "     --threshold=N           Exit with a non-zero status when overall\n"
213                "                             exposure level is over threshold value\n"
214                "     --security-policy=PATH  Use custom JSON security policy instead\n"
215                "                             of built-in one\n"
216                "     --json=pretty|short|off Generate JSON output of the security\n"
217                "                             analysis table\n"
218                "     --no-pager              Do not pipe output into a pager\n"
219                "     --system                Operate on system systemd instance\n"
220                "     --user                  Operate on user systemd instance\n"
221                "     --global                Operate on global user configuration\n"
222                "  -H --host=[USER@]HOST      Operate on remote host\n"
223                "  -M --machine=CONTAINER     Operate on local container\n"
224                "     --order                 Show only order in the graph\n"
225                "     --require               Show only requirement in the graph\n"
226                "     --from-pattern=GLOB     Show only origins in the graph\n"
227                "     --to-pattern=GLOB       Show only destinations in the graph\n"
228                "     --fuzz=SECONDS          Also print services which finished SECONDS\n"
229                "                             earlier than the latest in the branch\n"
230                "     --man[=BOOL]            Do [not] check for existence of man pages\n"
231                "     --generators[=BOOL]     Do [not] run unit generators\n"
232                "                             (requires privileges)\n"
233                "     --iterations=N          Show the specified number of iterations\n"
234                "     --base-time=TIMESTAMP   Calculate calendar times relative to\n"
235                "                             specified time\n"
236                "     --profile=name|PATH     Include the specified profile in the\n"
237                "                             security review of the unit(s)\n"
238                "  -h --help                  Show this help\n"
239                "     --version               Show package version\n"
240                "  -q --quiet                 Do not emit hints\n"
241                "\nSee the %s for details.\n",
242                program_invocation_short_name,
243                ansi_highlight(),
244                ansi_normal(),
245                dot_link,
246                link);
247 
248         /* When updating this list, including descriptions, apply changes to
249          * shell-completion/bash/systemd-analyze and shell-completion/zsh/_systemd-analyze too. */
250 
251         return 0;
252 }
253 
parse_argv(int argc,char * argv[])254 static int parse_argv(int argc, char *argv[]) {
255         enum {
256                 ARG_VERSION = 0x100,
257                 ARG_ORDER,
258                 ARG_REQUIRE,
259                 ARG_ROOT,
260                 ARG_IMAGE,
261                 ARG_SYSTEM,
262                 ARG_USER,
263                 ARG_GLOBAL,
264                 ARG_DOT_FROM_PATTERN,
265                 ARG_DOT_TO_PATTERN,
266                 ARG_FUZZ,
267                 ARG_NO_PAGER,
268                 ARG_MAN,
269                 ARG_GENERATORS,
270                 ARG_ITERATIONS,
271                 ARG_BASE_TIME,
272                 ARG_RECURSIVE_ERRORS,
273                 ARG_OFFLINE,
274                 ARG_THRESHOLD,
275                 ARG_SECURITY_POLICY,
276                 ARG_JSON,
277                 ARG_PROFILE,
278         };
279 
280         static const struct option options[] = {
281                 { "help",             no_argument,       NULL, 'h'                  },
282                 { "version",          no_argument,       NULL, ARG_VERSION          },
283                 { "quiet",            no_argument,       NULL, 'q'                  },
284                 { "order",            no_argument,       NULL, ARG_ORDER            },
285                 { "require",          no_argument,       NULL, ARG_REQUIRE          },
286                 { "root",             required_argument, NULL, ARG_ROOT             },
287                 { "image",            required_argument, NULL, ARG_IMAGE            },
288                 { "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
289                 { "offline",          required_argument, NULL, ARG_OFFLINE          },
290                 { "threshold",        required_argument, NULL, ARG_THRESHOLD        },
291                 { "security-policy",  required_argument, NULL, ARG_SECURITY_POLICY  },
292                 { "system",           no_argument,       NULL, ARG_SYSTEM           },
293                 { "user",             no_argument,       NULL, ARG_USER             },
294                 { "global",           no_argument,       NULL, ARG_GLOBAL           },
295                 { "from-pattern",     required_argument, NULL, ARG_DOT_FROM_PATTERN },
296                 { "to-pattern",       required_argument, NULL, ARG_DOT_TO_PATTERN   },
297                 { "fuzz",             required_argument, NULL, ARG_FUZZ             },
298                 { "no-pager",         no_argument,       NULL, ARG_NO_PAGER         },
299                 { "man",              optional_argument, NULL, ARG_MAN              },
300                 { "generators",       optional_argument, NULL, ARG_GENERATORS       },
301                 { "host",             required_argument, NULL, 'H'                  },
302                 { "machine",          required_argument, NULL, 'M'                  },
303                 { "iterations",       required_argument, NULL, ARG_ITERATIONS       },
304                 { "base-time",        required_argument, NULL, ARG_BASE_TIME        },
305                 { "unit",             required_argument, NULL, 'U'                  },
306                 { "json",             required_argument, NULL, ARG_JSON             },
307                 { "profile",          required_argument, NULL, ARG_PROFILE          },
308                 {}
309         };
310 
311         int r, c;
312 
313         assert(argc >= 0);
314         assert(argv);
315 
316         while ((c = getopt_long(argc, argv, "hH:M:U:", options, NULL)) >= 0)
317                 switch (c) {
318 
319                 case 'h':
320                         return help(0, NULL, NULL);
321 
322                 case ARG_VERSION:
323                         return version();
324 
325                 case 'q':
326                         arg_quiet = true;
327                         break;
328 
329                 case ARG_RECURSIVE_ERRORS:
330                         if (streq(optarg, "help")) {
331                                 DUMP_STRING_TABLE(recursive_errors, RecursiveErrors, _RECURSIVE_ERRORS_MAX);
332                                 return 0;
333                         }
334                         r = recursive_errors_from_string(optarg);
335                         if (r < 0)
336                                 return log_error_errno(r, "Unknown mode passed to --recursive-errors='%s'.", optarg);
337 
338                         arg_recursive_errors = r;
339                         break;
340 
341                 case ARG_ROOT:
342                         r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
343                         if (r < 0)
344                                 return r;
345                         break;
346 
347                 case ARG_IMAGE:
348                         r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
349                         if (r < 0)
350                                 return r;
351                         break;
352 
353                 case ARG_SYSTEM:
354                         arg_scope = LOOKUP_SCOPE_SYSTEM;
355                         break;
356 
357                 case ARG_USER:
358                         arg_scope = LOOKUP_SCOPE_USER;
359                         break;
360 
361                 case ARG_GLOBAL:
362                         arg_scope = LOOKUP_SCOPE_GLOBAL;
363                         break;
364 
365                 case ARG_ORDER:
366                         arg_dot = DEP_ORDER;
367                         break;
368 
369                 case ARG_REQUIRE:
370                         arg_dot = DEP_REQUIRE;
371                         break;
372 
373                 case ARG_DOT_FROM_PATTERN:
374                         if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
375                                 return log_oom();
376 
377                         break;
378 
379                 case ARG_DOT_TO_PATTERN:
380                         if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
381                                 return log_oom();
382 
383                         break;
384 
385                 case ARG_FUZZ:
386                         r = parse_sec(optarg, &arg_fuzz);
387                         if (r < 0)
388                                 return r;
389                         break;
390 
391                 case ARG_NO_PAGER:
392                         arg_pager_flags |= PAGER_DISABLE;
393                         break;
394 
395                 case 'H':
396                         arg_transport = BUS_TRANSPORT_REMOTE;
397                         arg_host = optarg;
398                         break;
399 
400                 case 'M':
401                         arg_transport = BUS_TRANSPORT_MACHINE;
402                         arg_host = optarg;
403                         break;
404 
405                 case ARG_MAN:
406                         r = parse_boolean_argument("--man", optarg, &arg_man);
407                         if (r < 0)
408                                 return r;
409                         break;
410 
411                 case ARG_GENERATORS:
412                         r = parse_boolean_argument("--generators", optarg, &arg_generators);
413                         if (r < 0)
414                                 return r;
415                         break;
416 
417                 case ARG_OFFLINE:
418                         r = parse_boolean_argument("--offline", optarg, &arg_offline);
419                         if (r < 0)
420                                 return r;
421                         break;
422 
423                 case ARG_THRESHOLD:
424                         r = safe_atou(optarg, &arg_threshold);
425                         if (r < 0 || arg_threshold > 100)
426                                 return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse threshold: %s", optarg);
427 
428                         break;
429 
430                 case ARG_SECURITY_POLICY:
431                         r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_security_policy);
432                         if (r < 0)
433                                 return r;
434                         break;
435 
436                 case ARG_JSON:
437                         r = parse_json_argument(optarg, &arg_json_format_flags);
438                         if (r <= 0)
439                                 return r;
440                         break;
441 
442                 case ARG_ITERATIONS:
443                         r = safe_atou(optarg, &arg_iterations);
444                         if (r < 0)
445                                 return log_error_errno(r, "Failed to parse iterations: %s", optarg);
446 
447                         break;
448 
449                 case ARG_BASE_TIME:
450                         r = parse_timestamp(optarg, &arg_base_time);
451                         if (r < 0)
452                                 return log_error_errno(r, "Failed to parse --base-time= parameter: %s", optarg);
453 
454                         break;
455 
456                 case ARG_PROFILE:
457                         if (isempty(optarg))
458                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Profile file name is empty");
459 
460                         if (is_path(optarg)) {
461                                 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_profile);
462                                 if (r < 0)
463                                         return r;
464                                 if (!endswith(arg_profile, ".conf"))
465                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Profile file name must end with .conf: %s", arg_profile);
466                         } else {
467                                 r = free_and_strdup(&arg_profile, optarg);
468                                 if (r < 0)
469                                         return log_oom();
470                         }
471 
472                         break;
473 
474                 case 'U': {
475                         _cleanup_free_ char *mangled = NULL;
476 
477                         r = unit_name_mangle(optarg, UNIT_NAME_MANGLE_WARN, &mangled);
478                         if (r < 0)
479                                 return log_error_errno(r, "Failed to mangle unit name %s: %m", optarg);
480 
481                         free_and_replace(arg_unit, mangled);
482                         break;
483                 }
484                 case '?':
485                         return -EINVAL;
486 
487                 default:
488                         assert_not_reached();
489                 }
490 
491         if (arg_offline && !streq_ptr(argv[optind], "security"))
492                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
493                                        "Option --offline= is only supported for security right now.");
494 
495         if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf"))
496                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
497                                        "Option --json= is only supported for security and inspect-elf right now.");
498 
499         if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
500                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
501                                        "Option --threshold= is only supported for security right now.");
502 
503         if (arg_scope == LOOKUP_SCOPE_GLOBAL &&
504             !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
505                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
506                                        "Option --global only makes sense with verbs dot, unit-paths, verify.");
507 
508         if (streq_ptr(argv[optind], "cat-config") && arg_scope == LOOKUP_SCOPE_USER)
509                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
510                                        "Option --user is not supported for cat-config right now.");
511 
512         if (arg_security_policy && !streq_ptr(argv[optind], "security"))
513                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
514                                        "Option --security-policy= is only supported for security.");
515 
516         if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify", "condition")) &&
517            (!(streq_ptr(argv[optind], "security") && arg_offline)))
518                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
519                                        "Options --root= and --image= are only supported for cat-config, verify, condition and security when used with --offline= right now.");
520 
521         /* Having both an image and a root is not supported by the code */
522         if (arg_root && arg_image)
523                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
524 
525         if (arg_unit && !streq_ptr(argv[optind], "condition"))
526                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --unit= is only supported for condition");
527 
528         if (streq_ptr(argv[optind], "condition") && !arg_unit && optind >= argc - 1)
529                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too few arguments for condition");
530 
531         if (streq_ptr(argv[optind], "condition") && arg_unit && optind < argc - 1)
532                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No conditions can be passed if --unit= is used.");
533 
534         return 1; /* work to do */
535 }
536 
run(int argc,char * argv[])537 static int run(int argc, char *argv[]) {
538         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
539         _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
540         _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
541 
542         static const Verb verbs[] = {
543                 { "help",              VERB_ANY, VERB_ANY, 0,            help                   },
544                 { "time",              VERB_ANY, 1,        VERB_DEFAULT, verb_time              },
545                 { "blame",             VERB_ANY, 1,        0,            verb_blame             },
546                 { "critical-chain",    VERB_ANY, VERB_ANY, 0,            verb_critical_chain    },
547                 { "plot",              VERB_ANY, 1,        0,            verb_plot              },
548                 { "dot",               VERB_ANY, VERB_ANY, 0,            verb_dot               },
549                 /* ↓ The following seven verbs are deprecated, from here … ↓ */
550                 { "log-level",         VERB_ANY, 2,        0,            verb_log_control       },
551                 { "log-target",        VERB_ANY, 2,        0,            verb_log_control       },
552                 { "set-log-level",     2,        2,        0,            verb_log_control       },
553                 { "get-log-level",     VERB_ANY, 1,        0,            verb_log_control       },
554                 { "set-log-target",    2,        2,        0,            verb_log_control       },
555                 { "get-log-target",    VERB_ANY, 1,        0,            verb_log_control       },
556                 { "service-watchdogs", VERB_ANY, 2,        0,            verb_service_watchdogs },
557                 /* ↑ … until here ↑ */
558                 { "dump",              VERB_ANY, 1,        0,            verb_dump              },
559                 { "cat-config",        2,        VERB_ANY, 0,            verb_cat_config        },
560                 { "unit-files",        VERB_ANY, VERB_ANY, 0,            verb_unit_files        },
561                 { "unit-paths",        1,        1,        0,            verb_unit_paths        },
562                 { "exit-status",       VERB_ANY, VERB_ANY, 0,            verb_exit_status       },
563                 { "syscall-filter",    VERB_ANY, VERB_ANY, 0,            verb_syscall_filters   },
564                 { "capability",        VERB_ANY, VERB_ANY, 0,            verb_capabilities      },
565                 { "filesystems",       VERB_ANY, VERB_ANY, 0,            verb_filesystems       },
566                 { "condition",         VERB_ANY, VERB_ANY, 0,            verb_condition         },
567                 { "verify",            2,        VERB_ANY, 0,            verb_verify            },
568                 { "calendar",          2,        VERB_ANY, 0,            verb_calendar          },
569                 { "timestamp",         2,        VERB_ANY, 0,            verb_timestamp         },
570                 { "timespan",          2,        VERB_ANY, 0,            verb_timespan          },
571                 { "security",          VERB_ANY, VERB_ANY, 0,            verb_security          },
572                 { "inspect-elf",       2,        VERB_ANY, 0,            verb_elf_inspection    },
573                 {}
574         };
575 
576         int r;
577 
578         setlocale(LC_ALL, "");
579         setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
580 
581         log_setup();
582 
583         r = parse_argv(argc, argv);
584         if (r <= 0)
585                 return r;
586 
587         /* Open up and mount the image */
588         if (arg_image) {
589                 assert(!arg_root);
590 
591                 r = mount_image_privately_interactively(
592                                 arg_image,
593                                 DISSECT_IMAGE_GENERIC_ROOT |
594                                 DISSECT_IMAGE_RELAX_VAR_CHECK |
595                                 DISSECT_IMAGE_READ_ONLY,
596                                 &unlink_dir,
597                                 &loop_device,
598                                 &decrypted_image);
599                 if (r < 0)
600                         return r;
601 
602                 arg_root = strdup(unlink_dir);
603                 if (!arg_root)
604                         return log_oom();
605         }
606 
607         return dispatch_verb(argc, argv, verbs, NULL);
608 }
609 
610 DEFINE_MAIN_FUNCTION(run);
611