install: beef up preset logic to limit to only enable or only disable, and do all-unit preset operations

The new "systemctl preset-all" command may now be used to put all
installed units back into the enable/disable state the vendor/admin
encoded in preset files.

Also, introduce "systemctl --preset-mode=enable-only" and "systemctl
--preset-mode=disable-only" to only apply the enable or only the disable
operations of a "systemctl preset" or "systemctl preset-all" operation.

"systemctl preset-all" implements this RFE:

https://bugzilla.redhat.com/show_bug.cgi?id=630174
This commit is contained in:
Lennart Poettering 2014-06-16 19:49:31 +02:00
parent bcafe923a7
commit d309c1c364
6 changed files with 385 additions and 30 deletions

View file

@ -476,6 +476,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
</listitem>
</varlistentry>
<varlistentry>
<term><option>--preset-mode=</option></term>
<listitem>
<para>Takes one of <literal>full</literal> (the default),
<literal>enable-only</literal>,
<literal>disable-only</literal>. When use with the
<command>preset</command> or <command>preset-all</command>
commands controls whether units shall be disabled and
enabled according to the preset rules, or only enabled, or
only disabled.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-n</option></term>
<term><option>--lines=</option></term>
@ -1025,16 +1039,35 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
command line, to the defaults configured in the preset
policy files. This has the same effect as
<command>disable</command> or <command>enable</command>,
depending how the unit is listed in the preset files. For
more information on the preset policy format, see
depending how the unit is listed in the preset files.</para>
<para>Use <option>--preset-mode=</option> to control
whether units shall be enabled and disabled, or only
enabled, or only disabled.</para>
<para>For more information on the preset policy format,
see
<citerefentry><refentrytitle>systemd.preset</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
For more information on the concept of presets, please
consult the
<ulink url="http://freedesktop.org/wiki/Software/systemd/Preset">Preset</ulink>
consult the <ulink
url="http://freedesktop.org/wiki/Software/systemd/Preset">Preset</ulink>
document.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>preset-all</command></term>
<listitem>
<para>Resets all installed unit files to the defaults
configured in the preset policy file (see above).</para>
<para>Use <option>--preset-mode=</option> to control
whether units shall be enabled and disabled, or only
enabled, or only disabled.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>mask <replaceable>NAME</replaceable>...</command></term>

View file

