summaryrefslogtreecommitdiff
path: root/src/shared/bus-unit-util.c
diff options
context:
space:
mode:
authorLuca Boccassi <luca.boccassi@microsoft.com>2021-07-14 18:22:21 +0100
committerLuca Boccassi <luca.boccassi@microsoft.com>2021-10-28 10:47:46 +0100
commit211a3d87fb1fe971dc42a47b4c5cc167def8ab4e (patch)
treef0e82b93e250abfab6b25a67af8a45bc9d4a2e45 /src/shared/bus-unit-util.c
parentdf61e79a5d9d6adfb8ea7f5f9dcb98fdda136910 (diff)
downloadsystemd-211a3d87fb1fe971dc42a47b4c5cc167def8ab4e.tar.gz
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.
Diffstat (limited to 'src/shared/bus-unit-util.c')
-rw-r--r--src/shared/bus-unit-util.c124
1 files changed, 120 insertions, 4 deletions
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 7df1e0b310..dbd33420be 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -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;
}