core: add [Enable|Disable]UnitFilesWithFlags DBUS methods

The new methods work as the unflavoured ones, but takes flags as a
single uint64_t DBUS parameters instead of different booleans, so
that it can be extended without breaking backward compatibility.
Add new flag to allow adding/removing symlinks in
[/etc|/run]/systemd/system.attached so that portable services
configuration files can be self-contained in those directories, without
affecting the system services directories.
Use the new methods and flags from portablectl --enable.

Useful in case /etc is read-only, with only the portable services
directories being mounted read-write.
This commit is contained in:
Luca Boccassi 2020-09-01 17:08:29 +01:00 committed by Lennart Poettering
parent f3f14c573b
commit 836540070d
5 changed files with 108 additions and 23 deletions

View File

@ -179,6 +179,13 @@ node /org/freedesktop/systemd1 {
DisableUnitFiles(in as files,
in b runtime,
out a(sss) changes);
EnableUnitFilesWithFlags(in as files,
in t flags,
out b carries_install_info,
out a(sss) changes);
DisableUnitFilesWithFlags(in as files,
in t flags,
out a(sss) changes);
ReenableUnitFiles(in as files,
in b runtime,
in b force,
@ -836,6 +843,10 @@ node /org/freedesktop/systemd1 {
<variablelist class="dbus-method" generated="True" extra-ref="DisableUnitFiles()"/>
<variablelist class="dbus-method" generated="True" extra-ref="EnableUnitFilesWithFlags()"/>
<variablelist class="dbus-method" generated="True" extra-ref="DisableUnitFilesWithFlags()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ReenableUnitFiles()"/>
<variablelist class="dbus-method" generated="True" extra-ref="LinkUnitFiles()"/>
@ -1292,6 +1303,20 @@ node /org/freedesktop/systemd1 {
<para>Similarly, <function>DisableUnitFiles()</function> disables one or more units in the system,
i.e. removes all symlinks to them in <filename>/etc</filename> and <filename>/run</filename>.</para>
<para>The <function>EnableUnitFilesWithFlags()</function> and <function>DisableUnitFilesWithFlags()</function>
take in options as flags instead of booleans to allow for extendability, defined as follows:</para>
<programlisting>
#define SD_SYSTEMD_UNIT_RUNTIME (UINT64_C(1) &lt;&lt; 0)
#define SD_SYSTEMD_UNIT_FORCE (UINT64_C(1) &lt;&lt; 1)
#define SD_SYSTEMD_UNIT_PORTABLE (UINT64_C(1) &lt;&lt; 2)
</programlisting>
<para><varname>SD_SYSTEMD_UNIT_RUNTIME</varname> will enable or disable the unit for runtime only,
<varname>SD_SYSTEMD_UNIT_FORCE</varname> controls whether symlinks pointing to other units shall be
replaced if necessary. <varname>SD_SYSTEMD_UNIT_PORTABLE</varname> will add or remove the symlinks in
<filename>/etc/systemd/system.attached</filename> and <filename>/run/systemd/system.attached</filename>.</para>
<para>Similarly, <function>ReenableUnitFiles()</function> applies the changes to one or more units that
would result from disabling and enabling the unit quickly one after the other in an atomic
fashion. This is useful to apply updated [Install] information contained in unit files.</para>
@ -1480,6 +1505,7 @@ node /org/freedesktop/systemd1 {
<function>RestartUnit()</function> and similar, <function>SetProperty()</function>) require
<interfacename>org.freedesktop.systemd1.manage-units</interfacename>. Operations which modify unit file
enablement state (<function>EnableUnitFiles()</function>, <function>DisableUnitFiles()</function>,
<function>EnableUnitFilesWithFlags()</function>, <function>DisableUnitFilesWithFlags()</function>,
<function>ReenableUnitFiles()</function>, <function>LinkUnitFiles()</function>,
<function>PresetUnitFiles</function>, <function>MaskUnitFiles</function>, and similar) require
<interfacename>org.freedesktop.systemd1.manage-unit-files</interfacename>. Operations which modify the

View File

@ -2083,7 +2083,7 @@ static int method_enable_unit_files_generic(
UnitFileChange *changes = NULL;
size_t n_changes = 0;
UnitFileFlags flags;
int runtime, force, r;
int r;
assert(message);
assert(m);
@ -2092,11 +2092,23 @@ 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;
if (sd_bus_message_is_method_call(message, NULL, "EnableUnitFilesWithFlags")) {
uint64_t raw_flags;
flags = unit_file_bools_to_flags(runtime, force);
r = sd_bus_message_read(message, "t", &raw_flags);
if (r < 0)
return r;
if ((raw_flags & ~_UNIT_FILE_FLAGS_MASK_PUBLIC) != 0)
return -EINVAL;
flags = raw_flags;
} else {
int runtime, force;
r = sd_bus_message_read(message, "bb", &runtime, &force);
if (r < 0)
return r;
flags = unit_file_bools_to_flags(runtime, force);
}
r = bus_verify_manage_unit_files_async(m, message, error);
if (r < 0)
@ -2111,6 +2123,10 @@ static int method_enable_unit_files_generic(
return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes, error);
}
static int method_enable_unit_files_with_flags(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return method_enable_unit_files_generic(message, userdata, unit_file_enable, true, error);
}
static int method_enable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return method_enable_unit_files_generic(message, userdata, unit_file_enable, true, error);
}
@ -2188,8 +2204,9 @@ static int method_disable_unit_files_generic(
_cleanup_strv_free_ char **l = NULL;
UnitFileChange *changes = NULL;
UnitFileFlags flags;
size_t n_changes = 0;
int r, runtime;
int r;
assert(message);
assert(m);
@ -2198,9 +2215,24 @@ static int method_disable_unit_files_generic(
if (r < 0)
return r;
r = sd_bus_message_read(message, "b", &runtime);
if (r < 0)
return r;
if (sd_bus_message_is_method_call(message, NULL, "DisableUnitFilesWithFlags")) {
uint64_t raw_flags;
r = sd_bus_message_read(message, "t", &raw_flags);
if (r < 0)
return r;
if ((raw_flags & ~_UNIT_FILE_FLAGS_MASK_PUBLIC) != 0 ||
FLAGS_SET(raw_flags, UNIT_FILE_FORCE))
return -EINVAL;
flags = raw_flags;
} else {
int runtime;
r = sd_bus_message_read(message, "b", &runtime);
if (r < 0)
return r;
flags = unit_file_bools_to_flags(runtime, false);
}
r = bus_verify_manage_unit_files_async(m, message, error);
if (r < 0)
@ -2208,13 +2240,17 @@ static int method_disable_unit_files_generic(
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = call(m->unit_file_scope, runtime ? UNIT_FILE_RUNTIME : 0, NULL, l, &changes, &n_changes);
r = call(m->unit_file_scope, flags, NULL, l, &changes, &n_changes);
if (r < 0)
return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error);
}
static int method_disable_unit_files_with_flags(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return method_disable_unit_files_generic(message, userdata, unit_file_disable, error);
}
static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return method_disable_unit_files_generic(message, userdata, unit_file_disable, error);
}
@ -2984,6 +3020,23 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PARAM(changes),
method_disable_unit_files,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("EnableUnitFilesWithFlags",
"ast",
SD_BUS_PARAM(files)
SD_BUS_PARAM(flags),
"ba(sss)",
SD_BUS_PARAM(carries_install_info)
SD_BUS_PARAM(changes),
method_enable_unit_files_with_flags,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("DisableUnitFilesWithFlags",
"ast",
SD_BUS_PARAM(files)
SD_BUS_PARAM(flags),
"a(sss)",
SD_BUS_PARAM(changes),
method_disable_unit_files_with_flags,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ReenableUnitFiles",
"asbb",
SD_BUS_PARAM(files)

View File

@ -392,6 +392,7 @@ static int maybe_enable_disable(sd_bus *bus, const char *path, bool enable) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_strv_free_ char **names = NULL;
UnitFileChange *changes = NULL;
const uint64_t flags = UNIT_FILE_PORTABLE | (arg_runtime ? UNIT_FILE_RUNTIME : 0);
size_t n_changes = 0;
int r;
@ -408,7 +409,7 @@ static int maybe_enable_disable(sd_bus *bus, const char *path, bool enable) {
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
enable ? "EnableUnitFiles" : "DisableUnitFiles");
enable ? "EnableUnitFilesWithFlags" : "DisableUnitFilesWithFlags");
if (r < 0)
return bus_log_create_error(r);
@ -416,16 +417,10 @@ static int maybe_enable_disable(sd_bus *bus, const char *path, bool enable) {
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "b", arg_runtime);
r = sd_bus_message_append(m, "t", flags);
if (r < 0)
return bus_log_create_error(r);
if (enable) {
r = sd_bus_message_append(m, "b", false);
if (r < 0)
return bus_log_create_error(r);
}
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0)
return log_error_errno(r, "Failed to %s the portable service %s: %s",

View File

@ -249,6 +249,15 @@ static int path_is_vendor_or_generator(const LookupPaths *p, const char *path) {
return path_equal(rpath, SYSTEM_DATA_UNIT_PATH);
}
static const char* config_path_from_flags(const LookupPaths *paths, UnitFileFlags flags) {
assert(paths);
if (FLAGS_SET(flags, UNIT_FILE_PORTABLE))
return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? paths->runtime_attached : paths->persistent_attached;
else
return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? paths->runtime_config : paths->persistent_config;
}
int unit_file_changes_add(
UnitFileChange **changes,
size_t *n_changes,
@ -2582,7 +2591,7 @@ int unit_file_enable(
if (r < 0)
return r;
config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
config_path = config_path_from_flags(&paths, flags);
if (!config_path)
return -ENXIO;
@ -2625,7 +2634,7 @@ int unit_file_disable(
if (r < 0)
return r;
config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
config_path = config_path_from_flags(&paths, flags);
if (!config_path)
return -ENXIO;

View File

@ -35,9 +35,11 @@ enum UnitFileChangeType {
};
enum UnitFileFlags {
UNIT_FILE_RUNTIME = 1 << 0,
UNIT_FILE_FORCE = 1 << 1,
UNIT_FILE_DRY_RUN = 1 << 2,
UNIT_FILE_RUNTIME = 1 << 0, /* Public API via DBUS, do not change */
UNIT_FILE_FORCE = 1 << 1, /* Public API via DBUS, do not change */
UNIT_FILE_PORTABLE = 1 << 2, /* Public API via DBUS, do not change */
UNIT_FILE_DRY_RUN = 1 << 3,
_UNIT_FILE_FLAGS_MASK_PUBLIC = UNIT_FILE_RUNTIME|UNIT_FILE_PORTABLE|UNIT_FILE_FORCE,
};
/* type can either one of the UnitFileChangeTypes listed above, or a negative error.