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:
+153
-9
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user