@ -1487,8 +1487,8 @@ fail:
static int method_enable_unit_files_generic(
sd_bus *bus,
sd_bus_message *message,
Manager *m, const
char *verb,
Manager *m,
const char *verb,
int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes),
bool carries_install_info,
sd_bus_error *error) {
@ -1510,6 +1510,10 @@ static int method_enable_unit_files_generic(
if (r < 0)
return r;
r = sd_bus_message_read(message, "bb", &runtime, &force);
if (r < 0)
return r;
#ifdef HAVE_SELINUX
STRV_FOREACH(i, l) {
Unit *u;
@ -1523,10 +1527,6 @@ static int method_enable_unit_files_generic(
}
#endif
r = sd_bus_message_read(message, "bb", &runtime, &force);
if (r < 0)
return r;
scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = call(scope, runtime, NULL, l, force, &changes, &n_changes);
@ -1548,14 +1548,74 @@ static int method_link_unit_files(sd_bus *bus, sd_bus_message *message, void *us
return method_enable_unit_files_generic(bus, message, userdata, "enable", unit_file_link, false, error);
}
static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes) {
return unit_file_preset(scope, runtime, root_dir, files, UNIT_FILE_PRESET_FULL, force, changes, n_changes);
}
static int method_preset_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
return method_enable_unit_files_generic(bus, message, userdata, "enable", unit_file_preset, true, error);
return method_enable_unit_files_generic(bus, message, userdata, "enable", unit_file_preset_without_mode, true, error);
}
static int method_mask_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
return method_enable_unit_files_generic(bus, message, userdata, "disable", unit_file_mask, false, error);
}
static int method_preset_unit_files_with_mode(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
#ifdef HAVE_SELINUX
char **i;
#endif
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
Manager *m = userdata;
UnitFilePresetMode mm;
UnitFileScope scope;
int runtime, force, r;
const char *mode;
assert(bus);
assert(message);
assert(m);
r = sd_bus_message_read_strv(message, &l);
if (r < 0)
return r;
r = sd_bus_message_read(message, "sbb", &mode, &runtime, &force);
if (r < 0)
return r;
if (isempty(mode))
mm = UNIT_FILE_PRESET_FULL;
else {
mm = unit_file_preset_mode_from_string(mode);
if (mm < 0)
return -EINVAL;
}
#ifdef HAVE_SELINUX
STRV_FOREACH(i, l) {
Unit *u;
u = manager_get_unit(m, *i);
if (u) {
r = selinux_unit_access_check(u, message, "enable", error);
if (r < 0)
return r;
}
}
#endif
scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = unit_file_preset(scope, runtime, NULL, l, mm, force, &changes, &n_changes);
if (r < 0)
return r;
return reply_unit_file_changes_and_free(m, bus, message, r, changes, n_changes);
}
static int method_disable_unit_files_generic(
sd_bus *bus,
sd_bus_message *message,
@ -1632,6 +1692,44 @@ static int method_set_default_target(sd_bus *bus, sd_bus_message *message, void
return reply_unit_file_changes_and_free(m, bus, message, -1, changes, n_changes);
}
static int method_preset_all_unit_files(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
Manager *m = userdata;
UnitFilePresetMode mm;
UnitFileScope scope;
const char *mode;
int force, runtime, r;
assert(bus);
assert(message);
assert(m);
r = selinux_access_check(message, "enable", error);
if (r < 0)
return r;
r = sd_bus_message_read(message, "sbb", &mode, &runtime, &force);
if (r < 0)
return r;
if (isempty(mode))
mm = UNIT_FILE_PRESET_FULL;
else {
mm = unit_file_preset_mode_from_string(mode);
if (mm < 0)
return -EINVAL;
}
scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = unit_file_preset_all(scope, runtime, NULL, mm, force, &changes, &n_changes);
if (r < 0)
return r;
return reply_unit_file_changes_and_free(m, bus, message, -1, changes, n_changes);
}
const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_VTABLE_START(0),
@ -1716,10 +1814,12 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("ReenableUnitFiles", "asbb", "ba(sss)", method_reenable_unit_files, 0),
SD_BUS_METHOD("LinkUnitFiles", "asbb", "a(sss)", method_link_unit_files, 0),
SD_BUS_METHOD("PresetUnitFiles", "asbb", "ba(sss)", method_preset_unit_files, 0),
SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, 0),
SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, 0),
SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, 0),
SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, 0),
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, 0),
SD_BUS_SIGNAL("UnitNew", "so", 0),
SD_BUS_SIGNAL("UnitRemoved", "so", 0),

View file

