#include "alloc-util.h"
#include "analyze-condition.h"
+#include "analyze-elf.h"
#include "analyze-security.h"
#include "analyze-verify.h"
#include "bus-error.h"
#include "copy.h"
#include "def.h"
#include "exit-status.h"
+#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
+#include "filesystems.h"
#include "format-table.h"
#include "glob-util.h"
#include "hashmap.h"
#include "parse-util.h"
#include "path-util.h"
#include "pretty-print.h"
+#include "rm-rf.h"
#if HAVE_SECCOMP
# include "seccomp-util.h"
#endif
#include "sort-util.h"
#include "special.h"
+#include "stat-util.h"
#include "string-table.h"
#include "strv.h"
#include "strxcpyx.h"
#include "terminal-util.h"
#include "time-util.h"
+#include "tmpfile-util.h"
#include "unit-name.h"
#include "util.h"
+#include "verb-log-control.h"
#include "verbs.h"
#include "version.h"
static bool arg_generators = false;
static char *arg_root = NULL;
static char *arg_image = NULL;
+static char *arg_security_policy = NULL;
static bool arg_offline = false;
static unsigned arg_threshold = 100;
static unsigned arg_iterations = 1;
static usec_t arg_base_time = USEC_INFINITY;
+static char *arg_unit = NULL;
+static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
+static bool arg_quiet = false;
+static char *arg_profile = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_unit, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_profile, freep);
typedef struct BootTimes {
usec_t firmware_time;
return CMP(a->activating, b->activating);
}
+static int process_aliases(char *argv[], char *tempdir, char ***ret) {
+ _cleanup_strv_free_ char **filenames = NULL;
+ char **filename;
+ int r;
+
+ assert(argv);
+ assert(tempdir);
+ assert(ret);
+
+ STRV_FOREACH(filename, strv_skip(argv, 1)) {
+ _cleanup_free_ char *src = NULL, *dst = NULL, *base = NULL;
+ const char *parse_arg;
+
+ parse_arg = *filename;
+ r = extract_first_word(&parse_arg, &src, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE);
+ if (r < 0)
+ return r;
+
+ if (!parse_arg) {
+ r = strv_consume(&filenames, TAKE_PTR(src));
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
+ r = path_extract_filename(parse_arg, &base);
+ if (r < 0)
+ return r;
+
+ dst = path_join(tempdir, base);
+ if (!dst)
+ return -ENOMEM;
+
+ r = copy_file(src, dst, 0, 0644, 0, 0, COPY_REFLINK);
+ if (r < 0)
+ return r;
+
+ r = strv_consume(&filenames, TAKE_PTR(dst));
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(filenames);
+ return 0;
+}
+
static UnitTimes* unit_times_free_array(UnitTimes *t) {
for (UnitTimes *p = t; p && p->has_data; p++)
free(p->name);
r = acquire_bus(&bus, &use_full_bus);
if (r < 0)
- return bus_log_connect_error(r);
+ return bus_log_connect_error(r, arg_transport);
n = acquire_boot_times(bus, &boot);
if (n < 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return bus_log_connect_error(r);
+ return bus_log_connect_error(r, arg_transport);
n = acquire_time_data(bus, ×);
if (n <= 0)
}
unit_times_hashmap = h;
- (void) pager_open(arg_pager_flags);
+ pager_open(arg_pager_flags);
puts("The time when unit became active or started is printed after the \"@\" character.\n"
"The time the unit took to start is printed after the \"+\" character.\n");
r = acquire_bus(&bus, NULL);
if (r < 0)
- return bus_log_connect_error(r);
+ return bus_log_connect_error(r, arg_transport);
n = acquire_time_data(bus, ×);
if (n <= 0)
return table_log_add_error(r);
}
- (void) pager_open(arg_pager_flags);
+ pager_open(arg_pager_flags);
return table_print(table, NULL);
}
r = acquire_bus(&bus, NULL);
if (r < 0)
- return bus_log_connect_error(r);
+ return bus_log_connect_error(r, arg_transport);
r = pretty_boot_time(bus, &buf);
if (r < 0)
r = acquire_bus(&bus, NULL);
if (r < 0)
- return bus_log_connect_error(r);
+ return bus_log_connect_error(r, arg_transport);
r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
if (r < 0)
" red = Conflicts\n"
" green = After\n");
- if (on_tty())
+ if (on_tty() && !arg_quiet)
log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
"-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
r = acquire_bus(&bus, NULL);
if (r < 0)
- return bus_log_connect_error(r);
+ return bus_log_connect_error(r, arg_transport);
- (void) pager_open(arg_pager_flags);
+ pager_open(arg_pager_flags);
if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD))
return dump_fallback(bus);
char **arg, **list;
int r;
- (void) pager_open(arg_pager_flags);
+ pager_open(arg_pager_flags);
list = strv_skip(argv, 1);
STRV_FOREACH(arg, list) {
return 0;
}
-static int set_log_level(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- int r;
-
- assert(argc == 2);
- assert(argv);
-
- r = acquire_bus(&bus, NULL);
- if (r < 0)
- return bus_log_connect_error(r);
-
- r = bus_set_property(bus, bus_systemd_mgr, "LogLevel", &error, "s", argv[1]);
- if (r < 0)
- return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
-
- return 0;
-}
-
-static int get_log_level(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_free_ char *level = NULL;
- int r;
-
- r = acquire_bus(&bus, NULL);
- if (r < 0)
- return bus_log_connect_error(r);
-
- r = bus_get_property_string(bus, bus_systemd_mgr, "LogLevel", &error, &level);
- if (r < 0)
- return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
-
- puts(level);
- return 0;
-}
-
-static int get_or_set_log_level(int argc, char *argv[], void *userdata) {
- return (argc == 1) ? get_log_level(argc, argv, userdata) : set_log_level(argc, argv, userdata);
-}
-
-static int set_log_target(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+static int verb_log_control(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
- assert(argc == 2);
- assert(argv);
-
- r = acquire_bus(&bus, NULL);
- if (r < 0)
- return bus_log_connect_error(r);
-
- r = bus_set_property(bus, bus_systemd_mgr, "LogTarget", &error, "s", argv[1]);
- if (r < 0)
- return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
-
- return 0;
-}
-
-static int get_log_target(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_free_ char *target = NULL;
- int r;
+ assert(IN_SET(argc, 1, 2));
r = acquire_bus(&bus, NULL);
if (r < 0)
- return bus_log_connect_error(r);
-
- r = bus_get_property_string(bus, bus_systemd_mgr, "LogTarget", &error, &target);
- if (r < 0)
- return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
+ return bus_log_connect_error(r, arg_transport);
- puts(target);
- return 0;
-}
-
-static int get_or_set_log_target(int argc, char *argv[], void *userdata) {
- return (argc == 1) ? get_log_target(argc, argv, userdata) : set_log_target(argc, argv, userdata);
+ return verb_log_control_common(bus, "org.freedesktop.systemd1", argv[0], argc == 2 ? argv[1] : NULL);
}
static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int flags) {
return table_log_add_error(r);
}
- (void) pager_open(arg_pager_flags);
+ pager_open(arg_pager_flags);
return table_print(table, NULL);
}
(void) table_set_sort(table, (size_t) 1);
}
- (void) pager_open(arg_pager_flags);
+ pager_open(arg_pager_flags);
return table_print(table, NULL);
}
static void syscall_set_remove(Set *s, const SyscallFilterSet *set) {
const char *syscall;
+ if (!set)
+ return;
+
NULSTR_FOREACH(syscall, set->value) {
if (syscall[0] == '@')
continue;
static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
bool first = true;
- (void) pager_open(arg_pager_flags);
+ pager_open(arg_pager_flags);
if (strv_isempty(strv_skip(argv, 1))) {
_cleanup_set_free_ Set *kernel = NULL, *known = NULL;
const char *sys;
- int k;
+ int k = 0; /* explicit initialization to appease gcc */
NULSTR_FOREACH(sys, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value)
if (set_put_strdup(&known, sys) < 0)
return log_oom();
- k = load_kernel_syscalls(&kernel);
+ if (!arg_quiet)
+ k = load_kernel_syscalls(&kernel);
for (int i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
const SyscallFilterSet *set = syscall_filter_sets + i;
first = false;
}
+ if (arg_quiet) /* Let's not show the extra stuff in quiet mode */
+ return 0;
+
if (!set_isempty(known)) {
_cleanup_free_ char **l = NULL;
char **syscall;
if (k < 0) {
fputc('\n', stdout);
fflush(stdout);
- log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
+ if (!arg_quiet)
+ log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
} else if (!set_isempty(kernel)) {
_cleanup_free_ char **l = NULL;
char **syscall;
}
#endif
+static int load_available_kernel_filesystems(Set **ret) {
+ _cleanup_set_free_ Set *filesystems = NULL;
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(ret);
+
+ /* Let's read the available filesystems */
+
+ r = read_virtual_file("/proc/filesystems", SIZE_MAX, &t, NULL);
+ if (r < 0)
+ return r;
+
+ for (int i = 0;;) {
+ _cleanup_free_ char *line = NULL;
+ const char *p;
+
+ r = string_extract_line(t, i++, &line);
+ if (r < 0)
+ return log_oom();
+ if (r == 0)
+ break;
+
+ if (!line)
+ line = t;
+
+ p = strchr(line, '\t');
+ if (!p)
+ continue;
+
+ p += strspn(p, WHITESPACE);
+
+ r = set_put_strdup(&filesystems, p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add filesystem to list: %m");
+ }
+
+ *ret = TAKE_PTR(filesystems);
+ return 0;
+}
+
+static void filesystem_set_remove(Set *s, const FilesystemSet *set) {
+ const char *filesystem;
+
+ NULSTR_FOREACH(filesystem, set->value) {
+ if (filesystem[0] == '@')
+ continue;
+
+ free(set_remove(s, filesystem));
+ }
+}
+
+static void dump_filesystem_set(const FilesystemSet *set) {
+ const char *filesystem;
+ int r;
+
+ if (!set)
+ return;
+
+ printf("%s%s%s\n"
+ " # %s\n",
+ ansi_highlight(),
+ set->name,
+ ansi_normal(),
+ set->help);
+
+ NULSTR_FOREACH(filesystem, set->value) {
+ const statfs_f_type_t *magic;
+
+ if (filesystem[0] == '@') {
+ printf(" %s%s%s\n", ansi_underline(), filesystem, ansi_normal());
+ continue;
+ }
+
+ r = fs_type_from_string(filesystem, &magic);
+ assert_se(r >= 0);
+
+ printf(" %s", filesystem);
+
+ for (size_t i = 0; magic[i] != 0; i++) {
+ const char *primary;
+ if (i == 0)
+ printf(" %s(magic: ", ansi_grey());
+ else
+ printf(", ");
+
+ printf("0x%llx", (unsigned long long) magic[i]);
+
+ primary = fs_type_to_string(magic[i]);
+ if (primary && !streq(primary, filesystem))
+ printf("[%s]", primary);
+
+ if (magic[i+1] == 0)
+ printf(")%s", ansi_normal());
+ }
+
+ printf("\n");
+ }
+}
+
+static int dump_filesystems(int argc, char *argv[], void *userdata) {
+ bool first = true;
+
+#if ! HAVE_LIBBPF
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with libbpf support, sorry.");
+#endif
+
+ pager_open(arg_pager_flags);
+
+ if (strv_isempty(strv_skip(argv, 1))) {
+ _cleanup_set_free_ Set *kernel = NULL, *known = NULL;
+ const char *fs;
+ int k;
+
+ NULSTR_FOREACH(fs, filesystem_sets[FILESYSTEM_SET_KNOWN].value)
+ if (set_put_strdup(&known, fs) < 0)
+ return log_oom();
+
+ k = load_available_kernel_filesystems(&kernel);
+
+ for (FilesystemGroups i = 0; i < _FILESYSTEM_SET_MAX; i++) {
+ const FilesystemSet *set = filesystem_sets + i;
+ if (!first)
+ puts("");
+
+ dump_filesystem_set(set);
+ filesystem_set_remove(kernel, set);
+ if (i != FILESYSTEM_SET_KNOWN)
+ filesystem_set_remove(known, set);
+ first = false;
+ }
+
+ if (arg_quiet) /* Let's not show the extra stuff in quiet mode */
+ return 0;
+
+ if (!set_isempty(known)) {
+ _cleanup_free_ char **l = NULL;
+ char **filesystem;
+
+ printf("\n"
+ "# %sUngrouped filesystems%s (known but not included in any of the groups except @known):\n",
+ ansi_highlight(), ansi_normal());
+
+ l = set_get_strv(known);
+ if (!l)
+ return log_oom();
+
+ strv_sort(l);
+
+ STRV_FOREACH(filesystem, l) {
+ const statfs_f_type_t *magic;
+ bool is_primary = false;
+
+ assert_se(fs_type_from_string(*filesystem, &magic) >= 0);
+
+ for (size_t i = 0; magic[i] != 0; i++) {
+ const char *primary;
+
+ primary = fs_type_to_string(magic[i]);
+ assert(primary);
+
+ if (streq(primary, *filesystem))
+ is_primary = true;
+ }
+
+ if (!is_primary) {
+ log_debug("Skipping ungrouped file system '%s', because it's an alias for another one.", *filesystem);
+ continue;
+ }
+
+ printf("# %s\n", *filesystem);
+ }
+ }
+
+ if (k < 0) {
+ fputc('\n', stdout);
+ fflush(stdout);
+ log_notice_errno(k, "# Not showing unlisted filesystems, couldn't retrieve kernel filesystem list: %m");
+ } else if (!set_isempty(kernel)) {
+ _cleanup_free_ char **l = NULL;
+ char **filesystem;
+
+ printf("\n"
+ "# %sUnlisted filesystems%s (available to the local kernel, but not included in any of the groups listed above):\n",
+ ansi_highlight(), ansi_normal());
+
+ l = set_get_strv(kernel);
+ if (!l)
+ return log_oom();
+
+ strv_sort(l);
+
+ STRV_FOREACH(filesystem, l)
+ printf("# %s\n", *filesystem);
+ }
+ } else {
+ char **name;
+
+ STRV_FOREACH(name, strv_skip(argv, 1)) {
+ const FilesystemSet *set;
+
+ if (!first)
+ puts("");
+
+ set = filesystem_set_find(*name);
+ if (!set) {
+ /* make sure the error appears below normal output */
+ fflush(stdout);
+
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Filesystem set \"%s\" not found.", *name);
+ }
+
+ dump_filesystem_set(set);
+ first = false;
+ }
+ }
+
+ return 0;
+}
+
static void parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) {
if (calendar && calendar_spec_from_string(p, NULL) >= 0)
log_notice("Hint: this expression is a valid calendar specification. "
putchar('\n');
}
- return EXIT_SUCCESS;
+ return 0;
}
static int test_timestamp_one(const char *p) {
r = acquire_bus(&bus, NULL);
if (r < 0)
- return bus_log_connect_error(r);
+ return bus_log_connect_error(r, arg_transport);
if (argc == 1) {
/* get ServiceWatchdogs */
}
static int do_condition(int argc, char *argv[], void *userdata) {
- return verify_conditions(strv_skip(argv, 1), arg_scope);
+ return verify_conditions(strv_skip(argv, 1), arg_scope, arg_unit, arg_root);
}
static int do_verify(int argc, char *argv[], void *userdata) {
- return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root);
+ _cleanup_strv_free_ char **filenames = NULL;
+ _cleanup_(rm_rf_physical_and_freep) char *tempdir = NULL;
+ int r;
+
+ r = mkdtemp_malloc("/tmp/systemd-analyze-XXXXXX", &tempdir);
+ if (r < 0)
+ return log_error_errno(r, "Failed to setup working directory: %m");
+
+ r = process_aliases(argv, tempdir, &filenames);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't process aliases: %m");
+
+ return verify_units(filenames, arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root);
}
static int do_security(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *policy = NULL;
int r;
+ unsigned line, column;
r = acquire_bus(&bus, NULL);
if (r < 0)
- return bus_log_connect_error(r);
+ return bus_log_connect_error(r, arg_transport);
- (void) pager_open(arg_pager_flags);
+ pager_open(arg_pager_flags);
- return analyze_security(bus, strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_offline, arg_threshold, arg_root, 0);
+ if (arg_security_policy) {
+ r = json_parse_file(/*f=*/ NULL, arg_security_policy, /*flags=*/ 0, &policy, &line, &column);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column);
+ } else {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *pp = NULL;
+
+ r = search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL, CONF_PATHS_NULSTR("systemd"), &f, &pp);
+ if (r < 0 && r != -ENOENT)
+ return r;
+
+ if (f) {
+ r = json_parse_file(f, pp, /*flags=*/ 0, &policy, &line, &column);
+ if (r < 0)
+ return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column);
+ }
+ }
+
+ return analyze_security(bus,
+ strv_skip(argv, 1),
+ policy,
+ arg_scope,
+ arg_man,
+ arg_generators,
+ arg_offline,
+ arg_threshold,
+ arg_root,
+ arg_profile,
+ arg_json_format_flags,
+ arg_pager_flags,
+ /*flags=*/ 0);
+}
+
+static int do_elf_inspection(int argc, char *argv[], void *userdata) {
+ pager_open(arg_pager_flags);
+
+ return analyze_elf(strv_skip(argv, 1), arg_json_format_flags);
}
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL, *dot_link = NULL;
int r;
- (void) pager_open(arg_pager_flags);
+ pager_open(arg_pager_flags);
r = terminal_urlify_man("systemd-analyze", "1", &link);
if (r < 0)
" unit-paths List load directories for units\n"
" exit-status [STATUS...] List exit status definitions\n"
" capability [CAP...] List capability definitions\n"
- " syscall-filter [NAME...] Print list of syscalls in seccomp\n"
- " filter\n"
+ " syscall-filter [NAME...] List syscalls in seccomp filters\n"
+ " filesystems [NAME...] List known filesystems\n"
" condition CONDITION... Evaluate conditions and asserts\n"
" verify FILE... Check unit files for correctness\n"
" calendar SPEC... Validate repetitive calendar time\n"
" timestamp TIMESTAMP... Validate a timestamp\n"
" timespan SPAN... Validate a time span\n"
" security [UNIT...] Analyze security of unit\n"
+ " inspect-elf FILE... Parse and print ELF package metadata\n"
"\nOptions:\n"
- " -h --help Show this help\n"
" --recursive-errors=MODE Control which units are verified\n"
" --offline=BOOL Perform a security review on unit file(s)\n"
" --threshold=N Exit with a non-zero status when overall\n"
" exposure level is over threshold value\n"
- " --version Show package version\n"
+ " --security-policy=PATH Use custom JSON security policy instead\n"
+ " of built-in one\n"
+ " --json=pretty|short|off Generate JSON output of the security\n"
+ " analysis table\n"
" --no-pager Do not pipe output into a pager\n"
" --system Operate on system systemd instance\n"
" --user Operate on user systemd instance\n"
" --iterations=N Show the specified number of iterations\n"
" --base-time=TIMESTAMP Calculate calendar times relative to\n"
" specified time\n"
+ " --profile=name|PATH Include the specified profile in the\n"
+ " security review of the unit(s)\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " -q --quiet Do not emit hints\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
ARG_RECURSIVE_ERRORS,
ARG_OFFLINE,
ARG_THRESHOLD,
+ ARG_SECURITY_POLICY,
+ ARG_JSON,
+ ARG_PROFILE,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
+ { "quiet", no_argument, NULL, 'q' },
{ "order", no_argument, NULL, ARG_ORDER },
{ "require", no_argument, NULL, ARG_REQUIRE },
{ "root", required_argument, NULL, ARG_ROOT },
{ "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
{ "offline", required_argument, NULL, ARG_OFFLINE },
{ "threshold", required_argument, NULL, ARG_THRESHOLD },
+ { "security-policy", required_argument, NULL, ARG_SECURITY_POLICY },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{ "global", no_argument, NULL, ARG_GLOBAL },
{ "machine", required_argument, NULL, 'M' },
{ "iterations", required_argument, NULL, ARG_ITERATIONS },
{ "base-time", required_argument, NULL, ARG_BASE_TIME },
+ { "unit", required_argument, NULL, 'U' },
+ { "json", required_argument, NULL, ARG_JSON },
+ { "profile", required_argument, NULL, ARG_PROFILE },
{}
};
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hH:M:U:", options, NULL)) >= 0)
switch (c) {
case 'h':
return help(0, NULL, NULL);
+ case ARG_VERSION:
+ return version();
+
+ case 'q':
+ arg_quiet = true;
+ break;
+
case ARG_RECURSIVE_ERRORS:
if (streq(optarg, "help")) {
DUMP_STRING_TABLE(recursive_errors, RecursiveErrors, _RECURSIVE_ERRORS_MAX);
arg_recursive_errors = r;
break;
- case ARG_VERSION:
- return version();
-
case ARG_ROOT:
r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
if (r < 0)
break;
+ case ARG_SECURITY_POLICY:
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_security_policy);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_JSON:
+ r = parse_json_argument(optarg, &arg_json_format_flags);
+ if (r <= 0)
+ return r;
+ break;
+
case ARG_ITERATIONS:
r = safe_atou(optarg, &arg_iterations);
if (r < 0)
break;
+ case ARG_PROFILE:
+ if (isempty(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Profile file name is empty");
+
+ if (is_path(optarg)) {
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_profile);
+ if (r < 0)
+ return r;
+ if (!endswith(arg_profile, ".conf"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Profile file name must end with .conf: %s", arg_profile);
+ } else {
+ r = free_and_strdup(&arg_profile, optarg);
+ if (r < 0)
+ return log_oom();
+ }
+
+ break;
+
+ case 'U': {
+ _cleanup_free_ char *mangled = NULL;
+
+ r = unit_name_mangle(optarg, UNIT_NAME_MANGLE_WARN, &mangled);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name %s: %m", optarg);
+
+ free_and_replace(arg_unit, mangled);
+ break;
+ }
case '?':
return -EINVAL;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --offline= is only supported for security right now.");
+ if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --json= is only supported for security right now.");
+
if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --threshold= is only supported for security right now.");
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --user is not supported for cat-config right now.");
- if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify")) &&
+ if (arg_security_policy && !streq_ptr(argv[optind], "security"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --security-policy= is only supported for security.");
+
+ if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify", "condition")) &&
(!(streq_ptr(argv[optind], "security") && arg_offline)))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Options --root= and --image= are only supported for cat-config, verify and security when used with --offline= right now.");
+ "Options --root= and --image= are only supported for cat-config, verify, condition and security when used with --offline= right now.");
/* Having both an image and a root is not supported by the code */
if (arg_root && arg_image)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
+ if (arg_unit && !streq_ptr(argv[optind], "condition"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --unit= is only supported for condition");
+
+ if (streq_ptr(argv[optind], "condition") && !arg_unit && optind >= argc - 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too few arguments for condition");
+
+ if (streq_ptr(argv[optind], "condition") && arg_unit && optind < argc - 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No conditions can be passed if --unit= is used.");
+
return 1; /* work to do */
}
{ "plot", VERB_ANY, 1, 0, analyze_plot },
{ "dot", VERB_ANY, VERB_ANY, 0, dot },
/* The following seven verbs are deprecated */
- { "log-level", VERB_ANY, 2, 0, get_or_set_log_level },
- { "log-target", VERB_ANY, 2, 0, get_or_set_log_target },
- { "set-log-level", 2, 2, 0, set_log_level },
- { "get-log-level", VERB_ANY, 1, 0, get_log_level },
- { "set-log-target", 2, 2, 0, set_log_target },
- { "get-log-target", VERB_ANY, 1, 0, get_log_target },
+ { "log-level", VERB_ANY, 2, 0, verb_log_control },
+ { "log-target", VERB_ANY, 2, 0, verb_log_control },
+ { "set-log-level", 2, 2, 0, verb_log_control },
+ { "get-log-level", VERB_ANY, 1, 0, verb_log_control },
+ { "set-log-target", 2, 2, 0, verb_log_control },
+ { "get-log-target", VERB_ANY, 1, 0, verb_log_control },
{ "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
{ "dump", VERB_ANY, 1, 0, dump },
{ "cat-config", 2, VERB_ANY, 0, cat_config },
{ "exit-status", VERB_ANY, VERB_ANY, 0, dump_exit_status },
{ "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
{ "capability", VERB_ANY, VERB_ANY, 0, dump_capabilities },
- { "condition", 2, VERB_ANY, 0, do_condition },
+ { "filesystems", VERB_ANY, VERB_ANY, 0, dump_filesystems },
+ { "condition", VERB_ANY, VERB_ANY, 0, do_condition },
{ "verify", 2, VERB_ANY, 0, do_verify },
{ "calendar", 2, VERB_ANY, 0, test_calendar },
{ "timestamp", 2, VERB_ANY, 0, test_timestamp },
{ "timespan", 2, VERB_ANY, 0, dump_timespan },
{ "security", VERB_ANY, VERB_ANY, 0, do_security },
+ { "inspect-elf", 2, VERB_ANY, 0, do_elf_inspection },
{}
};