mirror of
https://github.com/systemd/systemd.git
synced 2024-12-12 19:53:55 +08:00
Merge pull request #20321 from bluca/state_dir_symlink
core: add [State|Runtime|Cache|Logs]DirectorySymlink
This commit is contained in:
commit
ff8b7bd6a0
@ -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;
|
||||
}
|
||||
@ -2384,7 +2417,9 @@ static int setup_exec_directory(
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* And link it up from the original place */
|
||||
/* And link it up from the original place. Note that if a mount namespace is going to be
|
||||
* used, then this symlink remains on the host, and a new one for the child namespace will
|
||||
* be created later. */
|
||||
r = symlink_idempotent(pp, p, true);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -2407,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;
|
||||
@ -2460,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;
|
||||
@ -2483,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 +3078,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 +3119,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 +3144,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 +3161,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) {
|
||||
@ -3150,6 +3194,61 @@ finish:
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ret_symlinks will contain a list of pairs src:dest that describes
|
||||
* the symlinks to create later on. For example, the symlinks needed
|
||||
* to safely give private directories to DynamicUser=1 users. */
|
||||
static int compile_symlinks(
|
||||
const ExecContext *context,
|
||||
const ExecParameters *params,
|
||||
char ***ret_symlinks) {
|
||||
|
||||
_cleanup_strv_free_ char **symlinks = NULL;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(params);
|
||||
assert(ret_symlinks);
|
||||
|
||||
for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
|
||||
for (size_t i = 0; i < context->directories[dt].n_items; i++) {
|
||||
_cleanup_free_ char *private_path = NULL, *path = NULL;
|
||||
char **symlink;
|
||||
|
||||
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], context->directories[dt].items[i].path);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_consume_pair(&symlinks, TAKE_PTR(private_path), TAKE_PTR(path));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
*ret_symlinks = TAKE_PTR(symlinks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool insist_on_sandboxing(
|
||||
const ExecContext *context,
|
||||
const char *root_dir,
|
||||
@ -3196,7 +3295,7 @@ static int apply_mount_namespace(
|
||||
const ExecRuntime *runtime,
|
||||
char **error_path) {
|
||||
|
||||
_cleanup_strv_free_ char **empty_directories = NULL;
|
||||
_cleanup_strv_free_ char **empty_directories = NULL, **symlinks = NULL;
|
||||
const char *tmp_dir = NULL, *var_tmp_dir = NULL;
|
||||
const char *root_dir = NULL, *root_image = NULL;
|
||||
_cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL;
|
||||
@ -3219,6 +3318,11 @@ 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. */
|
||||
r = compile_symlinks(context, params, &symlinks);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command_flags & EXEC_COMMAND_FULLY_PRIVILEGED);
|
||||
if (needs_sandboxing) {
|
||||
/* The runtime struct only contains the parent of the private /tmp,
|
||||
@ -3302,6 +3406,7 @@ static int apply_mount_namespace(
|
||||
needs_sandboxing ? context->exec_paths : NULL,
|
||||
needs_sandboxing ? context->no_exec_paths : NULL,
|
||||
empty_directories,
|
||||
symlinks,
|
||||
bind_mounts,
|
||||
n_bind_mounts,
|
||||
context->temporary_filesystems,
|
||||
@ -3636,21 +3741,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;
|
||||
|
||||
@ -4170,8 +4273,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]);
|
||||
}
|
||||
@ -4358,7 +4463,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;
|
||||
|
||||
@ -5031,7 +5135,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;
|
||||
|
||||
@ -5053,26 +5157,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;
|
||||
@ -5484,8 +5601,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));
|
||||
@ -5959,18 +6080,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;
|
||||
|
||||
@ -5980,7 +6099,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;
|
||||
|
||||
@ -6002,7 +6132,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;
|
||||
@ -6680,6 +6810,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);
|
||||
|
||||
@ -6740,6 +6913,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)
|
||||
|
@ -4601,7 +4601,7 @@ int config_parse_exec_directories(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char***rt = data;
|
||||
ExecDirectory *ed = data;
|
||||
const Unit *u = userdata;
|
||||
int r;
|
||||
|
||||
@ -4612,45 +4612,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1586,11 +1586,36 @@ static void normalize_mounts(const char *root_directory, MountEntry *mounts, siz
|
||||
drop_nop(mounts, n_mounts);
|
||||
}
|
||||
|
||||
static int create_symlinks_from_tuples(const char *root, char **strv_symlinks) {
|
||||
char **src, **dst;
|
||||
int r;
|
||||
|
||||
STRV_FOREACH_PAIR(src, dst, strv_symlinks) {
|
||||
_cleanup_free_ char *src_abs = NULL, *dst_abs = NULL;
|
||||
|
||||
src_abs = path_join(root, *src);
|
||||
dst_abs = path_join(root, *dst);
|
||||
if (!src_abs || !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 apply_mounts(
|
||||
const char *root,
|
||||
const NamespaceInfo *ns_info,
|
||||
MountEntry *mounts,
|
||||
size_t *n_mounts,
|
||||
char **exec_dir_symlinks,
|
||||
char **error_path) {
|
||||
|
||||
_cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
|
||||
@ -1657,6 +1682,14 @@ static int apply_mounts(
|
||||
normalize_mounts(root, mounts, n_mounts);
|
||||
}
|
||||
|
||||
/* Now that all filesystems have been set up, but before the
|
||||
* read-only switches are flipped, create the exec dirs symlinks.
|
||||
* Note that when /var/lib is not empty/tmpfs, these symlinks will already
|
||||
* exist, which means this will be a no-op. */
|
||||
r = create_symlinks_from_tuples(root, exec_dir_symlinks);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Create a deny list we can pass to bind_mount_recursive() */
|
||||
deny_list = new(char*, (*n_mounts)+1);
|
||||
if (!deny_list)
|
||||
@ -1820,6 +1853,7 @@ int setup_namespace(
|
||||
char** exec_paths,
|
||||
char** no_exec_paths,
|
||||
char** empty_directories,
|
||||
char** exec_dir_symlinks,
|
||||
const BindMount *bind_mounts,
|
||||
size_t n_bind_mounts,
|
||||
const TemporaryFileSystem *temporary_filesystems,
|
||||
@ -2261,7 +2295,7 @@ int setup_namespace(
|
||||
(void) base_filesystem_create(root, UID_INVALID, GID_INVALID);
|
||||
|
||||
/* Now make the magic happen */
|
||||
r = apply_mounts(root, ns_info, mounts, &n_mounts, error_path);
|
||||
r = apply_mounts(root, ns_info, mounts, &n_mounts, exec_dir_symlinks, error_path);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
|
@ -121,6 +121,7 @@ int setup_namespace(
|
||||
char **exec_paths,
|
||||
char **no_exec_paths,
|
||||
char **empty_directories,
|
||||
char **exec_dir_symlinks,
|
||||
const BindMount *bind_mounts,
|
||||
size_t n_bind_mounts,
|
||||
const TemporaryFileSystem *temporary_filesystems,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -166,6 +166,7 @@ static void test_protect_kernel_logs(void) {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL, 0,
|
||||
NULL, 0,
|
||||
NULL, 0,
|
||||
|
@ -83,6 +83,7 @@ int main(int argc, char *argv[]) {
|
||||
(char **) writable,
|
||||
(char **) readonly,
|
||||
(char **) inaccessible,
|
||||
NULL,
|
||||
(char **) exec,
|
||||
(char **) no_exec,
|
||||
NULL,
|
||||
|
@ -6,42 +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 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 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 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