@ -1831,12 +1831,12 @@ int unit_file_preset(
bool runtime,
const char *root_dir,
char **files,
UnitFilePresetMode mode,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_install_context_done_ InstallContext plus = {}, minus = {};
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_free_ char *config_path = NULL;
char **i;
@ -1844,6 +1844,7 @@ int unit_file_preset(
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(mode < _UNIT_FILE_PRESET_MODE_MAX);
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
@ -1862,25 +1863,141 @@ int unit_file_preset(
if (r < 0)
return r;
if (r)
if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
r = install_info_add_auto(&plus, *i);
else
else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
r = install_info_add_auto(&minus, *i);
else
r = 0;
if (r < 0)
return r;
}
r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
r = 0;
q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
if (r == 0)
r = q;
if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
/* Returns number of symlinks that where supposed to be installed. */
q = install_context_apply(&plus, &paths, config_path, root_dir, force,
changes, n_changes);
if (r == 0)
r = q;
r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
if (r == 0)
r = q;
}
if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
/* Returns number of symlinks that where supposed to be installed. */
q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
if (r == 0)
r = q;
}
return r;
}
int unit_file_preset_all(
UnitFileScope scope,
bool runtime,
const char *root_dir,
UnitFilePresetMode mode,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_install_context_done_ InstallContext plus = {}, minus = {};
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_free_ char *config_path = NULL;
char **i;
int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(mode < _UNIT_FILE_PRESET_MODE_MAX);
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
return r;
STRV_FOREACH(i, paths.unit_path) {
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *buf = NULL;
const char *units_dir;
if (!isempty(root_dir)) {
buf = strjoin(root_dir, "/", *i, NULL);
if (!buf)
return -ENOMEM;
units_dir = buf;
} else
units_dir = *i;
d = opendir(units_dir);
if (!d) {
if (errno == ENOENT)
continue;
return -errno;
}
for (;;) {
struct dirent *de;
errno = 0;
de = readdir(d);
if (!de && errno != 0)
return -errno;
if (!de)
break;
if (ignore_file(de->d_name))
continue;
if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
continue;
dirent_ensure_type(d, de);
if (de->d_type != DT_REG)
continue;
r = unit_file_query_preset(scope, de->d_name);
if (r < 0)
return r;
if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
r = install_info_add_auto(&plus, de->d_name);
else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
r = install_info_add_auto(&minus, de->d_name);
else
r = 0;
if (r < 0)
return r;
}
}
r = 0;
if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, NULL);
if (r == 0)
r = q;
}
if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
if (r == 0)
r = q;
}
return r;
}
@ -1937,8 +2054,8 @@ int unit_file_get_list(
}
for (;;) {
struct dirent *de;
_cleanup_unitfilelist_free_ UnitFileList *f = NULL;
struct dirent *de;
errno = 0;
de = readdir(d);
@ -2031,3 +2148,11 @@ static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX]
};
DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MODE_MAX] = {
[UNIT_FILE_PRESET_FULL] = "full",
[UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
[UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
};
DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);

View file

@ -45,6 +45,14 @@ typedef enum UnitFileState {
_UNIT_FILE_STATE_INVALID = -1
} UnitFileState;
typedef enum UnitFilePresetMode {
UNIT_FILE_PRESET_FULL,
UNIT_FILE_PRESET_ENABLE_ONLY,
UNIT_FILE_PRESET_DISABLE_ONLY,
_UNIT_FILE_PRESET_MODE_MAX,
_UNIT_FILE_PRESET_INVALID = -1
} UnitFilePresetMode;
typedef enum UnitFileChangeType {
UNIT_FILE_SYMLINK,
UNIT_FILE_UNLINK,
@ -77,7 +85,8 @@ int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, ch
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, 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_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes);
@ -97,3 +106,6 @@ UnitFileState unit_file_state_from_string(const char *s) _pure_;
const char *unit_file_change_type_to_string(UnitFileChangeType s) _const_;
UnitFileChangeType unit_file_change_type_from_string(const char *s) _pure_;
const char *unit_file_preset_mode_to_string(UnitFilePresetMode m) _const_;
UnitFilePresetMode unit_file_preset_mode_from_string(const char *s) _pure_;

View file

@ -101,6 +101,7 @@ static bool arg_recursive = false;
static int arg_force = 0;
static bool arg_ask_password = true;
static bool arg_runtime = false;
static UnitFilePresetMode arg_preset_mode = UNIT_FILE_PRESET_FULL;
static char **arg_wall = NULL;
static const char *arg_kill_who = NULL;
static int arg_signal = SIGTERM;
@ -5209,7 +5210,7 @@ static int enable_unit(sd_bus *bus, char **args) {
} else if (streq(verb, "link"))
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_force, &changes, &n_changes);
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);
@ -5231,7 +5232,7 @@ static int enable_unit(sd_bus *bus, char **args) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int expect_carries_install_info = false;
bool send_force = true;
bool send_force = true, send_preset_mode = false;
const char *method;
if (streq(verb, "enable")) {
@ -5246,7 +5247,13 @@ static int enable_unit(sd_bus *bus, char **args) {
} else if (streq(verb, "link"))
method = "LinkUnitFiles";
else if (streq(verb, "preset")) {
method = "PresetUnitFiles";
if (arg_preset_mode != UNIT_FILE_PRESET_FULL) {
method = "PresetUnitFilesWithMode";
send_preset_mode = true;
} else
method = "PresetUnitFiles";
expect_carries_install_info = true;
} else if (streq(verb, "mask"))
method = "MaskUnitFiles";
@ -5270,6 +5277,12 @@ static int enable_unit(sd_bus *bus, char **args) {
if (r < 0)
return bus_log_create_error(r);
if (send_preset_mode) {
r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode));
if (r < 0)
return bus_log_create_error(r);
}
r = sd_bus_message_append(m, "b", arg_runtime);
if (r < 0)
return bus_log_create_error(r);
@ -5320,6 +5333,61 @@ finish:
return r;
}
static int preset_all(sd_bus *bus, char **args) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
int r;
if (!bus || avoid_bus()) {
r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes);
if (r < 0) {
log_error("Operation failed: %s", strerror(-r));
goto finish;
}
if (!arg_quiet)
dump_unit_file_changes(changes, n_changes);
r = 0;
} else {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"PresetAllUnitFiles",
&error,
&reply,
"sbb",
unit_file_preset_mode_to_string(arg_preset_mode),
arg_runtime,
arg_force);
if (r < 0) {
log_error("Failed to execute operation: %s", bus_error_message(&error, r));
return r;
}
r = deserialize_and_dump_unit_file_changes(reply);
if (r < 0)
return r;
if (!arg_no_reload)
r = daemon_reload(bus, args);
else
r = 0;
}
finish:
unit_file_changes_free(changes, n_changes);
return r;
}
static int unit_is_enabled(sd_bus *bus, char **args) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
@ -5437,6 +5505,8 @@ static int systemctl_help(void) {
" --runtime Enable unit files only temporarily until next reboot\n"
" -f --force When enabling unit files, override existing symlinks\n"
" When shutting down, execute action immediately\n"
" --preset-mode= Specifies whether fully apply presets, or only enable,\n"
" or only disable\n"
" --root=PATH Enable unit files in the specified root directory\n"
" -n --lines=INTEGER Number of journal entries to show\n"
" -o --output=STRING Change journal output mode (short, short-monotonic,\n"
@ -5477,6 +5547,8 @@ static int systemctl_help(void) {
" reenable NAME... Reenable one or more unit files\n"
" preset NAME... Enable/disable one or more unit files\n"
" based on preset configuration\n"
" preset-all Enable/disable all unit files based on\n"
" preset configuration\n"
" is-enabled NAME... Check whether unit files are enabled\n\n"
" mask NAME... Mask one or more units\n"
" unmask NAME... Unmask one or more units\n"
@ -5625,7 +5697,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_FORCE,
ARG_PLAIN,
ARG_STATE,
ARG_JOB_MODE
ARG_JOB_MODE,
ARG_PRESET_MODE,
};
static const struct option options[] = {
@ -5667,6 +5740,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "plain", no_argument, NULL, ARG_PLAIN },
{ "state", required_argument, NULL, ARG_STATE },
{ "recursive", no_argument, NULL, 'r' },
{ "preset-mode", required_argument, NULL, ARG_PRESET_MODE },
{}
};
@ -5932,6 +6006,16 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_recursive = true;
break;
case ARG_PRESET_MODE:
arg_preset_mode = unit_file_preset_mode_from_string(optarg);
if (arg_preset_mode < 0) {
log_error("Failed to parse preset mode: %s.", optarg);
return -EINVAL;
}
break;
case '?':
return -EINVAL;
@ -6483,6 +6567,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
{ "is-enabled", MORE, 2, unit_is_enabled, NOBUS },
{ "reenable", MORE, 2, enable_unit, NOBUS },
{ "preset", MORE, 2, enable_unit, NOBUS },
{ "preset-all", EQUAL, 1, preset_all, NOBUS },
{ "mask", MORE, 2, enable_unit, NOBUS },
{ "unmask", MORE, 2, enable_unit, NOBUS },
{ "link", MORE, 2, enable_unit, NOBUS },

View file

@ -253,7 +253,7 @@ int main(int argc, char* argv[]) {
changes = NULL;
n_changes = 0;
r = unit_file_preset(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
r = unit_file_preset(UNIT_FILE_SYSTEM, false, NULL, (char**) files, UNIT_FILE_PRESET_FULL, false, &changes, &n_changes);
assert_se(r >= 0);
dump_changes(changes, n_changes);