X Tutup
Skip to content

Commit 211a3d8

Browse files
committed
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.
1 parent df61e79 commit 211a3d8

File tree

11 files changed

+712
-156
lines changed

11 files changed

+712
-156
lines changed

man/org.freedesktop.systemd1.xml

Lines changed: 71 additions & 32 deletions
Large diffs are not rendered by default.

man/systemd.exec.xml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1327,6 +1327,13 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
13271327
configuration or lifetime guarantees, please consider using
13281328
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
13291329

1330+
<para><varname>RuntimeDirectory=</varname>, <varname>StateDirectory=</varname>, <varname>CacheDirectory=</varname>
1331+
and <varname>LogsDirectory=</varname> optionally support a second parameter, separated by <literal>:</literal>.
1332+
The second parameter will be interpreted as a destination path that will be created as a symlink to the directory.
1333+
The symlinks will be created after any <varname>BindPaths=</varname> or <varname>TemporaryFileSystem=</varname>
1334+
options have been set up, to make ephemeral symlinking possible. The same source can have multiple symlinks, by
1335+
using the same first parameter, but a diferent second parameter.</para></listitem>
1336+
13301337
<para>The directories defined by these options are always created under the standard paths used by systemd
13311338
(<filename>/var/</filename>, <filename>/run/</filename>, <filename>/etc/</filename>, …). If the service needs
13321339
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>
13551362
<programlisting>RuntimeDirectory=foo/bar
13561363
StateDirectory=aaa/bbb ccc</programlisting>
13571364
then the environment variable <literal>RUNTIME_DIRECTORY</literal> is set with <literal>/run/foo/bar</literal>, and
1358-
<literal>STATE_DIRECTORY</literal> is set with <literal>/var/lib/aaa/bbb:/var/lib/ccc</literal>.</para></listitem>
1365+
<literal>STATE_DIRECTORY</literal> is set with <literal>/var/lib/aaa/bbb:/var/lib/ccc</literal>.</para>
1366+
1367+
<para>Example: if a system service unit has the following,
1368+
<programlisting>RuntimeDirectory=foo:bar foo:baz</programlisting>
1369+
the service manager creates <filename index='false'>/run/foo</filename> (if it does not exist), and
1370+
<filename index='false'>/run/bar</filename> plus <filename index='false'>/run/baz</filename> as symlinks to
1371+
<filename index='false'>/run/foo</filename>.</para>
13591372
</varlistentry>
13601373

13611374
<varlistentry>

src/core/dbus-execute.c

Lines changed: 153 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,70 @@ static int property_get_extension_images(
10911091
return sd_bus_message_close_container(reply);
10921092
}
10931093

