mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
core: add [State|Runtime|Cache|Logs]Directory symlink as second parameter
When combined with a tmpfs on /run or /var/lib, allows to create arbitrary and ephemeral symlinks for StateDirectory or RuntimeDirectory. This is especially useful when sharing these directories between different services, to make the same state/runtime directory 'backend' appear as different names to each service, so that they can be added/removed to a sharing agreement transparently, without code changes. An example (simplified, but real) use case: foo.service: StateDirectory=foo bar.service: StateDirectory=bar foo.service.d/shared.conf: StateDirectory= StateDirectory=shared:foo bar.service.d/shared.conf: StateDirectory= StateDirectory=shared:bar foo and bar use respectively /var/lib/foo and /var/lib/bar. Then the orchestration layer decides to stop this sharing, the drop-in can be removed. The services won't need any update and will keep working and being able to store state, transparently. To keep backward compatibility, new DBUS messages are added.
This commit is contained in:
parent
df61e79a5d
commit
211a3d87fb
@ -2805,20 +2805,28 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly (bas) RestrictAddressFamilies = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) RuntimeDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s RuntimeDirectoryPreserve = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u RuntimeDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as RuntimeDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) StateDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u StateDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as StateDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) CacheDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u CacheDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as CacheDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) LogsDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u LogsDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as LogsDirectory = ['...', ...];
|
||||
@ -3336,20 +3344,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<!--property RuntimeDirectoryMode is not documented!-->
|
||||
|
||||
<!--property RuntimeDirectory is not documented!-->
|
||||
|
||||
<!--property StateDirectoryMode is not documented!-->
|
||||
|
||||
<!--property StateDirectory is not documented!-->
|
||||
|
||||
<!--property CacheDirectoryMode is not documented!-->
|
||||
|
||||
<!--property CacheDirectory is not documented!-->
|
||||
|
||||
<!--property LogsDirectoryMode is not documented!-->
|
||||
|
||||
<!--property LogsDirectory is not documented!-->
|
||||
|
||||
<!--property ConfigurationDirectoryMode is not documented!-->
|
||||
|
||||
<!--property ConfigurationDirectory is not documented!-->
|
||||
@ -3938,20 +3938,28 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RestrictAddressFamilies"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryPreserve"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
|
||||
@ -4120,6 +4128,13 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
the <literal>MemoryMax</literal> or <literal>MemoryHigh</literal> (whichever is lower) limit set by the cgroup
|
||||
memory controller is reached. It will take into consideration limits on all parent slices, other than the
|
||||
limits set on the unit itself.</para>
|
||||
|
||||
<para><varname>RuntimeDirectorySymlink</varname>, <varname>StateDirectorySymlink</varname>,
|
||||
<varname>CacheDirectorySymlink</varname> and <varname>LogsDirectorySymlink</varname> respectively
|
||||
implement the destination parameter of the unit files settings <varname>RuntimeDirectory</varname>,
|
||||
<varname>StateDirectory</varname>, <varname>CacheDirectory</varname> and <varname>LogsDirectory</varname>,
|
||||
which will create a symlink of the given name to the respective directory. The messages take an unused
|
||||
<varname>flags</varname> parameter, reserved for future backward-compatible changes.</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
@ -4651,20 +4666,28 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly (bas) RestrictAddressFamilies = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) RuntimeDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s RuntimeDirectoryPreserve = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u RuntimeDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as RuntimeDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) StateDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u StateDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as StateDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) CacheDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u CacheDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as CacheDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) LogsDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u LogsDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as LogsDirectory = ['...', ...];
|
||||
@ -5208,20 +5231,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<!--property RuntimeDirectoryMode is not documented!-->
|
||||
|
||||
<!--property RuntimeDirectory is not documented!-->
|
||||
|
||||
<!--property StateDirectoryMode is not documented!-->
|
||||
|
||||
<!--property StateDirectory is not documented!-->
|
||||
|
||||
<!--property CacheDirectoryMode is not documented!-->
|
||||
|
||||
<!--property CacheDirectory is not documented!-->
|
||||
|
||||
<!--property LogsDirectoryMode is not documented!-->
|
||||
|
||||
<!--property LogsDirectory is not documented!-->
|
||||
|
||||
<!--property ConfigurationDirectoryMode is not documented!-->
|
||||
|
||||
<!--property ConfigurationDirectory is not documented!-->
|
||||
@ -5806,20 +5821,28 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RestrictAddressFamilies"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryPreserve"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
|
||||
@ -6416,20 +6439,28 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly (bas) RestrictAddressFamilies = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) RuntimeDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s RuntimeDirectoryPreserve = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u RuntimeDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as RuntimeDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) StateDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u StateDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as StateDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) CacheDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u CacheDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as CacheDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) LogsDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u LogsDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as LogsDirectory = ['...', ...];
|
||||
@ -6901,20 +6932,12 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<!--property RuntimeDirectoryMode is not documented!-->
|
||||
|
||||
<!--property RuntimeDirectory is not documented!-->
|
||||
|
||||
<!--property StateDirectoryMode is not documented!-->
|
||||
|
||||
<!--property StateDirectory is not documented!-->
|
||||
|
||||
<!--property CacheDirectoryMode is not documented!-->
|
||||
|
||||
<!--property CacheDirectory is not documented!-->
|
||||
|
||||
<!--property LogsDirectoryMode is not documented!-->
|
||||
|
||||
<!--property LogsDirectory is not documented!-->
|
||||
|
||||
<!--property ConfigurationDirectoryMode is not documented!-->
|
||||
|
||||
<!--property ConfigurationDirectory is not documented!-->
|
||||
@ -7417,20 +7440,28 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RestrictAddressFamilies"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryPreserve"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
|
||||
@ -8148,20 +8179,28 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly (bas) RestrictAddressFamilies = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) RuntimeDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s RuntimeDirectoryPreserve = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u RuntimeDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as RuntimeDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) StateDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u StateDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as StateDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) CacheDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u CacheDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as CacheDirectory = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) LogsDirectorySymlink = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly u LogsDirectoryMode = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as LogsDirectory = ['...', ...];
|
||||
@ -8619,20 +8658,12 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<!--property RuntimeDirectoryMode is not documented!-->
|
||||
|
||||
<!--property RuntimeDirectory is not documented!-->
|
||||
|
||||
<!--property StateDirectoryMode is not documented!-->
|
||||
|
||||
<!--property StateDirectory is not documented!-->
|
||||
|
||||
<!--property CacheDirectoryMode is not documented!-->
|
||||
|
||||
<!--property CacheDirectory is not documented!-->
|
||||
|
||||
<!--property LogsDirectoryMode is not documented!-->
|
||||
|
||||
<!--property LogsDirectory is not documented!-->
|
||||
|
||||
<!--property ConfigurationDirectoryMode is not documented!-->
|
||||
|
||||
<!--property ConfigurationDirectory is not documented!-->
|
||||
@ -9121,20 +9152,28 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RestrictAddressFamilies"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryPreserve"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectorySymlink"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
|
||||
|
@ -1327,6 +1327,13 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
|
||||
configuration or lifetime guarantees, please consider using
|
||||
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
|
||||
<para><varname>RuntimeDirectory=</varname>, <varname>StateDirectory=</varname>, <varname>CacheDirectory=</varname>
|
||||
and <varname>LogsDirectory=</varname> optionally support a second parameter, separated by <literal>:</literal>.
|
||||
The second parameter will be interpreted as a destination path that will be created as a symlink to the directory.
|
||||
The symlinks will be created after any <varname>BindPaths=</varname> or <varname>TemporaryFileSystem=</varname>
|
||||
options have been set up, to make ephemeral symlinking possible. The same source can have multiple symlinks, by
|
||||
using the same first parameter, but a diferent second parameter.</para></listitem>
|
||||
|
||||
<para>The directories defined by these options are always created under the standard paths used by systemd
|
||||
(<filename>/var/</filename>, <filename>/run/</filename>, <filename>/etc/</filename>, …). If the service needs
|
||||
directories in a different location, a different mechanism has to be used to create them.</para>
|
||||
@ -1355,7 +1362,13 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
|
||||
<programlisting>RuntimeDirectory=foo/bar
|
||||
StateDirectory=aaa/bbb ccc</programlisting>
|
||||
then the environment variable <literal>RUNTIME_DIRECTORY</literal> is set with <literal>/run/foo/bar</literal>, and
|
||||
<literal>STATE_DIRECTORY</literal> is set with <literal>/var/lib/aaa/bbb:/var/lib/ccc</literal>.</para></listitem>
|
||||
<literal>STATE_DIRECTORY</literal> is set with <literal>/var/lib/aaa/bbb:/var/lib/ccc</literal>.</para>
|
||||
|
||||
<para>Example: if a system service unit has the following,
|
||||
<programlisting>RuntimeDirectory=foo:bar foo:baz</programlisting>
|
||||
the service manager creates <filename index='false'>/run/foo</filename> (if it does not exist), and
|
||||
<filename index='false'>/run/bar</filename> plus <filename index='false'>/run/baz</filename> as symlinks to
|
||||
<filename index='false'>/run/foo</filename>.</para>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -1091,6 +1091,70 @@ static int property_get_extension_images(
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int bus_property_get_exec_dir(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
ExecDirectory *d = userdata;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(d);
|
||||
assert(property);
|
||||
assert(reply);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "s");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (size_t i = 0; i < d->n_items; i++) {
|
||||
r = sd_bus_message_append_basic(reply, 's', d->items[i].path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int bus_property_get_exec_dir_symlink(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
ExecDirectory *d = userdata;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(d);
|
||||
assert(property);
|
||||
assert(reply);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "(sst)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (size_t i = 0; i < d->n_items; i++) {
|
||||
char **dst;
|
||||
|
||||
STRV_FOREACH(dst, d->items[i].symlinks) {
|
||||
r = sd_bus_message_append(reply, "(sst)", d->items[i].path, *dst, 0 /* flags, unused for now */);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -1224,17 +1288,21 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_PROPERTY("Personality", "s", property_get_personality, offsetof(ExecContext, personality), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("LockPersonality", "b", bus_property_get_bool, offsetof(ExecContext, lock_personality), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RestrictAddressFamilies", "(bas)", property_get_address_families, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RuntimeDirectorySymlink", "a(sst)", bus_property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RuntimeDirectoryPreserve", "s", property_get_exec_preserve_mode, offsetof(ExecContext, runtime_directory_preserve_mode), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RuntimeDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME].mode), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME].paths), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RuntimeDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StateDirectorySymlink", "a(sst)", bus_property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StateDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE].mode), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StateDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE].paths), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StateDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("CacheDirectorySymlink", "a(sst)", bus_property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("CacheDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE].mode), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("CacheDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE].paths), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("CacheDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("LogsDirectorySymlink", "a(sst)", bus_property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("LogsDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS].mode), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("LogsDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS].paths), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("LogsDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConfigurationDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_CONFIGURATION].mode), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConfigurationDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_CONFIGURATION].paths), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConfigurationDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_CONFIGURATION]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TimeoutCleanUSec", "t", bus_property_get_usec, offsetof(ExecContext, timeout_clean_usec), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RestrictRealtime", "b", bus_property_get_bool, offsetof(ExecContext, restrict_realtime), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -3294,14 +3362,17 @@ int bus_exec_context_set_transient_property(
|
||||
d = c->directories + i;
|
||||
|
||||
if (strv_isempty(l)) {
|
||||
d->paths = strv_free(d->paths);
|
||||
exec_directory_done(d);
|
||||
unit_write_settingf(u, flags, name, "%s=", name);
|
||||
} else {
|
||||
_cleanup_free_ char *joined = NULL;
|
||||
char **source;
|
||||
|
||||
r = strv_extend_strv(&d->paths, l, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
STRV_FOREACH(source, l) {
|
||||
r = exec_directory_add(&d->items, &d->n_items, *source, NULL);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
joined = unit_concat_strv(l, UNIT_ESCAPE_SPECIFIERS);
|
||||
if (!joined)
|
||||
@ -3711,6 +3782,79 @@ int bus_exec_context_set_transient_property(
|
||||
extension_images = mount_image_free_many(extension_images, &n_extension_images);
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (STR_IN_SET(name, "StateDirectorySymlink", "RuntimeDirectorySymlink", "CacheDirectorySymlink", "LogsDirectorySymlink")) {
|
||||
char *source, *destination;
|
||||
ExecDirectory *directory;
|
||||
uint64_t symlink_flags; /* No flags for now, reserved for future uses. */
|
||||
ExecDirectoryType i;
|
||||
|
||||
assert_se((i = exec_directory_type_symlink_from_string(name)) >= 0);
|
||||
directory = c->directories + i;
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "(sst)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
while ((r = sd_bus_message_read(message, "(sst)", &source, &destination, &symlink_flags)) > 0) {
|
||||
if (!path_is_valid(source))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not valid.", source);
|
||||
if (path_is_absolute(source))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is absolute.", source);
|
||||
if (!path_is_normalized(source))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not normalized.", source);
|
||||
if (!path_is_valid(destination))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not valid.", destination);
|
||||
if (path_is_absolute(destination))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is absolute.", destination);
|
||||
if (!path_is_normalized(destination))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not normalized.", destination);
|
||||
if (symlink_flags != 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
_cleanup_free_ char *destination_escaped = NULL, *source_escaped = NULL;
|
||||
ExecDirectoryItem *item = NULL;
|
||||
|
||||
/* Adding new directories is supported from both *DirectorySymlink methods and the
|
||||
* older ones, so try to find an existing configuration first and create it if it's
|
||||
* not there yet. */
|
||||
for (size_t j = 0; j < directory->n_items; ++j)
|
||||
if (path_equal(source, directory->items[j].path)) {
|
||||
item = &directory->items[j];
|
||||
break;
|
||||
}
|
||||
|
||||
if (item)
|
||||
r = strv_extend(&item->symlinks, destination);
|
||||
else
|
||||
r = exec_directory_add(&directory->items, &directory->n_items, source, STRV_MAKE(destination));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Need to store them in the unit with the escapes, so that they can be parsed again */
|
||||
source_escaped = xescape(source, ":");
|
||||
destination_escaped = xescape(destination, ":");
|
||||
if (!source_escaped || !destination_escaped)
|
||||
return -ENOMEM;
|
||||
|
||||
unit_write_settingf(
|
||||
u, flags|UNIT_ESCAPE_SPECIFIERS, exec_directory_type_to_string(i),
|
||||
"%s=%s:%s",
|
||||
exec_directory_type_to_string(i),
|
||||
source_escaped,
|
||||
destination_escaped);
|
||||
}
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_exit_container(message);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1943,26 +1943,29 @@ static int build_environment(
|
||||
}
|
||||
|
||||
for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
|
||||
_cleanup_free_ char *pre = NULL, *joined = NULL;
|
||||
_cleanup_free_ char *joined = NULL;
|
||||
const char *n;
|
||||
|
||||
if (!p->prefix[t])
|
||||
continue;
|
||||
|
||||
if (strv_isempty(c->directories[t].paths))
|
||||
if (c->directories[t].n_items == 0)
|
||||
continue;
|
||||
|
||||
n = exec_directory_env_name_to_string(t);
|
||||
if (!n)
|
||||
continue;
|
||||
|
||||
pre = strjoin(p->prefix[t], "/");
|
||||
if (!pre)
|
||||
return -ENOMEM;
|
||||
for (size_t i = 0; i < c->directories[t].n_items; i++) {
|
||||
_cleanup_free_ char *prefixed = NULL;
|
||||
|
||||
joined = strv_join_full(c->directories[t].paths, ":", pre, true);
|
||||
if (!joined)
|
||||
return -ENOMEM;
|
||||
prefixed = path_join(p->prefix[t], c->directories[t].items[i].path);
|
||||
if (!prefixed)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!strextend_with_separator(&joined, ":", prefixed))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
x = strjoin(n, "=", joined);
|
||||
if (!x)
|
||||
@ -2078,15 +2081,15 @@ bool exec_needs_mount_namespace(
|
||||
if (params && !params->prefix[t])
|
||||
continue;
|
||||
|
||||
if (!strv_isempty(context->directories[t].paths))
|
||||
if (context->directories[t].n_items > 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (context->dynamic_user &&
|
||||
(!strv_isempty(context->directories[EXEC_DIRECTORY_STATE].paths) ||
|
||||
!strv_isempty(context->directories[EXEC_DIRECTORY_CACHE].paths) ||
|
||||
!strv_isempty(context->directories[EXEC_DIRECTORY_LOGS].paths)))
|
||||
(context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
|
||||
context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
|
||||
context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
|
||||
return true;
|
||||
|
||||
if (context->log_namespace)
|
||||
@ -2268,12 +2271,43 @@ static bool exec_directory_is_private(const ExecContext *context, ExecDirectoryT
|
||||
return true;
|
||||
}
|
||||
|
||||
static int create_many_symlinks(const char *root, const char *source, char **symlinks) {
|
||||
_cleanup_free_ char *src_abs = NULL;
|
||||
char **dst;
|
||||
int r;
|
||||
|
||||
assert(source);
|
||||
|
||||
src_abs = path_join(root, source);
|
||||
if (!src_abs)
|
||||
return -ENOMEM;
|
||||
|
||||
STRV_FOREACH(dst, symlinks) {
|
||||
_cleanup_free_ char *dst_abs = NULL;
|
||||
|
||||
dst_abs = path_join(root, *dst);
|
||||
if (!dst_abs)
|
||||
return -ENOMEM;
|
||||
|
||||
r = mkdir_parents_label(dst_abs, 0755);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = symlink_idempotent(src_abs, dst_abs, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_exec_directory(
|
||||
const ExecContext *context,
|
||||
const ExecParameters *params,
|
||||
uid_t uid,
|
||||
gid_t gid,
|
||||
ExecDirectoryType type,
|
||||
bool needs_mount_namespace,
|
||||
int *exit_status) {
|
||||
|
||||
static const int exit_status_table[_EXEC_DIRECTORY_TYPE_MAX] = {
|
||||
@ -2283,7 +2317,6 @@ static int setup_exec_directory(
|
||||
[EXEC_DIRECTORY_LOGS] = EXIT_LOGS_DIRECTORY,
|
||||
[EXEC_DIRECTORY_CONFIGURATION] = EXIT_CONFIGURATION_DIRECTORY,
|
||||
};
|
||||
char **rt;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
@ -2301,10 +2334,10 @@ static int setup_exec_directory(
|
||||
gid = 0;
|
||||
}
|
||||
|
||||
STRV_FOREACH(rt, context->directories[type].paths) {
|
||||
for (size_t i = 0; i < context->directories[type].n_items; i++) {
|
||||
_cleanup_free_ char *p = NULL, *pp = NULL;
|
||||
|
||||
p = path_join(params->prefix[type], *rt);
|
||||
p = path_join(params->prefix[type], context->directories[type].items[i].path);
|
||||
if (!p) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
@ -2351,7 +2384,7 @@ static int setup_exec_directory(
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (!path_extend(&pp, *rt)) {
|
||||
if (!path_extend(&pp, context->directories[type].items[i].path)) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
@ -2409,7 +2442,7 @@ static int setup_exec_directory(
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
q = path_join(params->prefix[type], "private", *rt);
|
||||
q = path_join(params->prefix[type], "private", context->directories[type].items[i].path);
|
||||
if (!q) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
@ -2462,7 +2495,7 @@ static int setup_exec_directory(
|
||||
if (((st.st_mode ^ context->directories[type].mode) & 07777) != 0)
|
||||
log_warning("%s \'%s\' already exists but the mode is different. "
|
||||
"(File system: %o %sMode: %o)",
|
||||
exec_directory_type_to_string(type), *rt,
|
||||
exec_directory_type_to_string(type), context->directories[type].items[i].path,
|
||||
st.st_mode & 07777, exec_directory_type_to_string(type), context->directories[type].mode & 07777);
|
||||
|
||||
continue;
|
||||
@ -2485,6 +2518,17 @@ static int setup_exec_directory(
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* If we are not going to run in a namespace, set up the symlinks - otherwise
|
||||
* they are set up later, to allow configuring empty var/run/etc. */
|
||||
if (!needs_mount_namespace)
|
||||
for (size_t i = 0; i < context->directories[type].n_items; i++) {
|
||||
r = create_many_symlinks(params->prefix[type],
|
||||
context->directories[type].items[i].path,
|
||||
context->directories[type].items[i].symlinks);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
@ -3032,7 +3076,7 @@ static int compile_bind_mounts(
|
||||
if (!params->prefix[t])
|
||||
continue;
|
||||
|
||||
n += strv_length(context->directories[t].paths);
|
||||
n += context->directories[t].n_items;
|
||||
}
|
||||
|
||||
if (n <= 0) {
|
||||
@ -3073,12 +3117,10 @@ static int compile_bind_mounts(
|
||||
}
|
||||
|
||||
for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
|
||||
char **suffix;
|
||||
|
||||
if (!params->prefix[t])
|
||||
continue;
|
||||
|
||||
if (strv_isempty(context->directories[t].paths))
|
||||
if (context->directories[t].n_items == 0)
|
||||
continue;
|
||||
|
||||
if (exec_directory_is_private(context, t) &&
|
||||
@ -3100,13 +3142,13 @@ static int compile_bind_mounts(
|
||||
goto finish;
|
||||
}
|
||||
|
||||
STRV_FOREACH(suffix, context->directories[t].paths) {
|
||||
for (size_t i = 0; i < context->directories[t].n_items; i++) {
|
||||
char *s, *d;
|
||||
|
||||
if (exec_directory_is_private(context, t))
|
||||
s = path_join(params->prefix[t], "private", *suffix);
|
||||
s = path_join(params->prefix[t], "private", context->directories[t].items[i].path);
|
||||
else
|
||||
s = path_join(params->prefix[t], *suffix);
|
||||
s = path_join(params->prefix[t], context->directories[t].items[i].path);
|
||||
if (!s) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
@ -3117,7 +3159,7 @@ static int compile_bind_mounts(
|
||||
/* When RootDirectory= or RootImage= are set, then the symbolic link to the private
|
||||
* directory is not created on the root directory. So, let's bind-mount the directory
|
||||
* on the 'non-private' place. */
|
||||
d = path_join(params->prefix[t], *suffix);
|
||||
d = path_join(params->prefix[t], context->directories[t].items[i].path);
|
||||
else
|
||||
d = strdup(s);
|
||||
if (!d) {
|
||||
@ -3166,19 +3208,31 @@ static int compile_symlinks(
|
||||
assert(ret_symlinks);
|
||||
|
||||
for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
|
||||
char **src;
|
||||
|
||||
if (!exec_directory_is_private(context, dt))
|
||||
continue;
|
||||
|
||||
STRV_FOREACH(src, context->directories[dt].paths) {
|
||||
for (size_t i = 0; i < context->directories[dt].n_items; i++) {
|
||||
_cleanup_free_ char *private_path = NULL, *path = NULL;
|
||||
char **symlink;
|
||||
|
||||
private_path = path_join(params->prefix[dt], "private", *src);
|
||||
STRV_FOREACH(symlink, context->directories[dt].items[i].symlinks) {
|
||||
_cleanup_free_ char *src_abs = NULL, *dst_abs = NULL;
|
||||
|
||||
src_abs = path_join(params->prefix[dt], context->directories[dt].items[i].path);
|
||||
dst_abs = path_join(params->prefix[dt], *symlink);
|
||||
if (!src_abs || !dst_abs)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_consume_pair(&symlinks, TAKE_PTR(src_abs), TAKE_PTR(dst_abs));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!exec_directory_is_private(context, dt))
|
||||
continue;
|
||||
|
||||
private_path = path_join(params->prefix[dt], "private", context->directories[dt].items[i].path);
|
||||
if (!private_path)
|
||||
return -ENOMEM;
|
||||
|
||||
path = path_join(params->prefix[dt], *src);
|
||||
path = path_join(params->prefix[dt], context->directories[dt].items[i].path);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -3262,8 +3316,7 @@ static int apply_mount_namespace(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Symlinks for exec dirs are set up after other mounts, before they are
|
||||
* made read-only. */
|
||||
/* Symlinks for exec dirs are set up after other mounts, before they are made read-only. */
|
||||
r = compile_symlinks(context, params, &symlinks);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -3686,21 +3739,19 @@ static int compile_suggested_paths(const ExecContext *c, const ExecParameters *p
|
||||
* directories. */
|
||||
|
||||
for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
|
||||
char **i;
|
||||
|
||||
if (t == EXEC_DIRECTORY_CONFIGURATION)
|
||||
continue;
|
||||
|
||||
if (!p->prefix[t])
|
||||
continue;
|
||||
|
||||
STRV_FOREACH(i, c->directories[t].paths) {
|
||||
for (size_t i = 0; i < c->directories[t].n_items; i++) {
|
||||
char *e;
|
||||
|
||||
if (exec_directory_is_private(c, t))
|
||||
e = path_join(p->prefix[t], "private", *i);
|
||||
e = path_join(p->prefix[t], "private", c->directories[t].items[i].path);
|
||||
else
|
||||
e = path_join(p->prefix[t], *i);
|
||||
e = path_join(p->prefix[t], c->directories[t].items[i].path);
|
||||
if (!e)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -4220,8 +4271,10 @@ static int exec_child(
|
||||
}
|
||||
}
|
||||
|
||||
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
|
||||
|
||||
for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
|
||||
r = setup_exec_directory(context, params, uid, gid, dt, exit_status);
|
||||
r = setup_exec_directory(context, params, uid, gid, dt, needs_mount_namespace, exit_status);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(unit, r, "Failed to set up special execution directory in %s: %m", params->prefix[dt]);
|
||||
}
|
||||
@ -4408,7 +4461,6 @@ static int exec_child(
|
||||
log_unit_warning(unit, "PrivateIPC=yes is configured, but the kernel does not support IPC namespaces, ignoring.");
|
||||
}
|
||||
|
||||
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
|
||||
if (needs_mount_namespace) {
|
||||
_cleanup_free_ char *error_path = NULL;
|
||||
|
||||
@ -5081,7 +5133,7 @@ void exec_context_done(ExecContext *c) {
|
||||
c->address_families = set_free(c->address_families);
|
||||
|
||||
for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
|
||||
c->directories[t].paths = strv_free(c->directories[t].paths);
|
||||
exec_directory_done(&c->directories[t]);
|
||||
|
||||
c->log_level_max = -1;
|
||||
|
||||
@ -5103,26 +5155,39 @@ void exec_context_done(ExecContext *c) {
|
||||
}
|
||||
|
||||
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
|
||||
char **i;
|
||||
|
||||
assert(c);
|
||||
|
||||
if (!runtime_prefix)
|
||||
return 0;
|
||||
|
||||
STRV_FOREACH(i, c->directories[EXEC_DIRECTORY_RUNTIME].paths) {
|
||||
for (size_t i = 0; i < c->directories[EXEC_DIRECTORY_RUNTIME].n_items; i++) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
|
||||
p = path_join(runtime_prefix, "private", *i);
|
||||
p = path_join(runtime_prefix, "private", c->directories[EXEC_DIRECTORY_RUNTIME].items[i].path);
|
||||
else
|
||||
p = path_join(runtime_prefix, *i);
|
||||
p = path_join(runtime_prefix, c->directories[EXEC_DIRECTORY_RUNTIME].items[i].path);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We execute this synchronously, since we need to be sure this is gone when we start the
|
||||
* service next. */
|
||||
(void) rm_rf(p, REMOVE_ROOT);
|
||||
|
||||
char **symlink;
|
||||
STRV_FOREACH(symlink, c->directories[EXEC_DIRECTORY_RUNTIME].items[i].symlinks) {
|
||||
_cleanup_free_ char *symlink_abs = NULL;
|
||||
|
||||
if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
|
||||
symlink_abs = path_join(runtime_prefix, "private", *symlink);
|
||||
else
|
||||
symlink_abs = path_join(runtime_prefix, *symlink);
|
||||
if (!symlink_abs)
|
||||
return -ENOMEM;
|
||||
|
||||
(void) unlink(symlink_abs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -5534,8 +5599,12 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||||
for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
|
||||
fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode);
|
||||
|
||||
STRV_FOREACH(d, c->directories[dt].paths)
|
||||
fprintf(f, "%s%s: %s\n", prefix, exec_directory_type_to_string(dt), *d);
|
||||
for (size_t i = 0; i < c->directories[dt].n_items; i++) {
|
||||
fprintf(f, "%s%s: %s\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].items[i].path);
|
||||
|
||||
STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
|
||||
fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC));
|
||||
@ -6009,18 +6078,16 @@ int exec_context_get_clean_directories(
|
||||
assert(ret);
|
||||
|
||||
for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
|
||||
char **i;
|
||||
|
||||
if (!FLAGS_SET(mask, 1U << t))
|
||||
continue;
|
||||
|
||||
if (!prefix[t])
|
||||
continue;
|
||||
|
||||
STRV_FOREACH(i, c->directories[t].paths) {
|
||||
for (size_t i = 0; i < c->directories[t].n_items; i++) {
|
||||
char *j;
|
||||
|
||||
j = path_join(prefix[t], *i);
|
||||
j = path_join(prefix[t], c->directories[t].items[i].path);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -6030,7 +6097,18 @@ int exec_context_get_clean_directories(
|
||||
|
||||
/* Also remove private directories unconditionally. */
|
||||
if (t != EXEC_DIRECTORY_CONFIGURATION) {
|
||||
j = path_join(prefix[t], "private", *i);
|
||||
j = path_join(prefix[t], "private", c->directories[t].items[i].path);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_consume(&l, j);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
char **symlink;
|
||||
STRV_FOREACH(symlink, c->directories[t].items[i].symlinks) {
|
||||
j = path_join(prefix[t], *symlink);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -6052,7 +6130,7 @@ int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
|
||||
assert(ret);
|
||||
|
||||
for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
|
||||
if (!strv_isempty(c->directories[t].paths))
|
||||
if (c->directories[t].n_items > 0)
|
||||
mask |= 1U << t;
|
||||
|
||||
*ret = mask;
|
||||
@ -6730,6 +6808,49 @@ ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc) {
|
||||
return mfree(lc);
|
||||
}
|
||||
|
||||
void exec_directory_done(ExecDirectory *d) {
|
||||
if (!d)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < d->n_items; i++) {
|
||||
free(d->items[i].path);
|
||||
strv_free(d->items[i].symlinks);
|
||||
}
|
||||
|
||||
d->items = mfree(d->items);
|
||||
d->n_items = 0;
|
||||
d->mode = 0755;
|
||||
}
|
||||
|
||||
int exec_directory_add(ExecDirectoryItem **d, size_t *n, const char *path, char **symlinks) {
|
||||
_cleanup_strv_free_ char **s = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
assert(d);
|
||||
assert(n);
|
||||
assert(path);
|
||||
|
||||
p = strdup(path);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
if (symlinks) {
|
||||
s = strv_copy(symlinks);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(*d, *n + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
(*d)[(*n) ++] = (ExecDirectoryItem) {
|
||||
.path = TAKE_PTR(p),
|
||||
.symlinks = TAKE_PTR(s),
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_set_credential_hash_ops, char, string_hash_func, string_compare_func, ExecSetCredential, exec_set_credential_free);
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_load_credential_hash_ops, char, string_hash_func, string_compare_func, ExecLoadCredential, exec_load_credential_free);
|
||||
|
||||
@ -6790,6 +6911,17 @@ static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
|
||||
|
||||
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
|
||||
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
|
||||
[EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectorySymlink",
|
||||
[EXEC_DIRECTORY_STATE] = "StateDirectorySymlink",
|
||||
[EXEC_DIRECTORY_CACHE] = "CacheDirectorySymlink",
|
||||
[EXEC_DIRECTORY_LOGS] = "LogsDirectorySymlink",
|
||||
[EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
|
||||
|
||||
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
|
||||
* one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
|
||||
* directories, specifically .timer units with their timestamp touch file. */
|
||||
|
@ -132,9 +132,15 @@ typedef enum ExecDirectoryType {
|
||||
_EXEC_DIRECTORY_TYPE_INVALID = -EINVAL,
|
||||
} ExecDirectoryType;
|
||||
|
||||
typedef struct ExecDirectoryItem {
|
||||
char *path;
|
||||
char **symlinks;
|
||||
} ExecDirectoryItem;
|
||||
|
||||
typedef struct ExecDirectory {
|
||||
char **paths;
|
||||
mode_t mode;
|
||||
size_t n_items;
|
||||
ExecDirectoryItem *items;
|
||||
} ExecDirectory;
|
||||
|
||||
typedef enum ExecCleanMask {
|
||||
@ -479,6 +485,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSetCredential*, exec_set_credential_free);
|
||||
ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecLoadCredential*, exec_load_credential_free);
|
||||
|
||||
void exec_directory_done(ExecDirectory *d);
|
||||
int exec_directory_add(ExecDirectoryItem **d, size_t *n, const char *path, char **symlinks);
|
||||
|
||||
extern const struct hash_ops exec_set_credential_hash_ops;
|
||||
extern const struct hash_ops exec_load_credential_hash_ops;
|
||||
|
||||
@ -500,6 +509,9 @@ ExecKeyringMode exec_keyring_mode_from_string(const char *s) _pure_;
|
||||
const char* exec_directory_type_to_string(ExecDirectoryType i) _const_;
|
||||
ExecDirectoryType exec_directory_type_from_string(const char *s) _pure_;
|
||||
|
||||
const char* exec_directory_type_symlink_to_string(ExecDirectoryType i) _const_;
|
||||
ExecDirectoryType exec_directory_type_symlink_from_string(const char *s) _pure_;
|
||||
|
||||
const char* exec_resource_type_to_string(ExecDirectoryType i) _const_;
|
||||
ExecDirectoryType exec_resource_type_from_string(const char *s) _pure_;
|
||||
|
||||
|
@ -131,15 +131,15 @@
|
||||
{{type}}.Personality, config_parse_personality, 0, offsetof({{type}}, exec_context.personality)
|
||||
{{type}}.RuntimeDirectoryPreserve, config_parse_runtime_preserve_mode, 0, offsetof({{type}}, exec_context.runtime_directory_preserve_mode)
|
||||
{{type}}.RuntimeDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME].mode)
|
||||
{{type}}.RuntimeDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME].paths)
|
||||
{{type}}.RuntimeDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME])
|
||||
{{type}}.StateDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE].mode)
|
||||
{{type}}.StateDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE].paths)
|
||||
{{type}}.StateDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE])
|
||||
{{type}}.CacheDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE].mode)
|
||||
{{type}}.CacheDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE].paths)
|
||||
{{type}}.CacheDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE])
|
||||
{{type}}.LogsDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS].mode)
|
||||
{{type}}.LogsDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS].paths)
|
||||
{{type}}.LogsDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS])
|
||||
{{type}}.ConfigurationDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].mode)
|
||||
{{type}}.ConfigurationDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].paths)
|
||||
{{type}}.ConfigurationDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION])
|
||||
{{type}}.SetCredential, config_parse_set_credential, 0, offsetof({{type}}, exec_context)
|
||||
{{type}}.SetCredentialEncrypted, config_parse_set_credential, 1, offsetof({{type}}, exec_context)
|
||||
{{type}}.LoadCredential, config_parse_load_credential, 0, offsetof({{type}}, exec_context)
|
||||
|
@ -4511,7 +4511,7 @@ int config_parse_exec_directories(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char***rt = data;
|
||||
ExecDirectory *ed = data;
|
||||
const Unit *u = userdata;
|
||||
int r;
|
||||
|
||||
@ -4522,45 +4522,84 @@ int config_parse_exec_directories(
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
/* Empty assignment resets the list */
|
||||
*rt = strv_free(*rt);
|
||||
exec_directory_done(ed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const char *p = rvalue;;) {
|
||||
_cleanup_free_ char *word = NULL, *k = NULL;
|
||||
_cleanup_free_ char *tuple = NULL;
|
||||
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
|
||||
r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Invalid syntax, ignoring: %s", rvalue);
|
||||
"Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
r = unit_path_printf(u, word, &k);
|
||||
_cleanup_free_ char *src = NULL, *dest = NULL;
|
||||
const char *q = tuple;
|
||||
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &src, &dest, NULL);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r <= 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r ?: SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *sresolved = NULL;
|
||||
r = unit_path_printf(u, src, &sresolved);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
|
||||
"Failed to resolve unit specifiers in \"%s\", ignoring: %m", src);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = path_simplify_and_warn(k, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
|
||||
r = path_simplify_and_warn(sresolved, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
if (path_startswith(k, "private")) {
|
||||
if (path_startswith(sresolved, "private")) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"%s= path can't be 'private', ignoring assignment: %s", lvalue, word);
|
||||
"%s= path can't be 'private', ignoring assignment: %s", lvalue, tuple);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = strv_push(rt, k);
|
||||
/* For State and Runtime directories we support an optional destination parameter, which
|
||||
* will be used to create a symlink to the source. */
|
||||
_cleanup_strv_free_ char **symlinks = NULL;
|
||||
if (!isempty(dest)) {
|
||||
_cleanup_free_ char *dresolved = NULL;
|
||||
|
||||
if (streq(lvalue, "ConfigurationDirectory")) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"Destination parameter is not supported for ConfigurationDirectory, ignoring: %s", tuple);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = unit_path_printf(u, dest, &dresolved);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to resolve unit specifiers in \"%s\", ignoring: %m", dest);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = path_simplify_and_warn(dresolved, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
r = strv_consume(&symlinks, TAKE_PTR(dresolved));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = exec_directory_add(&ed->items, &ed->n_items, sresolved, symlinks);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
k = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1253,11 +1253,10 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
|
||||
if (!u->manager->prefix[dt])
|
||||
continue;
|
||||
|
||||
char **dp;
|
||||
STRV_FOREACH(dp, c->directories[dt].paths) {
|
||||
for (size_t i = 0; i < c->directories[dt].n_items; i++) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
p = path_join(u->manager->prefix[dt], *dp);
|
||||
p = path_join(u->manager->prefix[dt], c->directories[dt].items[i].path);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1272,9 +1271,9 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
|
||||
|
||||
/* For the following three directory types we need write access, and /var/ is possibly on the root
|
||||
* fs. Hence order after systemd-remount-fs.service, to ensure things are writable. */
|
||||
if (!strv_isempty(c->directories[EXEC_DIRECTORY_STATE].paths) ||
|
||||
!strv_isempty(c->directories[EXEC_DIRECTORY_CACHE].paths) ||
|
||||
!strv_isempty(c->directories[EXEC_DIRECTORY_LOGS].paths)) {
|
||||
if (c->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
|
||||
c->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
|
||||
c->directories[EXEC_DIRECTORY_LOGS].n_items > 0) {
|
||||
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, true, UNIT_DEPENDENCY_FILE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "libmount-util.h"
|
||||
#include "locale-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "missing_fs.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "nsflags.h"
|
||||
@ -970,10 +971,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
|
||||
"ExecPaths",
|
||||
"NoExecPaths",
|
||||
"ExecSearchPath",
|
||||
"RuntimeDirectory",
|
||||
"StateDirectory",
|
||||
"CacheDirectory",
|
||||
"LogsDirectory",
|
||||
"ConfigurationDirectory",
|
||||
"SupplementaryGroups",
|
||||
"SystemCallArchitectures"))
|
||||
@ -1926,6 +1923,125 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (STR_IN_SET(field, "StateDirectory", "RuntimeDirectory", "CacheDirectory", "LogsDirectory")) {
|
||||
_cleanup_strv_free_ char **symlinks = NULL, **sources = NULL;
|
||||
const char *p = eq;
|
||||
|
||||
/* Adding new directories is supported from both *DirectorySymlink methods and the
|
||||
* older ones, so first parse the input, and if we are given a new-style src:dst
|
||||
* tuple use the new method, else use the old one. */
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *tuple = NULL, *source = NULL, *destination = NULL;
|
||||
|
||||
r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse argument: %m");
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
const char *t = tuple;
|
||||
r = extract_many_words(&t, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL);
|
||||
if (r <= 0)
|
||||
return log_error_errno(r ?: SYNTHETIC_ERRNO(EINVAL), "Failed to parse argument: %m");
|
||||
|
||||
path_simplify(source);
|
||||
|
||||
if (isempty(destination)) {
|
||||
r = strv_extend(&sources, TAKE_PTR(source));
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
} else {
|
||||
path_simplify(destination);
|
||||
|
||||
r = strv_consume_pair(&symlinks, TAKE_PTR(source), TAKE_PTR(destination));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
}
|
||||
|
||||
if (!strv_isempty(sources)) {
|
||||
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "as");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append_strv(m, sources);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
}
|
||||
|
||||
/* For State and Runtime directories we support an optional destination parameter, which
|
||||
* will be used to create a symlink to the source. But it is new so we cannot change the
|
||||
* old DBUS signatures, so append a new message type. */
|
||||
if (!strv_isempty(symlinks)) {
|
||||
const char *symlink_field;
|
||||
|
||||
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
if (streq(field, "StateDirectory"))
|
||||
symlink_field = "StateDirectorySymlink";
|
||||
else if (streq(field, "RuntimeDirectory"))
|
||||
symlink_field = "RuntimeDirectorySymlink";
|
||||
else if (streq(field, "CacheDirectory"))
|
||||
symlink_field = "CacheDirectorySymlink";
|
||||
else if (streq(field, "LogsDirectory"))
|
||||
symlink_field = "LogsDirectorySymlink";
|
||||
else
|
||||
assert_not_reached();
|
||||
|
||||
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, symlink_field);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "a(sst)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "(sst)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
char **source, **destination;
|
||||
STRV_FOREACH_PAIR(source, destination, symlinks) {
|
||||
r = sd_bus_message_append(m, "(sst)", *source, *destination, 0);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1773,6 +1773,24 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return 1;
|
||||
} else if (STR_IN_SET(name, "StateDirectorySymlink", "RuntimeDirectorySymlink", "CacheDirectorySymlink", "LogsDirectorySymlink")) {
|
||||
const char *a, *p;
|
||||
uint64_t symlink_flags;
|
||||
|
||||
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sst)");
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
while ((r = sd_bus_message_read(m, "(sst)", &a, &p, &symlink_flags)) > 0)
|
||||
bus_print_property_valuef(name, expected_value, flags, "%s:%s", a, p);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = sd_bus_message_exit_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -6,45 +6,89 @@ set -o pipefail
|
||||
systemd-analyze log-level debug
|
||||
systemd-analyze log-target console
|
||||
|
||||
# Set everything up without DynamicUser=1
|
||||
function test_directory() {
|
||||
local directory="$1"
|
||||
local path="$2"
|
||||
|
||||
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz touch /var/lib/zzz/test
|
||||
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
|
||||
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz -p TemporaryFileSystem=/var/lib test -f /var/lib/zzz/test
|
||||
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
|
||||
&& { echo 'unexpected success'; exit 1; }
|
||||
# Set everything up without DynamicUser=1
|
||||
|
||||
test -d /var/lib/zzz
|
||||
test ! -L /var/lib/zzz
|
||||
test ! -e /var/lib/private/zzz
|
||||
test -f /var/lib/zzz/test
|
||||
test ! -f /var/lib/zzz/test-missing
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz touch "${path}"/zzz/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}" test -f "${path}"/xxx/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing \
|
||||
&& { echo 'unexpected success'; exit 1; }
|
||||
|
||||
# Convert to DynamicUser=1
|
||||
test -d "${path}"/zzz
|
||||
test ! -L "${path}"/zzz
|
||||
test ! -e "${path}"/private/zzz
|
||||
test -f "${path}"/zzz/test
|
||||
test ! -f "${path}"/zzz/test-missing
|
||||
|
||||
systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test
|
||||
systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz -p TemporaryFileSystem=/var/lib test -f /var/lib/zzz/test
|
||||
systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
|
||||
&& { echo 'unexpected success'; exit 1; }
|
||||
# Convert to DynamicUser=1
|
||||
|
||||
test -L /var/lib/zzz
|
||||
test -d /var/lib/private/zzz
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}" test -f "${path}"/xxx/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing \
|
||||
&& { echo 'unexpected success'; exit 1; }
|
||||
|
||||
test -f /var/lib/zzz/test
|
||||
test ! -f /var/lib/zzz/test-missing
|
||||
test -L "${path}"/zzz
|
||||
test -L "${path}"/yyy
|
||||
test -d "${path}"/private/zzz
|
||||
test ! -L "${path}"/private/xxx
|
||||
test ! -L "${path}"/xxx
|
||||
|
||||
# Convert back
|
||||
test -f "${path}"/zzz/test
|
||||
test ! -f "${path}"/zzz/test-missing
|
||||
|
||||
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
|
||||
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz -p TemporaryFileSystem=/var/lib test -f /var/lib/zzz/test
|
||||
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
|
||||
&& { echo 'unexpected success'; exit 1; }
|
||||
# Convert back
|
||||
|
||||
test -d /var/lib/zzz
|
||||
test ! -L /var/lib/zzz
|
||||
test ! -e /var/lib/private/zzz
|
||||
test -f /var/lib/zzz/test
|
||||
test ! -f /var/lib/zzz/test-missing
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}" test -f "${path}"/xxx/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
|
||||
systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing \
|
||||
&& { echo 'unexpected success'; exit 1; }
|
||||
|
||||
# Exercise the unit parsing paths too
|
||||
cat >/run/systemd/system/testservice-34.service <<EOF
|
||||
[Service]
|
||||
Type=oneshot
|
||||
TemporaryFileSystem=${path}
|
||||
RuntimeDirectoryPreserve=yes
|
||||
${directory}=zzz:x\:yz zzz:x\:yz2
|
||||
ExecStart=test -f ${path}/x:yz2/test
|
||||
ExecStart=test -f ${path}/x:yz/test
|
||||
ExecStart=test -f ${path}/zzz/test
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl start --wait testservice-34.service
|
||||
|
||||
test -d "${path}"/zzz
|
||||
test ! -L "${path}"/xxx
|
||||
test ! -L "${path}"/xxx2
|
||||
test ! -L "${path}"/private/xxx
|
||||
test ! -L "${path}"/private/xxx2
|
||||
test -L "${path}"/yyy
|
||||
test ! -L "${path}"/zzz
|
||||
test ! -e "${path}"/private/zzz
|
||||
test -f "${path}"/zzz/test
|
||||
test ! -f "${path}"/zzz/test-missing
|
||||
test ! -L "${path}"/x:yz
|
||||
test ! -L "${path}"/x:yz2
|
||||
}
|
||||
|
||||
test_directory "StateDirectory" "/var/lib"
|
||||
test_directory "RuntimeDirectory" "/run"
|
||||
test_directory "CacheDirectory" "/var/cache"
|
||||
test_directory "LogsDirectory" "/var/log"
|
||||
|
||||
systemd-analyze log-level info
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user