From 4f9a91055ce83be9b6a81bdeab6d360f13ff897a Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sun, 2 Aug 2015 14:22:10 -0400 Subject: systemctl: add --value option With this option, systemctl will only print the rhs in show: $ systemctl show -p Wants,After systemd-journald --value systemd-journald.socket ... systemd-journald-dev-log.socket ... This is useful in scripts, because the need to call awk or similar is removed. --- man/systemctl.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'man/systemctl.xml') diff --git a/man/systemctl.xml b/man/systemctl.xml index 1480bf8380..089fb0f5c3 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -232,6 +232,16 @@ + + + + + When printing properties with show, + only print the value, and skip the property name and + =. + + + -- cgit v1.2.3-54-g00ecf From f413930863ab3b98cb7bf9e761081b4e88a5d7d9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 24 Feb 2016 15:44:46 +0100 Subject: core: add a new unit file state "generated" Now that we store the generator directories in LookupPaths we can use this to intrdouce a new unit file state called "generated", for units in these directories. Fixes: #2348 --- man/systemctl.xml | 13 +++++++++---- src/shared/install.c | 23 +++++++++++++++++++++++ src/shared/install.h | 1 + src/systemctl/systemctl.c | 7 ++++--- 4 files changed, 37 insertions(+), 7 deletions(-) (limited to 'man/systemctl.xml') diff --git a/man/systemctl.xml b/man/systemctl.xml index 089fb0f5c3..0febdfd4de 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1168,22 +1168,27 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service static - The unit file is not enabled, and has no provisions for enabling in the [Install] section. + The unit file is not enabled, and has no provisions for enabling in the [Install] unit file section. 0 indirect - The unit file itself is not enabled, but it has a non-empty Also= setting in the [Install] section, listing other unit files that might be enabled. + The unit file itself is not enabled, but it has a non-empty Also= setting in the [Install] unit file section, listing other unit files that might be enabled. 0 disabled - Unit file is not enabled, but contains an [Install] section with installation instructions. + The unit file is not enabled, but contains an [Install] section with installation instructions. > 0 + + generated + The unit file was generated dynamically via a generator tool. See systemd.generator7. Generated unit files may not be enabled, they are enabled implicitly by their generator. + 0 + bad - Unit file is invalid or another error occurred. Note that is-enabled will not actually return this state, but print an error message instead. However the unit file listing printed by list-unit-files might show it. + The unit file is invalid or another error occurred. Note that is-enabled will not actually return this state, but print an error message instead. However the unit file listing printed by list-unit-files might show it. > 0 diff --git a/src/shared/install.c b/src/shared/install.c index e232d76dd7..202d16e129 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -82,6 +82,20 @@ static int in_search_path(const char *path, char **search) { return false; } +static int unit_file_is_generated(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + + assert(path); + + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + return path_equal(p->generator, parent) || + path_equal(p->generator_early, parent) || + path_equal(p->generator_late, parent); +} + static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) { char *p = NULL; int r; @@ -2023,6 +2037,14 @@ int unit_file_lookup_state( break; case UNIT_FILE_TYPE_REGULAR: + r = unit_file_is_generated(paths, i->path); + if (r < 0) + return r; + if (r > 0) { + state = UNIT_FILE_GENERATED; + break; + } + r = find_symlinks_in_scope(scope, root_dir, i->name, &state); if (r < 0) return r; @@ -2453,6 +2475,7 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { [UNIT_FILE_STATIC] = "static", [UNIT_FILE_DISABLED] = "disabled", [UNIT_FILE_INDIRECT] = "indirect", + [UNIT_FILE_GENERATED] = "generated", [UNIT_FILE_BAD] = "bad", }; diff --git a/src/shared/install.h b/src/shared/install.h index 82b6d425eb..18aad65d50 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -54,6 +54,7 @@ enum UnitFileState { UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_INDIRECT, + UNIT_FILE_GENERATED, UNIT_FILE_BAD, _UNIT_FILE_STATE_MAX, _UNIT_FILE_STATE_INVALID = -1 diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 4d0b4754ae..6394b4749e 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -5783,7 +5783,8 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { UNIT_FILE_ENABLED, UNIT_FILE_ENABLED_RUNTIME, UNIT_FILE_STATIC, - UNIT_FILE_INDIRECT)) + UNIT_FILE_INDIRECT, + UNIT_FILE_GENERATED)) enabled = true; if (!arg_quiet) @@ -5818,7 +5819,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect")) + if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect", "generated")) enabled = true; if (!arg_quiet) @@ -5826,7 +5827,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { } } - return !enabled; + return enabled ? EXIT_SUCCESS : EXIT_FAILURE; } static int is_system_running(int argc, char *argv[], void *userdata) { -- cgit v1.2.3-54-g00ecf From e4fca67ff02c44216780c5a61b1ab66cb6c09752 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 7 Mar 2016 19:07:30 +0100 Subject: install: introduce a new unit file state "transient" Now, that the search path logic knows the unit path for transient units we also can introduce an explicit unit file state "transient" that clarifies to the user what kind of unit file he is encountering. --- man/systemctl.xml | 5 +++++ src/core/dbus-manager.c | 4 ++-- src/shared/install.c | 32 ++++++++++++++++++++++++++++++++ src/shared/install.h | 1 + src/systemctl/systemctl.c | 4 ++-- 5 files changed, 42 insertions(+), 4 deletions(-) (limited to 'man/systemctl.xml') diff --git a/man/systemctl.xml b/man/systemctl.xml index 0febdfd4de..064777810f 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1186,6 +1186,11 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service The unit file was generated dynamically via a generator tool. See systemd.generator7. Generated unit files may not be enabled, they are enabled implicitly by their generator. 0 + + transient + The unit file has been created dynamically with the runtime API. Transient units may not be enabled. + 0 + bad The unit file is invalid or another error occurred. Note that is-enabled will not actually return this state, but print an error message instead. However the unit file listing printed by list-unit-files might show it. diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 5fc3526751..f0ee2fcb36 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1641,7 +1641,7 @@ static int method_enable_unit_files_generic( if (r == -ESHUTDOWN) return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked."); if (r == -EADDRNOTAVAIL) - return sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, "Unit file is generated."); + return sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, "Unit file is transient or generated."); if (r < 0) return r; @@ -1866,7 +1866,7 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd if (r == -ESHUTDOWN) return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked."); if (r == -EADDRNOTAVAIL) - return sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, "Unit file is generated."); + return sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, "Unit file is transient or generated."); if (r < 0) return r; diff --git a/src/shared/install.c b/src/shared/install.c index 6c5d3ce7d1..c19c85a3b5 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -119,6 +119,19 @@ static int path_is_generator(const LookupPaths *p, const char *path) { path_equal(p->generator_late, parent); } +static int path_is_transient(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + + assert(p); + assert(path); + + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + return path_equal(p->transient, parent); +} + static int path_is_config(const LookupPaths *p, const char *path) { _cleanup_free_ char *parent = NULL; const char *rpath; @@ -1710,6 +1723,8 @@ int unit_file_add_dependency( return -ESHUTDOWN; if (path_is_generator(&paths, target_info->path)) return -EADDRNOTAVAIL; + if (path_is_transient(&paths, target_info->path)) + return -EADDRNOTAVAIL; assert(target_info->type == UNIT_FILE_TYPE_REGULAR); @@ -1723,6 +1738,8 @@ int unit_file_add_dependency( return -ESHUTDOWN; if (path_is_generator(&paths, i->path)) return -EADDRNOTAVAIL; + if (path_is_transient(&paths, i->path)) + return -EADDRNOTAVAIL; assert(i->type == UNIT_FILE_TYPE_REGULAR); @@ -1777,6 +1794,8 @@ int unit_file_enable( return -ESHUTDOWN; if (path_is_generator(&paths, i->path)) return -EADDRNOTAVAIL; + if (path_is_transient(&paths, i->path)) + return -EADDRNOTAVAIL; assert(i->type == UNIT_FILE_TYPE_REGULAR); } @@ -1891,6 +1910,8 @@ int unit_file_set_default( return -ESHUTDOWN; if (path_is_generator(&paths, i->path)) return -EADDRNOTAVAIL; + if (path_is_transient(&paths, i->path)) + return -EADDRNOTAVAIL; old_path = skip_root(&paths, i->path); new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET); @@ -1975,6 +1996,14 @@ int unit_file_lookup_state( break; } + r = path_is_transient(paths, i->path); + if (r < 0) + return r; + if (r > 0) { + state = UNIT_FILE_TRANSIENT; + break; + } + r = find_symlinks_in_scope(scope, paths, i->name, &state); if (r < 0) return r; @@ -2176,6 +2205,8 @@ static int preset_prepare_one( return -ESHUTDOWN; if (path_is_generator(paths, i->path)) return -EADDRNOTAVAIL; + if (path_is_transient(paths, i->path)) + return -EADDRNOTAVAIL; } else r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); @@ -2372,6 +2403,7 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { [UNIT_FILE_DISABLED] = "disabled", [UNIT_FILE_INDIRECT] = "indirect", [UNIT_FILE_GENERATED] = "generated", + [UNIT_FILE_TRANSIENT] = "transient", [UNIT_FILE_BAD] = "bad", }; diff --git a/src/shared/install.h b/src/shared/install.h index 9c33110e44..578664dd48 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -55,6 +55,7 @@ enum UnitFileState { UNIT_FILE_DISABLED, UNIT_FILE_INDIRECT, UNIT_FILE_GENERATED, + UNIT_FILE_TRANSIENT, UNIT_FILE_BAD, _UNIT_FILE_STATE_MAX, _UNIT_FILE_STATE_INVALID = -1 diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 3fd44a01d4..b64a97375e 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -5444,7 +5444,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { if (r == -ESHUTDOWN) return log_error_errno(r, "Unit file is masked."); if (r == -EADDRNOTAVAIL) - return log_error_errno(r, "Unit file is generated."); + return log_error_errno(r, "Unit file is transient or generated."); if (r < 0) return log_error_errno(r, "Operation failed: %m"); @@ -5612,7 +5612,7 @@ static int add_dependency(int argc, char *argv[], void *userdata) { if (r == -ESHUTDOWN) return log_error_errno(r, "Unit file is masked."); if (r == -EADDRNOTAVAIL) - return log_error_errno(r, "Unit file is generated."); + return log_error_errno(r, "Unit file is transient or generated."); if (r < 0) return log_error_errno(r, "Can't add dependency: %m"); -- cgit v1.2.3-54-g00ecf From 344ca7556bf6c1c8727c19a0eb8751a27dc0a85f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Apr 2016 18:00:36 +0200 Subject: core,systemctl: add new "systemctl revert" command This allows dropping all user configuration and reverting back to the vendor default of a unit file. It basically undoes what "systemctl edit", "systemctl set-property" and "systemctl mask" do. --- man/systemctl.xml | 22 ++++ src/core/dbus-manager.c | 28 ++++ src/core/org.freedesktop.systemd1.conf | 4 + src/shared/install.c | 234 +++++++++++++++++++++++++++++++-- src/shared/install.h | 3 +- src/systemctl/systemctl.c | 20 ++- src/test/test-install-root.c | 52 ++++++++ 7 files changed, 347 insertions(+), 16 deletions(-) (limited to 'man/systemctl.xml') diff --git a/man/systemctl.xml b/man/systemctl.xml index 064777810f..5f624243f7 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1244,6 +1244,28 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service + + revert NAME... + + + Revert one or more unit files to their vendor versions. This command removes drop-in configuration + files that modify the specified units, as well as any user-configured unit file that overrides a matching + vendor supplied unit file. Specifically, for a unit foo.service the matching directories + foo.service.d/ with all their contained files are removed, both below the persistent and + runtime configuration directories (i.e. below /etc/systemd/system and + /run/systemd/system); if the unit file has a vendor-supplied version (i.e. a unit file + located below /usr) any matching peristent or runtime unit file that overrides it is + removed, too. Note that if a unit file has no vendor-supplied version (i.e. is only defined below + /etc/systemd/system or /run/systemd/system, but not in a unit + file stored below /usr), then it is not removed. Also, if a unit is masked, it is + unmasked. + + Effectively, this command may be used to undo all changes made with systemctl + edit, systemctl set-property and systemctl mask and puts + the original unit file with its settings back in effect. + + + add-wants TARGET NAME... diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index f0ee2fcb36..41f27ec26b 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1758,6 +1758,33 @@ static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_ return method_disable_unit_files_generic(message, userdata, "enable", unit_file_unmask, error); } +static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = bus_verify_manage_unit_files_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = unit_file_revert(m->unit_file_scope, NULL, l, &changes, &n_changes); + if (r < 0) + return r; + + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); +} + static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { UnitFileChange *changes = NULL; unsigned n_changes = 0; @@ -2005,6 +2032,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("RevertUnitFiles", "as", "a(sss)", method_revert_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index 6a7a37ee92..f78eedbd6e 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -174,6 +174,10 @@ send_interface="org.freedesktop.systemd1.Manager" send_member="LinkUnitFiles"/> + + diff --git a/src/shared/install.c b/src/shared/install.c index 8fc9205107..8d9f96553b 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -45,6 +45,7 @@ #include "mkdir.h" #include "path-lookup.h" #include "path-util.h" +#include "rm-rf.h" #include "set.h" #include "special.h" #include "stat-util.h" @@ -136,27 +137,35 @@ static int path_is_transient(const LookupPaths *p, const char *path) { return path_equal(p->transient, parent); } -static int path_is_config(const LookupPaths *p, const char *path) { +static int path_is_control(const LookupPaths *p, const char *path) { _cleanup_free_ char *parent = NULL; - const char *rpath; assert(p); assert(path); - /* Checks whether the specified path is intended for configuration or is outside of it. We check both the - * top-level directory and the one actually configured. The latter is particularly relevant for cases where we - * operate on a root directory. */ + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - rpath = skip_root(p, path); - if (rpath && (path_startswith(rpath, "/etc") || path_startswith(rpath, "/run"))) - return true; + return path_equal(parent, p->persistent_control) || + path_equal(parent, p->runtime_control); +} + +static int path_is_config(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + + assert(p); + assert(path); + + /* Note that we do *not* have generic checks for /etc or /run in place, since with them we couldn't discern + * configuration from transient or generated units */ parent = dirname_malloc(path); if (!parent) return -ENOMEM; return path_equal(parent, p->persistent_config) || - path_equal(parent, p->runtime_config); + path_equal(parent, p->runtime_config); } static int path_is_runtime(const LookupPaths *p, const char *path) { @@ -166,6 +175,9 @@ static int path_is_runtime(const LookupPaths *p, const char *path) { assert(p); assert(path); + /* Everything in /run is considered runtime. On top of that we also add explicit checks for the various runtime + * directories, as safety net. */ + rpath = skip_root(p, path); if (rpath && path_startswith(rpath, "/run")) return true; @@ -174,7 +186,33 @@ static int path_is_runtime(const LookupPaths *p, const char *path) { if (!parent) return -ENOMEM; - return path_equal(parent, p->runtime_config); + return path_equal(parent, p->runtime_config) || + path_equal(parent, p->generator) || + path_equal(parent, p->generator_early) || + path_equal(parent, p->generator_late) || + path_equal(parent, p->transient) || + path_equal(parent, p->runtime_control); +} + +static int path_is_vendor(const LookupPaths *p, const char *path) { + const char *rpath; + + assert(p); + assert(path); + + rpath = skip_root(p, path); + if (!rpath) + return 0; + + if (path_startswith(rpath, "/usr")) + return true; + +#ifdef HAVE_SPLIT_USR + if (path_startswith(rpath, "/lib")) + return true; +#endif + + return path_equal(rpath, SYSTEM_DATA_UNIT_PATH); } int unit_file_changes_add( @@ -1703,6 +1741,182 @@ int unit_file_link( return r; } +static int path_shall_revert(const LookupPaths *paths, const char *path) { + int r; + + assert(paths); + assert(path); + + /* Checks whether the path is one where the drop-in directories shall be removed. */ + + r = path_is_config(paths, path); + if (r != 0) + return r; + + r = path_is_control(paths, path); + if (r != 0) + return r; + + return path_is_transient(paths, path); +} + +int unit_file_revert( + UnitFileScope scope, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + /* _cleanup_(install_context_done) InstallContext c = {}; */ + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_strv_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + char **i; + int r, q; + + /* Puts a unit file back into vendor state. This means: + * + * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and + * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated"). + * + * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in + * "config", but not in "transient" or "control" or even "generated"). + * + * We remove all that in both the runtime and the persistant directories, if that applies. + */ + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + bool has_vendor = false; + char **p; + + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + STRV_FOREACH(p, paths.search_path) { + _cleanup_free_ char *path = NULL, *dropin = NULL; + struct stat st; + + path = path_make_absolute(*i, *p); + if (!path) + return -ENOMEM; + + r = lstat(path, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISREG(st.st_mode)) { + /* Check if there's a vendor version */ + r = path_is_vendor(&paths, path); + if (r < 0) + return r; + if (r > 0) + has_vendor = true; + } + + dropin = strappend(path, ".d"); + if (!dropin) + return -ENOMEM; + + r = lstat(dropin, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISDIR(st.st_mode)) { + /* Remove the drop-ins */ + r = path_shall_revert(&paths, dropin); + if (r < 0) + return r; + if (r > 0) { + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = dropin; + dropin = NULL; + } + } + } + + if (!has_vendor) + continue; + + /* OK, there's a vendor version, hence drop all configuration versions */ + STRV_FOREACH(p, paths.search_path) { + _cleanup_free_ char *path = NULL; + struct stat st; + + path = path_make_absolute(*i, *p); + if (!path) + return -ENOMEM; + + r = lstat(path, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { + r = path_is_config(&paths, path); + if (r < 0) + return r; + if (r > 0) { + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = path; + path = NULL; + } + } + } + } + + strv_uniq(todo); + + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_strv_free_ char **fs = NULL; + const char *rp; + char **j; + + (void) get_files_in_directory(*i, &fs); + + q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL); + if (q < 0 && q != -ENOENT && r >= 0) { + r = q; + continue; + } + + STRV_FOREACH(j, fs) { + _cleanup_free_ char *t = NULL; + + t = strjoin(*i, "/", *j, NULL); + if (!t) + return -ENOMEM; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL); + } + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL); + + rp = skip_root(&paths, *i); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i); + if (q < 0) + return q; + } + + q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, changes, n_changes); + if (r >= 0) + r = q; + + q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, changes, n_changes); + if (r >= 0) + r = q; + + return r; +} + int unit_file_add_dependency( UnitFileScope scope, bool runtime, diff --git a/src/shared/install.h b/src/shared/install.h index 8d54fd14ad..6f8b45d4f6 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -128,11 +128,12 @@ static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) { int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_preset_all(UnitFileScope scope, bool runtime, const char *root_dir, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); +int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_revert(UnitFileScope scope, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name); int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, const char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 3344d25f77..b94af9cf08 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1993,7 +1993,7 @@ static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_cha if (changes[i].type == UNIT_FILE_SYMLINK) log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source); else - log_info("Removed symlink %s.", changes[i].path); + log_info("Removed %s.", changes[i].path); } } @@ -5437,6 +5437,8 @@ static int enable_unit(int argc, char *argv[], void *userdata) { r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); else if (streq(verb, "unmask")) r = unit_file_unmask(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes); + else if (streq(verb, "revert")) + r = unit_file_revert(arg_scope, arg_root, names, &changes, &n_changes); else assert_not_reached("Unknown verb"); @@ -5455,7 +5457,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int expect_carries_install_info = false; - bool send_force = true, send_preset_mode = false; + bool send_runtime = true, send_force = true, send_preset_mode = false; const char *method; sd_bus *bus; @@ -5490,6 +5492,9 @@ static int enable_unit(int argc, char *argv[], void *userdata) { else if (streq(verb, "unmask")) { method = "UnmaskUnitFiles"; send_force = false; + } else if (streq(verb, "revert")) { + method = "RevertUnitFiles"; + send_runtime = send_force = false; } else assert_not_reached("Unknown verb"); @@ -5513,9 +5518,11 @@ static int enable_unit(int argc, char *argv[], void *userdata) { return bus_log_create_error(r); } - r = sd_bus_message_append(m, "b", arg_runtime); - if (r < 0) - return bus_log_create_error(r); + if (send_runtime) { + r = sd_bus_message_append(m, "b", arg_runtime); + if (r < 0) + return bus_log_create_error(r); + } if (send_force) { r = sd_bus_message_append(m, "b", arg_force); @@ -6280,6 +6287,8 @@ static void systemctl_help(void) { " unmask NAME... Unmask one or more units\n" " link PATH... Link one or more units files into\n" " the search path\n" + " revert NAME... Revert one or more unit files to vendor\n" + " version\n" " add-wants TARGET NAME... Add 'Wants' dependency for the target\n" " on specified one or more units\n" " add-requires TARGET NAME... Add 'Requires' dependency for the target\n" @@ -7403,6 +7412,7 @@ static int systemctl_main(int argc, char *argv[]) { { "mask", 2, VERB_ANY, 0, enable_unit }, { "unmask", 2, VERB_ANY, 0, enable_unit }, { "link", 2, VERB_ANY, 0, enable_unit }, + { "revert", 2, VERB_ANY, 0, enable_unit }, { "switch-root", 2, VERB_ANY, VERB_NOCHROOT, switch_root }, { "list-dependencies", VERB_ANY, 2, VERB_NOCHROOT, list_dependencies }, { "set-default", 2, 2, 0, set_default }, diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index bc4206a1b0..2138655e29 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -630,6 +630,57 @@ static void test_preset_and_list(const char *root) { assert_se(got_yes && got_no); } +static void test_revert(const char *root) { + const char *p; + UnitFileState state; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + + assert(root); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "yy.service", NULL) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/xx.service"); + assert_se(write_string_file(p, "# Empty\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", &state) >= 0 && state == UNIT_FILE_STATIC); + + /* Initially there's nothing to revert */ + assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service"); + assert_se(write_string_file(p, "# Empty override\n", WRITE_STRING_FILE_CREATE) >= 0); + + /* Revert the override file */ + assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d/dropin.conf"); + assert_se(mkdir_parents(p, 0755) >= 0); + assert_se(write_string_file(p, "# Empty dropin\n", WRITE_STRING_FILE_CREATE) >= 0); + + /* Revert the dropin file */ + assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[0].path, p)); + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d"); + assert_se(changes[1].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[1].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; +} + int main(int argc, char *argv[]) { char root[] = "/tmp/rootXXXXXX"; const char *p; @@ -658,6 +709,7 @@ int main(int argc, char *argv[]) { test_template_enable(root); test_indirect(root); test_preset_and_list(root); + test_revert(root); assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); -- cgit v1.2.3-54-g00ecf From 39207373dd638e548019ddb49929f15795b8b404 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Thu, 21 Apr 2016 20:04:21 -0400 Subject: systemctl,pid1: do not warn about missing install info with "preset" When "preset" was executed for a unit without install info, we'd warn similarly as for "enable" and "disable". But "preset" is usually called for all units, because the preset files are provided by the distribution, and the units are under control of individual programs, and it's reasonable to call "preset" for all units rather then try to do it only for the ones that can be installed. We also don't warn about missing info for "preset-all". Thus it seems reasonable to silently ignore units w/o install info when presetting. (In addition, when more than one unit was specified, we'd issue the warning only if none of them had install info. But this is probably something to fix for enable/disable too.) --- man/systemctl.xml | 26 +++++++++++++------------- src/systemctl/systemctl.c | 7 ++++--- 2 files changed, 17 insertions(+), 16 deletions(-) (limited to 'man/systemctl.xml') diff --git a/man/systemctl.xml b/man/systemctl.xml index 5f624243f7..991e9bafaf 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1084,22 +1084,22 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service preset NAME... - Reset one or more unit files, as specified on the - command line, to the defaults configured in the preset - policy files. This has the same effect as - disable or enable, - depending how the unit is listed in the preset files. + Reset the enable/disable status one or more unit files, as specified on + the command line, to the defaults configured in the preset policy files. This + has the same effect as disable or + enable, depending how the unit is listed in the preset + files. - Use to control - whether units shall be enabled and disabled, or only - enabled, or only disabled. + Use to control whether units shall be + enabled and disabled, or only enabled, or only disabled. + + If the unit carries no install information, it will be silently ignored + by this command. - For more information on the preset policy format, - see + For more information on the preset policy format, see systemd.preset5. - For more information on the concept of presets, please - consult the Preset + For more information on the concept of presets, please consult the + Preset document. diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 059e985463..04205411dd 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -5390,6 +5390,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { UnitFileChange *changes = NULL; unsigned n_changes = 0; int carries_install_info = -1; + bool ignore_carries_install_info = false; int r; if (!argv[1]) @@ -5420,7 +5421,6 @@ static int enable_unit(int argc, char *argv[], void *userdata) { r = unit_file_link(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); else if (streq(verb, "preset")) { r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_preset_mode, arg_force, &changes, &n_changes); - carries_install_info = r; } else if (streq(verb, "mask")) r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); else if (streq(verb, "unmask")) @@ -5437,7 +5437,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { } else { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int expect_carries_install_info = false; + bool expect_carries_install_info = false; bool send_runtime = true, send_force = true, send_preset_mode = false; const char *method; sd_bus *bus; @@ -5468,6 +5468,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { method = "PresetUnitFiles"; expect_carries_install_info = true; + ignore_carries_install_info = true; } else if (streq(verb, "mask")) method = "MaskUnitFiles"; else if (streq(verb, "unmask")) { @@ -5532,7 +5533,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { r = 0; } - if (carries_install_info == 0) + if (carries_install_info == 0 && !ignore_carries_install_info) log_warning("The unit files have no installation config (WantedBy, RequiredBy, Also, Alias\n" "settings in the [Install] section, and DefaultInstance for template units).\n" "This means they are not meant to be enabled using systemctl.\n" -- cgit v1.2.3-54-g00ecf