1094+
static int bus_property_get_exec_dir(
1095+
sd_bus *bus,
1096+
const char *path,
1097+
const char *interface,
1098+
const char *property,
1099+
sd_bus_message *reply,
1100+
void *userdata,
1101+
sd_bus_error *error) {
1102+
1103+
ExecDirectory *d = userdata;
1104+
int r;
1105+
1106+
assert(bus);
1107+
assert(d);
1108+
assert(property);
1109+
assert(reply);
1110+
1111+
r = sd_bus_message_open_container(reply, 'a', "s");
1112+
if (r < 0)
1113+
return r;
1114+
1115+
for (size_t i = 0; i < d->n_items; i++) {
1116+
r = sd_bus_message_append_basic(reply, 's', d->items[i].path);
1117+
if (r < 0)
1118+
return r;
1119+
}
1120+
1121+
return sd_bus_message_close_container(reply);
1122+
}
1123+
1124+
static int bus_property_get_exec_dir_symlink(
1125+
sd_bus *bus,
1126+
const char *path,
1127+
const char *interface,
1128+
const char *property,
1129+
sd_bus_message *reply,
1130+
void *userdata,
1131+
sd_bus_error *error) {
1132+
1133+
ExecDirectory *d = userdata;
1134+
int r;
1135+
1136+
assert(bus);
1137+
assert(d);
1138+
assert(property);
1139+
assert(reply);
1140+
1141+
r = sd_bus_message_open_container(reply, 'a', "(sst)");
1142+
if (r < 0)
1143+
return r;
1144+
1145+
for (size_t i = 0; i < d->n_items; i++) {
1146+
char **dst;
1147+
1148+
STRV_FOREACH(dst, d->items[i].symlinks) {
1149+
r = sd_bus_message_append(reply, "(sst)", d->items[i].path, *dst, 0 /* flags, unused for now */);
1150+
if (r < 0)
1151+
return r;
1152+
}
1153+
}
1154+
1155+
return sd_bus_message_close_container(reply);
1156+
}
1157+
10941158
const sd_bus_vtable bus_exec_vtable[] = {
10951159
SD_BUS_VTABLE_START(0),
10961160
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[] = {
12241288
SD_BUS_PROPERTY("Personality", "s", property_get_personality, offsetof(ExecContext, personality), SD_BUS_VTABLE_PROPERTY_CONST),
12251289
SD_BUS_PROPERTY("LockPersonality", "b", bus_property_get_bool, offsetof(ExecContext, lock_personality), SD_BUS_VTABLE_PROPERTY_CONST),
12261290
SD_BUS_PROPERTY("RestrictAddressFamilies", "(bas)", property_get_address_families, 0, SD_BUS_VTABLE_PROPERTY_CONST),
1291+
SD_BUS_PROPERTY("RuntimeDirectorySymlink", "a(sst)", bus_property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
12271292
SD_BUS_PROPERTY("RuntimeDirectoryPreserve", "s", property_get_exec_preserve_mode, offsetof(ExecContext, runtime_directory_preserve_mode), SD_BUS_VTABLE_PROPERTY_CONST),
12281293
SD_BUS_PROPERTY("RuntimeDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME].mode), SD_BUS_VTABLE_PROPERTY_CONST),
1229-
SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME].paths), SD_BUS_VTABLE_PROPERTY_CONST),
1294+
SD_BUS_PROPERTY("RuntimeDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
1295+
SD_BUS_PROPERTY("StateDirectorySymlink", "a(sst)", bus_property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE]), SD_BUS_VTABLE_PROPERTY_CONST),
12301296
SD_BUS_PROPERTY("StateDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE].mode), SD_BUS_VTABLE_PROPERTY_CONST),
1231-
SD_BUS_PROPERTY("StateDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE].paths), SD_BUS_VTABLE_PROPERTY_CONST),
1297+
SD_BUS_PROPERTY("StateDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE]), SD_BUS_VTABLE_PROPERTY_CONST),
1298+
SD_BUS_PROPERTY("CacheDirectorySymlink", "a(sst)", bus_property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE]), SD_BUS_VTABLE_PROPERTY_CONST),
12321299
SD_BUS_PROPERTY("CacheDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE].mode), SD_BUS_VTABLE_PROPERTY_CONST),
1233-
SD_BUS_PROPERTY("CacheDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE].paths), SD_BUS_VTABLE_PROPERTY_CONST),
1300+
SD_BUS_PROPERTY("CacheDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_CACHE]), SD_BUS_VTABLE_PROPERTY_CONST),
1301+
SD_BUS_PROPERTY("LogsDirectorySymlink", "a(sst)", bus_property_get_exec_dir_symlink, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS]), SD_BUS_VTABLE_PROPERTY_CONST),
12341302
SD_BUS_PROPERTY("LogsDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS].mode), SD_BUS_VTABLE_PROPERTY_CONST),
1235-
SD_BUS_PROPERTY("LogsDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS].paths), SD_BUS_VTABLE_PROPERTY_CONST),
1303+
SD_BUS_PROPERTY("LogsDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_LOGS]), SD_BUS_VTABLE_PROPERTY_CONST),
12361304
SD_BUS_PROPERTY("ConfigurationDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_CONFIGURATION].mode), SD_BUS_VTABLE_PROPERTY_CONST),
1237-
SD_BUS_PROPERTY("ConfigurationDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_CONFIGURATION].paths), SD_BUS_VTABLE_PROPERTY_CONST),
1305+
SD_BUS_PROPERTY("ConfigurationDirectory", "as", bus_property_get_exec_dir, offsetof(ExecContext, directories[EXEC_DIRECTORY_CONFIGURATION]), SD_BUS_VTABLE_PROPERTY_CONST),
12381306
SD_BUS_PROPERTY("TimeoutCleanUSec", "t", bus_property_get_usec, offsetof(ExecContext, timeout_clean_usec), SD_BUS_VTABLE_PROPERTY_CONST),
12391307
SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST),
12401308
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(
32943362
d = c->directories + i;
32953363

32963364
if (strv_isempty(l)) {
3297-
d->paths = strv_free(d->paths);
3365+
exec_directory_done(d);
32983366
unit_write_settingf(u, flags, name, "%s=", name);
32993367
} else {
33003368
_cleanup_free_ char *joined = NULL;
3369+
char **source;
33013370

3302-
r = strv_extend_strv(&d->paths, l, true);
3303-
if (r < 0)
3304-
return r;
3371+
STRV_FOREACH(source, l) {
3372+
r = exec_directory_add(&d->items, &d->n_items, *source, NULL);
3373+
if (r < 0)
3374+
return log_oom();
3375+
}
33053376

33063377
joined = unit_concat_strv(l, UNIT_ESCAPE_SPECIFIERS);
33073378
if (!joined)
@@ -3711,6 +3782,79 @@ int bus_exec_context_set_transient_property(
37113782
extension_images = mount_image_free_many(extension_images, &n_extension_images);
37123783

37133784
return 1;
3785+
3786+
} else if (STR_IN_SET(name, "StateDirectorySymlink", "RuntimeDirectorySymlink", "CacheDirectorySymlink", "LogsDirectorySymlink")) {
3787+
char *source, *destination;
3788+
ExecDirectory *directory;
3789+
uint64_t symlink_flags; /* No flags for now, reserved for future uses. */
3790+
ExecDirectoryType i;
3791+
3792+
assert_se((i = exec_directory_type_symlink_from_string(name)) >= 0);
3793+
directory = c->directories + i;
3794+
3795+
r = sd_bus_message_enter_container(message, 'a', "(sst)");
3796+
if (r < 0)
3797+
return r;
3798+
3799+
while ((r = sd_bus_message_read(message, "(sst)", &source, &destination, &symlink_flags)) > 0) {
3800+
if (!path_is_valid(source))
3801+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not valid.", source);
3802+
if (path_is_absolute(source))
3803+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is absolute.", source);
3804+
if (!path_is_normalized(source))
3805+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not normalized.", source);
3806+
if (!path_is_valid(destination))
3807+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not valid.", destination);
3808+
if (path_is_absolute(destination))
3809+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is absolute.", destination);
3810+
if (!path_is_normalized(destination))
3811+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not normalized.", destination);
3812+
if (symlink_flags != 0)
3813+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
3814+
3815+
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
3816+
_cleanup_free_ char *destination_escaped = NULL, *source_escaped = NULL;
3817+
ExecDirectoryItem *item = NULL;
3818+
3819+
/* Adding new directories is supported from both *DirectorySymlink methods and the
3820+
* older ones, so try to find an existing configuration first and create it if it's
3821+
* not there yet. */
3822+
for (size_t j = 0; j < directory->n_items; ++j)
3823+
if (path_equal(source, directory->items[j].path)) {
3824+
item = &directory->items[j];
3825+
break;
3826+
}
3827+
3828+
if (item)
3829+
r = strv_extend(&item->symlinks, destination);
3830+
else
3831+
r = exec_directory_add(&directory->items, &directory->n_items, source, STRV_MAKE(destination));
3832+
if (r < 0)
3833+
return r;
3834+
3835+
/* Need to store them in the unit with the escapes, so that they can be parsed again */
3836+
source_escaped = xescape(source, ":");
3837+
destination_escaped = xescape(destination, ":");
3838+
if (!source_escaped || !destination_escaped)
3839+
return -ENOMEM;
3840+
3841+
unit_write_settingf(
3842+
u, flags|UNIT_ESCAPE_SPECIFIERS, exec_directory_type_to_string(i),
3843+
"%s=%s:%s",
3844+
exec_directory_type_to_string(i),
3845+
source_escaped,
3846+
destination_escaped);
3847+
}
3848+
}
3849+
if (r < 0)
3850+
return r;
3851+
3852+
r = sd_bus_message_exit_container(message);
3853+
if (r < 0)
3854+
return r;
3855+
3856+
return 1;
3857+
37143858
}
37153859

37163860
return 0;

0 commit comments

Comments
 (0)
X Tutup