diff --git a/man/systemctl.xml b/man/systemctl.xml index 570c1a5505..36495cbd33 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -753,6 +753,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err linked-runtime + + alias + The name is an alias (symlink to another unit file). + 0 + masked Completely disabled, so that any start operation on it fails (permanently in /etc/systemd/system/ or transiently in /run/systemd/systemd/). diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 3e2ae93bf0..4818f3423c 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -891,10 +891,11 @@ CapabilityBoundingSet=~CAP_B CAP_C ProtectSystem= Takes a boolean argument or the special values full or - strict. If true, mounts the /usr and /boot - directories read-only for processes invoked by this unit. If set to full, the - /etc directory is mounted read-only, too. If set to strict the entire - file system hierarchy is mounted read-only, except for the API file system subtrees /dev, + strict. If true, mounts the /usr and the boot loader + directories (/boot and /efi) read-only for processes + invoked by this unit. If set to full, the /etc directory is + mounted read-only, too. If set to strict the entire file system hierarchy is + mounted read-only, except for the API file system subtrees /dev, /proc and /sys (protect these directories using PrivateDevices=, ProtectKernelTunables=, ProtectControlGroups=). This setting ensures that any modification of the vendor-supplied diff --git a/src/core/unit.c b/src/core/unit.c index 8ef9e4fed5..19da4a2af9 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -4316,7 +4316,8 @@ int unit_get_unit_file_preset(Unit *u) { u->unit_file_preset = unit_file_query_preset( u->manager->unit_file_scope, NULL, - basename(u->fragment_path)); + basename(u->fragment_path), + NULL); return u->unit_file_preset; } diff --git a/src/shared/install.c b/src/shared/install.c index 236f279ffb..ec2b99ab98 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -55,16 +55,11 @@ typedef enum { PRESET_DISABLE, } PresetAction; -typedef struct { +struct UnitFilePresetRule { char *pattern; PresetAction action; char **instances; -} PresetRule; - -typedef struct { - PresetRule *rules; - size_t n_rules; -} Presets; +}; static bool unit_file_install_info_has_rules(const UnitFileInstallInfo *i) { assert(i); @@ -80,7 +75,7 @@ static bool unit_file_install_info_has_also(const UnitFileInstallInfo *i) { return !strv_isempty(i->also); } -static void presets_freep(Presets *p) { +void unit_file_presets_freep(UnitFilePresets *p) { size_t i; if (!p) @@ -1231,7 +1226,7 @@ static int unit_file_load( return -EINVAL; if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Unit type %s cannot be templated.", unit_type_to_string(type)); + "%s: unit type %s cannot be templated, ignoring.", path, unit_type_to_string(type)); if (!(flags & SEARCH_LOAD)) { r = lstat(path, &st); @@ -2772,6 +2767,12 @@ int unit_file_lookup_state( break; case UNIT_FILE_TYPE_REGULAR: + /* Check if the name we were querying is actually an alias */ + if (!streq(name, basename(i->path)) && !unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) { + state = UNIT_FILE_ALIAS; + break; + } + r = path_is_generator(paths, i->path); if (r < 0) return r; @@ -2913,8 +2914,8 @@ static int presets_find_config(UnitFileScope scope, const char *root_dir, char * return conf_files_list_strv(files, ".preset", root_dir, 0, dirs); } -static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) { - _cleanup_(presets_freep) Presets ps = {}; +static int read_presets(UnitFileScope scope, const char *root_dir, UnitFilePresets *presets) { + _cleanup_(unit_file_presets_freep) UnitFilePresets ps = {}; size_t n_allocated = 0; _cleanup_strv_free_ char **files = NULL; char **p; @@ -2942,7 +2943,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres for (;;) { _cleanup_free_ char *line = NULL; - PresetRule rule = {}; + UnitFilePresetRule rule = {}; const char *parameter; char *l; @@ -2972,7 +2973,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres continue; } - rule = (PresetRule) { + rule = (UnitFilePresetRule) { .pattern = unit_name, .action = PRESET_ENABLE, .instances = instances, @@ -2987,7 +2988,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres if (!pattern) return -ENOMEM; - rule = (PresetRule) { + rule = (UnitFilePresetRule) { .pattern = pattern, .action = PRESET_DISABLE, }; @@ -3005,14 +3006,15 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres } } + ps.initialized = true; *presets = ps; - ps = (Presets){}; + ps = (UnitFilePresets){}; return 0; } static int pattern_match_multiple_instances( - const PresetRule rule, + const UnitFilePresetRule rule, const char *unit_name, char ***ret) { @@ -3066,17 +3068,17 @@ static int pattern_match_multiple_instances( return 0; } -static int query_presets(const char *name, const Presets presets, char ***instance_name_list) { +static int query_presets(const char *name, const UnitFilePresets *presets, char ***instance_name_list) { PresetAction action = PRESET_UNKNOWN; size_t i; char **s; if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - for (i = 0; i < presets.n_rules; i++) - if (pattern_match_multiple_instances(presets.rules[i], name, instance_name_list) > 0 || - fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { - action = presets.rules[i].action; + for (i = 0; i < presets->n_rules; i++) + if (pattern_match_multiple_instances(presets->rules[i], name, instance_name_list) > 0 || + fnmatch(presets->rules[i].pattern, name, FNM_NOESCAPE) == 0) { + action = presets->rules[i].action; break; } @@ -3099,15 +3101,19 @@ static int query_presets(const char *name, const Presets presets, char ***instan } } -int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { - _cleanup_(presets_freep) Presets presets = {}; +int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) { + _cleanup_(unit_file_presets_freep) UnitFilePresets tmp = {}; int r; - r = read_presets(scope, root_dir, &presets); - if (r < 0) - return r; + if (!cached) + cached = &tmp; + if (!cached->initialized) { + r = read_presets(scope, root_dir, cached); + if (r < 0) + return r; + } - return query_presets(name, presets, NULL); + return query_presets(name, cached, NULL); } static int execute_preset( @@ -3162,7 +3168,7 @@ static int preset_prepare_one( InstallContext *minus, LookupPaths *paths, const char *name, - Presets presets, + const UnitFilePresets *presets, UnitFileChange **changes, size_t *n_changes) { @@ -3221,7 +3227,7 @@ int unit_file_preset( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_(lookup_paths_free) LookupPaths paths = {}; - _cleanup_(presets_freep) Presets presets = {}; + _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; const char *config_path; char **i; int r; @@ -3243,7 +3249,7 @@ int unit_file_preset( return r; STRV_FOREACH(i, files) { - r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes); + r = preset_prepare_one(scope, &plus, &minus, &paths, *i, &presets, changes, n_changes); if (r < 0) return r; } @@ -3261,7 +3267,7 @@ int unit_file_preset_all( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_(lookup_paths_free) LookupPaths paths = {}; - _cleanup_(presets_freep) Presets presets = {}; + _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; const char *config_path = NULL; char **i; int r; @@ -3305,7 +3311,7 @@ int unit_file_preset_all( continue; /* we don't pass changes[] in, because we want to handle errors on our own */ - r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0); + r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, &presets, NULL, 0); if (r == -ERFKILL) r = unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, de->d_name, NULL); @@ -3344,7 +3350,7 @@ int unit_file_get_list( char **patterns) { _cleanup_(lookup_paths_free) LookupPaths paths = {}; - char **i; + char **dirname; int r; assert(scope >= 0); @@ -3355,16 +3361,16 @@ int unit_file_get_list( if (r < 0) return r; - STRV_FOREACH(i, paths.search_path) { + STRV_FOREACH(dirname, paths.search_path) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; - d = opendir(*i); + d = opendir(*dirname); if (!d) { if (errno == ENOENT) continue; if (IN_SET(errno, ENOTDIR, EACCES)) { - log_debug_errno(errno, "Failed to open \"%s\": %m", *i); + log_debug_errno(errno, "Failed to open \"%s\": %m", *dirname); continue; } @@ -3392,7 +3398,7 @@ int unit_file_get_list( if (!f) return -ENOMEM; - f->path = path_make_absolute(de->d_name, *i); + f->path = path_make_absolute(de->d_name, *dirname); if (!f->path) return -ENOMEM; @@ -3416,34 +3422,35 @@ int unit_file_get_list( } static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { - [UNIT_FILE_ENABLED] = "enabled", + [UNIT_FILE_ENABLED] = "enabled", [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime", - [UNIT_FILE_LINKED] = "linked", - [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime", - [UNIT_FILE_MASKED] = "masked", - [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", - [UNIT_FILE_STATIC] = "static", - [UNIT_FILE_DISABLED] = "disabled", - [UNIT_FILE_INDIRECT] = "indirect", - [UNIT_FILE_GENERATED] = "generated", - [UNIT_FILE_TRANSIENT] = "transient", - [UNIT_FILE_BAD] = "bad", + [UNIT_FILE_LINKED] = "linked", + [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime", + [UNIT_FILE_ALIAS] = "alias", + [UNIT_FILE_MASKED] = "masked", + [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", + [UNIT_FILE_STATIC] = "static", + [UNIT_FILE_DISABLED] = "disabled", + [UNIT_FILE_INDIRECT] = "indirect", + [UNIT_FILE_GENERATED] = "generated", + [UNIT_FILE_TRANSIENT] = "transient", + [UNIT_FILE_BAD] = "bad", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = { - [UNIT_FILE_SYMLINK] = "symlink", - [UNIT_FILE_UNLINK] = "unlink", - [UNIT_FILE_IS_MASKED] = "masked", + [UNIT_FILE_SYMLINK] = "symlink", + [UNIT_FILE_UNLINK] = "unlink", + [UNIT_FILE_IS_MASKED] = "masked", [UNIT_FILE_IS_DANGLING] = "dangling", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = { - [UNIT_FILE_PRESET_FULL] = "full", - [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only", + [UNIT_FILE_PRESET_FULL] = "full", + [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only", [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only", }; diff --git a/src/shared/install.h b/src/shared/install.h index 54d22a45d3..5ff8bec165 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -189,7 +189,16 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char **ret_dst); -int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name); +typedef struct UnitFilePresetRule UnitFilePresetRule; + +typedef struct { + UnitFilePresetRule *rules; + size_t n_rules; + bool initialized; +} UnitFilePresets; + +void unit_file_presets_freep(UnitFilePresets *p); +int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name, UnitFilePresets *cached); const char *unit_file_state_to_string(UnitFileState s) _const_; UnitFileState unit_file_state_from_string(const char *s) _pure_; diff --git a/src/shared/unit-file.c b/src/shared/unit-file.c index 7ad0ecc8fd..7b64bbf7f1 100644 --- a/src/shared/unit-file.c +++ b/src/shared/unit-file.c @@ -320,7 +320,7 @@ int unit_file_build_name_map( /* We don't explicitly check for alias loops here. unit_ids_map_get() which * limits the number of hops should be used to access the map. */ - _cleanup_free_ char *target = NULL, *target_abs = NULL; + _cleanup_free_ char *target = NULL; r = readlinkat_malloc(dirfd(d), de->d_name, &target); if (r < 0) { @@ -329,8 +329,9 @@ int unit_file_build_name_map( continue; } - if (!path_is_absolute(target)) { - target_abs = path_join(*dir, target); + const bool is_abs = path_is_absolute(target); + if (lp->root_dir || !is_abs) { + char *target_abs = path_join(is_abs ? lp->root_dir : *dir, target); if (!target_abs) return log_oom(); diff --git a/src/shared/unit-file.h b/src/shared/unit-file.h index a44ba5b051..9d402e792a 100644 --- a/src/shared/unit-file.h +++ b/src/shared/unit-file.h @@ -16,6 +16,7 @@ enum UnitFileState { UNIT_FILE_ENABLED_RUNTIME, UNIT_FILE_LINKED, UNIT_FILE_LINKED_RUNTIME, + UNIT_FILE_ALIAS, UNIT_FILE_MASKED, UNIT_FILE_MASKED_RUNTIME, UNIT_FILE_STATIC, diff --git a/src/shared/verbs.c b/src/shared/verbs.c index 1d23ed48a2..d2744b6918 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -45,10 +45,19 @@ bool running_in_chroot_or_offline(void) { return r > 0; } +const Verb* verbs_find_verb(const char *name, const Verb verbs[]) { + for (size_t i = 0; verbs[i].dispatch; i++) + if (streq_ptr(name, verbs[i].verb) || + (!name && FLAGS_SET(verbs[i].flags, VERB_DEFAULT))) + return &verbs[i]; + + /* At the end of the list? */ + return NULL; +} + int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { const Verb *verb; const char *name; - unsigned i; int left; assert(verbs); @@ -62,31 +71,16 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { optind = 0; name = argv[0]; - for (i = 0;; i++) { - bool found; - - /* At the end of the list? */ - if (!verbs[i].dispatch) { - if (name) - log_error("Unknown operation %s.", name); - else - log_error("Requires operation parameter."); - return -EINVAL; - } - + verb = verbs_find_verb(name, verbs); + if (!verb) { if (name) - found = streq(name, verbs[i].verb); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unknown command verb %s.", name); else - found = verbs[i].flags & VERB_DEFAULT; - - if (found) { - verb = &verbs[i]; - break; - } + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Command verb required."); } - assert(verb); - if (!name) left = 1; @@ -101,10 +95,7 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { "Too many arguments."); if ((verb->flags & VERB_ONLINE_ONLY) && running_in_chroot_or_offline()) { - if (name) - log_info("Running in chroot, ignoring request: %s", name); - else - log_info("Running in chroot, ignoring request."); + log_info("Running in chroot, ignoring command '%s'", name ?: verb->verb); return 0; } diff --git a/src/shared/verbs.h b/src/shared/verbs.h index c5fe6cc7c5..b6a1afcdee 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -6,8 +6,8 @@ #define VERB_ANY ((unsigned) -1) typedef enum VerbFlags { - VERB_DEFAULT = 1 << 0, - VERB_ONLINE_ONLY = 1 << 1, + VERB_DEFAULT = 1 << 0, /* The verb to run if no verb is specified */ + VERB_ONLINE_ONLY = 1 << 1, /* Just do nothing when running in chroot or offline */ } VerbFlags; typedef struct { @@ -19,4 +19,5 @@ typedef struct { bool running_in_chroot_or_offline(void); +const Verb* verbs_find_verb(const char *name, const Verb verbs[]); int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index a658c65a08..03b4d1b783 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1456,9 +1456,18 @@ static bool output_show_unit_file(const UnitFileList *u, char **states, char **p return true; } +static bool show_preset_for_state(UnitFileState state) { + /* Don't show preset state in those unit file states, it'll only confuse users. */ + return !IN_SET(state, + UNIT_FILE_ALIAS, + UNIT_FILE_STATIC, + UNIT_FILE_GENERATED, + UNIT_FILE_TRANSIENT); +} + static int output_unit_file_list(const UnitFileList *units, unsigned c) { _cleanup_(table_unrefp) Table *table = NULL; - const UnitFileList *u; + _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; int r; table = table_new("unit file", "state", "vendor preset"); @@ -1469,9 +1478,8 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) { if (arg_full) table_set_width(table, 0); - for (u = units; u < units + c; u++) { + for (const UnitFileList *u = units; u < units + c; u++) { const char *on_underline = NULL, *on_unit_color = NULL, *id; - const char *on_preset_color = NULL, *unit_preset_str; bool underline; underline = u + 1 < units + c && @@ -1486,32 +1494,44 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) { UNIT_FILE_DISABLED, UNIT_FILE_BAD)) on_unit_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); - else if (u->state == UNIT_FILE_ENABLED) + else if (IN_SET(u->state, + UNIT_FILE_ENABLED, + UNIT_FILE_ALIAS)) on_unit_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green(); else on_unit_color = on_underline; id = basename(u->path); - r = unit_file_query_preset(arg_scope, NULL, id); - if (r < 0) { - unit_preset_str = "n/a"; - on_preset_color = underline ? on_underline : ansi_normal(); - } else if (r == 0) { - unit_preset_str = "disabled"; - on_preset_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); - } else { - unit_preset_str = "enabled"; - on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green(); - } - r = table_add_many(table, TABLE_STRING, id, TABLE_SET_COLOR, strempty(on_underline), TABLE_STRING, unit_file_state_to_string(u->state), - TABLE_SET_COLOR, strempty(on_unit_color), - TABLE_STRING, unit_preset_str, - TABLE_SET_COLOR, strempty(on_preset_color)); + TABLE_SET_COLOR, strempty(on_unit_color)); + if (r < 0) + return table_log_add_error(r); + + if (show_preset_for_state(u->state)) { + const char *unit_preset_str, *on_preset_color; + + r = unit_file_query_preset(arg_scope, arg_root, id, &presets); + if (r < 0) { + unit_preset_str = "n/a"; + on_preset_color = underline ? on_underline : ansi_normal(); + } else if (r == 0) { + unit_preset_str = "disabled"; + on_preset_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); + } else { + unit_preset_str = "enabled"; + on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green(); + } + + r = table_add_many(table, + TABLE_STRING, unit_preset_str, + TABLE_SET_COLOR, strempty(on_preset_color)); + } else + r = table_add_many(table, TABLE_EMPTY); + if (r < 0) return table_log_add_error(r); } @@ -4246,14 +4266,18 @@ static void print_status_info( if (!isempty(i->load_error)) printf(" Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error); - else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset) && - !STR_IN_SET(i->unit_file_state, "generated", "transient")) - printf(" Loaded: %s%s%s (%s; %s; vendor preset: %s)\n", - on, strna(i->load_state), off, path, i->unit_file_state, i->unit_file_preset); - else if (path && !isempty(i->unit_file_state)) - printf(" Loaded: %s%s%s (%s; %s)\n", - on, strna(i->load_state), off, path, i->unit_file_state); - else if (path) + else if (path && !isempty(i->unit_file_state)) { + bool show_preset = !isempty(i->unit_file_preset) && + show_preset_for_state(unit_file_state_from_string(i->unit_file_state)); + + printf(" Loaded: %s%s%s (%s; %s%s%s)\n", + on, strna(i->load_state), off, + path, + i->unit_file_state, + show_preset ? "; vendor preset: " : "", + show_preset ? i->unit_file_preset : ""); + + } else if (path) printf(" Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, path); else @@ -5892,7 +5916,8 @@ static int show(int argc, char *argv[], void *userdata) { if (show_mode == SYSTEMCTL_SHOW_HELP && argc <= 1) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "This command expects one or more unit names. Did you mean --help?"); + "'help' command expects one or more unit names.\n" + "(Alternatively, help for systemctl itself may be shown with --help)"); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -7367,6 +7392,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { UNIT_FILE_ENABLED, UNIT_FILE_ENABLED_RUNTIME, UNIT_FILE_STATIC, + UNIT_FILE_ALIAS, UNIT_FILE_INDIRECT, UNIT_FILE_GENERATED)) enabled = true; @@ -9211,9 +9237,9 @@ static int systemctl_main(int argc, char *argv[]) { { "help", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show }, { "daemon-reload", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload }, { "daemon-reexec", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload }, - { "log-level", VERB_ANY, 2, 0, log_level }, - { "log-target", VERB_ANY, 2, 0, log_target }, - { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs }, + { "log-level", VERB_ANY, 2, VERB_ONLINE_ONLY, log_level }, + { "log-target", VERB_ANY, 2, VERB_ONLINE_ONLY, log_target }, + { "service-watchdogs", VERB_ANY, 2, VERB_ONLINE_ONLY, service_watchdogs }, { "show-environment", VERB_ANY, 1, VERB_ONLINE_ONLY, show_environment }, { "set-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, set_environment }, { "unset-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, set_environment }, @@ -9253,6 +9279,12 @@ static int systemctl_main(int argc, char *argv[]) { {} }; + const Verb *verb = verbs_find_verb(argv[optind], verbs); + if (verb && (verb->flags & VERB_ONLINE_ONLY) && arg_root) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Verb '%s' cannot be used with --root=.", + argv[optind] ?: verb->verb); + return dispatch_verb(argc, argv, verbs, NULL); } diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index 79a105c5c3..515f14b8ca 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -36,20 +36,20 @@ static void test_basic_mask_and_enable(const char *root) { assert_se(symlink("a.service", p) >= 0); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); p = strjoina(root, "/usr/lib/systemd/system/c.service"); assert_se(symlink("/usr/lib/systemd/system/a.service", p) >= 0); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); p = strjoina(root, "/usr/lib/systemd/system/d.service"); assert_se(symlink("c.service", p) >= 0); /* This one is interesting, as d follows a relative, then an absolute symlink */ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); assert_se(unit_file_mask(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); @@ -89,9 +89,9 @@ static void test_basic_mask_and_enable(const char *root) { changes = NULL; n_changes = 0; assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Enabling it again should succeed but be a NOP */ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); @@ -108,9 +108,9 @@ static void test_basic_mask_and_enable(const char *root) { changes = NULL; n_changes = 0; assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Disabling a disabled unit must succeed but be a NOP */ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); @@ -129,9 +129,9 @@ static void test_basic_mask_and_enable(const char *root) { changes = NULL; n_changes = 0; assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Let's try to reenable */ @@ -147,9 +147,9 @@ static void test_basic_mask_and_enable(const char *root) { changes = NULL; n_changes = 0; assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); } static void test_linked_units(const char *root) { @@ -386,7 +386,7 @@ static void test_template_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); @@ -404,7 +404,7 @@ static void test_template_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); @@ -418,7 +418,7 @@ static void test_template_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); @@ -450,7 +450,7 @@ static void test_template_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); @@ -469,7 +469,7 @@ static void test_template_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); @@ -500,7 +500,7 @@ static void test_indirect(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS); assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); @@ -513,7 +513,7 @@ static void test_indirect(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS); assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); @@ -624,7 +624,7 @@ static void test_preset_and_list(const char *root) { got_no = true; assert_se(fl->state == UNIT_FILE_DISABLED); } else - assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT)); + assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT, UNIT_FILE_ALIAS)); } unit_file_list_free(h);