#include "label.h"
#include "log.h"
#include "macro.h"
+#include "manager.h"
#include "missing.h"
#include "mkdir.h"
#include "namespace.h"
return 0;
}
+static void *remove_tmpdir_thread(void *p) {
+ _cleanup_free_ char *path = p;
-static int exec_runtime_allocate(ExecRuntime **rt) {
+ (void) rm_rf(path, REMOVE_ROOT|REMOVE_PHYSICAL);
+ return NULL;
+}
+
+static ExecRuntime* exec_runtime_free(ExecRuntime *rt, bool destroy) {
+ int r;
+
+ if (!rt)
+ return NULL;
+
+ if (rt->manager)
+ (void) hashmap_remove(rt->manager->exec_runtime_by_id, rt->id);
+
+ /* When destroy is true, then rm_rf tmp_dir and var_tmp_dir. */
+ if (destroy && rt->tmp_dir) {
+ log_debug("Spawning thread to nuke %s", rt->tmp_dir);
+
+ r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to nuke %s: %m", rt->tmp_dir);
+ free(rt->tmp_dir);
+ }
+
+ rt->tmp_dir = NULL;
+ }
+ if (destroy && rt->var_tmp_dir) {
+ log_debug("Spawning thread to nuke %s", rt->var_tmp_dir);
+
+ r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to nuke %s: %m", rt->var_tmp_dir);
+ free(rt->var_tmp_dir);
+ }
+
+ rt->var_tmp_dir = NULL;
+ }
+
+ rt->id = mfree(rt->id);
+ rt->tmp_dir = mfree(rt->tmp_dir);
+ rt->var_tmp_dir = mfree(rt->var_tmp_dir);
+ safe_close_pair(rt->netns_storage_socket);
+ return mfree(rt);
+}
+
+static void exec_runtime_freep(ExecRuntime **rt) {
if (*rt)
- return 0;
+ (void) exec_runtime_free(*rt, false);
+}
+
+static int exec_runtime_allocate(ExecRuntime **rt) {
+ assert(rt);
*rt = new0(ExecRuntime, 1);
if (!*rt)
return -ENOMEM;
- (*rt)->n_ref = 1;
(*rt)->netns_storage_socket[0] = (*rt)->netns_storage_socket[1] = -1;
-
return 0;
}
-int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id) {
+static int exec_runtime_add(
+ Manager *m,
+ const char *id,
+ const char *tmp_dir,
+ const char *var_tmp_dir,
+ const int netns_storage_socket[2],
+ ExecRuntime **ret) {
+
+ _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
int r;
- assert(rt);
- assert(c);
+ assert(m);
assert(id);
- if (*rt)
- return 1;
-
- if (!c->private_network && !c->private_tmp)
- return 0;
+ r = hashmap_ensure_allocated(&m->exec_runtime_by_id, &string_hash_ops);
+ if (r < 0)
+ return r;
- r = exec_runtime_allocate(rt);
+ r = exec_runtime_allocate(&rt);
if (r < 0)
return r;
- if (c->private_network && (*rt)->netns_storage_socket[0] < 0) {
- if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, (*rt)->netns_storage_socket) < 0)
- return -errno;
+ rt->id = strdup(id);
+ if (!rt->id)
+ return -ENOMEM;
+
+ if (tmp_dir) {
+ rt->tmp_dir = strdup(tmp_dir);
+ if (!rt->tmp_dir)
+ return -ENOMEM;
+
+ /* When tmp_dir is set, then we require var_tmp_dir is also set. */
+ assert(var_tmp_dir);
+ rt->var_tmp_dir = strdup(var_tmp_dir);
+ if (!rt->var_tmp_dir)
+ return -ENOMEM;
+ }
+
+ if (netns_storage_socket) {
+ rt->netns_storage_socket[0] = netns_storage_socket[0];
+ rt->netns_storage_socket[1] = netns_storage_socket[1];
}
- if (c->private_tmp && !(*rt)->tmp_dir) {
- r = setup_tmp_dirs(id, &(*rt)->tmp_dir, &(*rt)->var_tmp_dir);
+ r = hashmap_put(m->exec_runtime_by_id, rt->id, rt);
+ if (r < 0)
+ return r;
+
+ rt->manager = m;
+
+ if (ret)
+ *ret = rt;
+
+ /* do not remove created ExecRuntime object when the operation succeeds. */
+ rt = NULL;
+ return 0;
+}
+
+static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, ExecRuntime **ret) {
+ _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
+ _cleanup_close_pair_ int netns_storage_socket[2] = {-1, -1};
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(id);
+
+ /* It is not necessary to create ExecRuntime object. */
+ if (!c->private_network && !c->private_tmp)
+ return 0;
+
+ if (c->private_tmp) {
+ r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
if (r < 0)
return r;
}
+ if (c->private_network) {
+ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
+ return -errno;
+ }
+
+ r = exec_runtime_add(m, id, tmp_dir, var_tmp_dir, netns_storage_socket, ret);
+ if (r < 0)
+ return r;
+
+ /* Avoid cleanup */
+ netns_storage_socket[0] = -1;
+ netns_storage_socket[1] = -1;
return 1;
}
-ExecRuntime *exec_runtime_ref(ExecRuntime *r) {
- assert(r);
- assert(r->n_ref > 0);
+int exec_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecRuntime **ret) {
+ ExecRuntime *rt;
+ int r;
- r->n_ref++;
- return r;
-}
+ assert(m);
+ assert(id);
+ assert(ret);
+
+ rt = hashmap_get(m->exec_runtime_by_id, id);
+ if (rt)
+ /* We already have a ExecRuntime object, let's increase the ref count and reuse it */
+ goto ref;
+
+ if (!create)
+ return 0;
+
+ /* If not found, then create a new object. */
+ r = exec_runtime_make(m, c, id, &rt);
+ if (r <= 0)
+ /* When r == 0, it is not necessary to create ExecRuntime object. */
+ return r;
-ExecRuntime *exec_runtime_unref(ExecRuntime *r) {
+ref:
+ /* increment reference counter. */
+ rt->n_ref++;
+ *ret = rt;
+ return 1;
+}
- if (!r)
+ExecRuntime *exec_runtime_unref(ExecRuntime *rt, bool destroy) {
+ if (!rt)
return NULL;
- assert(r->n_ref > 0);
+ assert(rt->n_ref > 0);
- r->n_ref--;
- if (r->n_ref > 0)
+ rt->n_ref--;
+ if (rt->n_ref > 0)
return NULL;
- free(r->tmp_dir);
- free(r->var_tmp_dir);
- safe_close_pair(r->netns_storage_socket);
- return mfree(r);
+ return exec_runtime_free(rt, destroy);
}
-int exec_runtime_serialize(Unit *u, ExecRuntime *rt, FILE *f, FDSet *fds) {
- assert(u);
+int exec_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
+ ExecRuntime *rt;
+ Iterator i;
+
+ assert(m);
assert(f);
assert(fds);
- if (!rt)
- return 0;
+ HASHMAP_FOREACH(rt, m->exec_runtime_by_id, i) {
+ fprintf(f, "exec-runtime=%s", rt->id);
- if (rt->tmp_dir)
- unit_serialize_item(u, f, "tmp-dir", rt->tmp_dir);
+ if (rt->tmp_dir)
+ fprintf(f, " tmp-dir=%s", rt->tmp_dir);
- if (rt->var_tmp_dir)
- unit_serialize_item(u, f, "var-tmp-dir", rt->var_tmp_dir);
+ if (rt->var_tmp_dir)
+ fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
- if (rt->netns_storage_socket[0] >= 0) {
- int copy;
+ if (rt->netns_storage_socket[0] >= 0) {
+ int copy;
- copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
- if (copy < 0)
- return copy;
+ copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
+ if (copy < 0)
+ return copy;
- unit_serialize_item_format(u, f, "netns-socket-0", "%i", copy);
- }
+ fprintf(f, " netns-socket-0=%i", copy);
+ }
- if (rt->netns_storage_socket[1] >= 0) {
- int copy;
+ if (rt->netns_storage_socket[1] >= 0) {
+ int copy;
- copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
- if (copy < 0)
- return copy;
+ copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
+ if (copy < 0)
+ return copy;
- unit_serialize_item_format(u, f, "netns-socket-1", "%i", copy);
+ fprintf(f, " netns-socket-1=%i", copy);
+ }
+
+ fputc('\n', f);
}
return 0;
}
-int exec_runtime_deserialize_item(Unit *u, ExecRuntime **rt, const char *key, const char *value, FDSet *fds) {
+int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
+ _cleanup_(exec_runtime_freep) ExecRuntime *rt_create = NULL;
+ ExecRuntime *rt;
int r;
- assert(rt);
+ /* This is for the migration from old (v237 or earlier) deserialization text.
+ * Due to the bug #7790, this may not work with the units that use JoinsNamespaceOf=.
+ * Even if the ExecRuntime object originally created by the other unit, we cannot judge
+ * so or not from the serialized text, then we always creates a new object owned by this. */
+
+ assert(u);
assert(key);
assert(value);
- if (streq(key, "tmp-dir")) {
- char *copy;
+ /* Manager manages ExecRuntime objects by the unit id.
+ * So, we omit the serialized text when the unit does not have id (yet?)... */
+ if (isempty(u->id)) {
+ log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
+ return 0;
+ }
- r = exec_runtime_allocate(rt);
+ r = hashmap_ensure_allocated(&u->manager->exec_runtime_by_id, &string_hash_ops);
+ if (r < 0) {
+ log_unit_debug_errno(u, r, "Failed to allocate storage for runtime parameter: %m");
+ return 0;
+ }
+
+ rt = hashmap_get(u->manager->exec_runtime_by_id, u->id);
+ if (!rt) {
+ r = exec_runtime_allocate(&rt_create);
if (r < 0)
return log_oom();
+ rt_create->id = strdup(u->id);
+ if (!rt_create->id)
+ return log_oom();
+
+ rt = rt_create;
+ }
+
+ if (streq(key, "tmp-dir")) {
+ char *copy;
+
copy = strdup(value);
if (!copy)
return log_oom();
- free((*rt)->tmp_dir);
- (*rt)->tmp_dir = copy;
+ free_and_replace(rt->tmp_dir, copy);
} else if (streq(key, "var-tmp-dir")) {
char *copy;
- r = exec_runtime_allocate(rt);
- if (r < 0)
- return log_oom();
-
copy = strdup(value);
if (!copy)
return log_oom();
- free((*rt)->var_tmp_dir);
- (*rt)->var_tmp_dir = copy;
+ free_and_replace(rt->var_tmp_dir, copy);
} else if (streq(key, "netns-socket-0")) {
int fd;
- r = exec_runtime_allocate(rt);
- if (r < 0)
- return log_oom();
-
- if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
+ if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) {
log_unit_debug(u, "Failed to parse netns socket value: %s", value);
- else {
- safe_close((*rt)->netns_storage_socket[0]);
- (*rt)->netns_storage_socket[0] = fdset_remove(fds, fd);
+ return 0;
}
+
+ safe_close(rt->netns_storage_socket[0]);
+ rt->netns_storage_socket[0] = fdset_remove(fds, fd);
+
} else if (streq(key, "netns-socket-1")) {
int fd;
- r = exec_runtime_allocate(rt);
- if (r < 0)
- return log_oom();
-
- if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
+ if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) {
log_unit_debug(u, "Failed to parse netns socket value: %s", value);
- else {
- safe_close((*rt)->netns_storage_socket[1]);
- (*rt)->netns_storage_socket[1] = fdset_remove(fds, fd);
+ return 0;
}
+
+ safe_close(rt->netns_storage_socket[1]);
+ rt->netns_storage_socket[1] = fdset_remove(fds, fd);
} else
return 0;
- return 1;
-}
-static void *remove_tmpdir_thread(void *p) {
- _cleanup_free_ char *path = p;
+ /* If the object is newly created, then put it to the hashmap which manages ExecRuntime objects. */
+ if (rt_create) {
+ r = hashmap_put(u->manager->exec_runtime_by_id, rt_create->id, rt_create);
+ if (r < 0) {
+ log_unit_debug_errno(u, r, "Failed to put runtime paramter to manager's storage: %m");
+ return 0;
+ }
- (void) rm_rf(path, REMOVE_ROOT|REMOVE_PHYSICAL);
- return NULL;
-}
+ rt_create->manager = u->manager;
-void exec_runtime_destroy(ExecRuntime *rt) {
- int r;
+ /* Avoid cleanup */
+ rt_create = NULL;
+ }
- if (!rt)
- return;
+ return 1;
+}
- /* If there are multiple users of this, let's leave the stuff around */
- if (rt->n_ref > 1)
- return;
+void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
+ char *id = NULL, *tmp_dir = NULL, *var_tmp_dir = NULL;
+ int r, fd0 = -1, fd1 = -1;
+ const char *p, *v = value;
+ size_t n;
- if (rt->tmp_dir) {
- log_debug("Spawning thread to nuke %s", rt->tmp_dir);
+ assert(m);
+ assert(value);
+ assert(fds);
- r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
- if (r < 0) {
- log_warning_errno(r, "Failed to nuke %s: %m", rt->tmp_dir);
- free(rt->tmp_dir);
+ n = strcspn(v, " ");
+ id = strndupa(v, n);
+ if (v[n] != ' ')
+ goto finalize;
+ p = v + n + 1;
+
+ v = startswith(p, "tmp-dir=");
+ if (v) {
+ n = strcspn(v, " ");
+ tmp_dir = strndupa(v, n);
+ if (v[n] != ' ')
+ goto finalize;
+ p = v + n + 1;
+ }
+
+ v = startswith(p, "var-tmp-dir=");
+ if (v) {
+ n = strcspn(v, " ");
+ var_tmp_dir = strndupa(v, n);
+ if (v[n] != ' ')
+ goto finalize;
+ p = v + n + 1;
+ }
+
+ v = startswith(p, "netns-socket-0=");
+ if (v) {
+ char *buf;
+
+ n = strcspn(v, " ");
+ buf = strndupa(v, n);
+ if (safe_atoi(buf, &fd0) < 0 || !fdset_contains(fds, fd0)) {
+ log_debug("Unable to process exec-runtime netns fd specification.");
+ return;
}
-
- rt->tmp_dir = NULL;
+ fd0 = fdset_remove(fds, fd0);
+ if (v[n] != ' ')
+ goto finalize;
+ p = v + n + 1;
}
- if (rt->var_tmp_dir) {
- log_debug("Spawning thread to nuke %s", rt->var_tmp_dir);
+ v = startswith(p, "netns-socket-1=");
+ if (v) {
+ char *buf;
- r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
- if (r < 0) {
- log_warning_errno(r, "Failed to nuke %s: %m", rt->var_tmp_dir);
- free(rt->var_tmp_dir);
+ n = strcspn(v, " ");
+ buf = strndupa(v, n);
+ if (safe_atoi(buf, &fd1) < 0 || !fdset_contains(fds, fd1)) {
+ log_debug("Unable to process exec-runtime netns fd specification.");
+ return;
}
+ fd1 = fdset_remove(fds, fd1);
+ }
- rt->var_tmp_dir = NULL;
+finalize:
+
+ r = exec_runtime_add(m, id, tmp_dir, var_tmp_dir, (int[]) { fd0, fd1 }, NULL);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to add exec-runtime: %m");
+ return;
}
+}
- safe_close_pair(rt->netns_storage_socket);
+void exec_runtime_vacuum(Manager *m) {
+ ExecRuntime *rt;
+ Iterator i;
+
+ assert(m);
+
+ /* Free unreferenced ExecRuntime objects. This is used after manager deserialization process. */
+
+ HASHMAP_FOREACH(rt, m->exec_runtime_by_id, i) {
+ if (rt->n_ref > 0)
+ continue;
+
+ (void) exec_runtime_free(rt, false);
+ }
}
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {