DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
static const char* const service_state_table[_SERVICE_STATE_MAX] = {
- [SERVICE_DEAD] = "dead",
- [SERVICE_CONDITION] = "condition",
- [SERVICE_START_PRE] = "start-pre",
- [SERVICE_START] = "start",
- [SERVICE_START_POST] = "start-post",
- [SERVICE_RUNNING] = "running",
- [SERVICE_EXITED] = "exited",
- [SERVICE_RELOAD] = "reload",
- [SERVICE_RELOAD_SIGNAL] = "reload-signal",
- [SERVICE_RELOAD_NOTIFY] = "reload-notify",
- [SERVICE_STOP] = "stop",
- [SERVICE_STOP_WATCHDOG] = "stop-watchdog",
- [SERVICE_STOP_SIGTERM] = "stop-sigterm",
- [SERVICE_STOP_SIGKILL] = "stop-sigkill",
- [SERVICE_STOP_POST] = "stop-post",
- [SERVICE_FINAL_WATCHDOG] = "final-watchdog",
- [SERVICE_FINAL_SIGTERM] = "final-sigterm",
- [SERVICE_FINAL_SIGKILL] = "final-sigkill",
- [SERVICE_FAILED] = "failed",
- [SERVICE_AUTO_RESTART] = "auto-restart",
- [SERVICE_CLEANING] = "cleaning",
+ [SERVICE_DEAD] = "dead",
+ [SERVICE_CONDITION] = "condition",
+ [SERVICE_START_PRE] = "start-pre",
+ [SERVICE_START] = "start",
+ [SERVICE_START_POST] = "start-post",
+ [SERVICE_RUNNING] = "running",
+ [SERVICE_EXITED] = "exited",
+ [SERVICE_RELOAD] = "reload",
+ [SERVICE_RELOAD_SIGNAL] = "reload-signal",
+ [SERVICE_RELOAD_NOTIFY] = "reload-notify",
+ [SERVICE_STOP] = "stop",
+ [SERVICE_STOP_WATCHDOG] = "stop-watchdog",
+ [SERVICE_STOP_SIGTERM] = "stop-sigterm",
+ [SERVICE_STOP_SIGKILL] = "stop-sigkill",
+ [SERVICE_STOP_POST] = "stop-post",
+ [SERVICE_FINAL_WATCHDOG] = "final-watchdog",
+ [SERVICE_FINAL_SIGTERM] = "final-sigterm",
+ [SERVICE_FINAL_SIGKILL] = "final-sigkill",
+ [SERVICE_FAILED] = "failed",
+ [SERVICE_DEAD_BEFORE_AUTO_RESTART] = "dead-before-auto-restart",
+ [SERVICE_FAILED_BEFORE_AUTO_RESTART] = "failed-before-auto-restart",
+ [SERVICE_AUTO_RESTART] = "auto-restart",
+ [SERVICE_CLEANING] = "cleaning",
};
DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */
SERVICE_FINAL_SIGKILL,
SERVICE_FAILED,
+ SERVICE_DEAD_BEFORE_AUTO_RESTART,
+ SERVICE_FAILED_BEFORE_AUTO_RESTART,
SERVICE_AUTO_RESTART,
SERVICE_CLEANING,
_SERVICE_STATE_MAX,
return r;
}
-int dynamic_creds_acquire(DynamicCreds *creds, Manager *m, const char *user, const char *group) {
+int dynamic_creds_make(Manager *m, const char *user, const char *group, DynamicCreds **ret) {
+ _cleanup_(dynamic_creds_unrefp) DynamicCreds *creds = NULL;
bool acquired = false;
int r;
- assert(creds);
assert(m);
+ assert(ret);
+
+ if (!user && !group) {
+ *ret = NULL;
+ return 0;
+ }
+
+ creds = new0(DynamicCreds, 1);
+ if (!creds)
+ return -ENOMEM;
/* A DynamicUser object encapsulates an allocation of both a UID and a GID for a specific name. However, some
* services use different user and groups. For cases like that there's DynamicCreds containing a pair of user
* and group. This call allocates a pair. */
- if (!creds->user && user) {
+ if (user) {
r = dynamic_user_acquire(m, user, &creds->user);
if (r < 0)
return r;
acquired = true;
}
- if (!creds->group) {
-
- if (creds->user && (!group || streq_ptr(user, group)))
- creds->group = dynamic_user_ref(creds->user);
- else if (group) {
- r = dynamic_user_acquire(m, group, &creds->group);
- if (r < 0) {
- if (acquired)
- creds->user = dynamic_user_unref(creds->user);
- return r;
- }
+ if (creds->user && (!group || streq_ptr(user, group)))
+ creds->group = dynamic_user_ref(creds->user);
+ else if (group) {
+ r = dynamic_user_acquire(m, group, &creds->group);
+ if (r < 0) {
+ if (acquired)
+ creds->user = dynamic_user_unref(creds->user);
+ return r;
}
}
+ *ret = TAKE_PTR(creds);
+
return 0;
}
return 0;
}
-void dynamic_creds_unref(DynamicCreds *creds) {
- assert(creds);
+DynamicCreds* dynamic_creds_unref(DynamicCreds *creds) {
+ if (!creds)
+ return NULL;
creds->user = dynamic_user_unref(creds->user);
creds->group = dynamic_user_unref(creds->group);
+
+ return mfree(creds);
}
-void dynamic_creds_destroy(DynamicCreds *creds) {
- assert(creds);
+DynamicCreds* dynamic_creds_destroy(DynamicCreds *creds) {
+ if (!creds)
+ return NULL;
creds->user = dynamic_user_destroy(creds->user);
creds->group = dynamic_user_destroy(creds->group);
+
+ return mfree(creds);
}
int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret);
int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret);
-int dynamic_creds_acquire(DynamicCreds *creds, Manager *m, const char *user, const char *group);
+int dynamic_creds_make(Manager *m, const char *user, const char *group, DynamicCreds **ret);
int dynamic_creds_realize(DynamicCreds *creds, char **suggested_paths, uid_t *uid, gid_t *gid);
-void dynamic_creds_unref(DynamicCreds *creds);
-void dynamic_creds_destroy(DynamicCreds *creds);
+DynamicCreds *dynamic_creds_unref(DynamicCreds *creds);
+DynamicCreds *dynamic_creds_destroy(DynamicCreds *creds);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DynamicCreds*, dynamic_creds_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DynamicCreds*, dynamic_creds_destroy);
if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
return true;
- if (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir))
+ if (context->private_tmp && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
return true;
if (context->private_devices ||
* that is sticky, and that's the one we want to use here.
* This does not apply when we are using /run/systemd/empty as fallback. */
- if (context->private_tmp && runtime) {
- if (streq_ptr(runtime->tmp_dir, RUN_SYSTEMD_EMPTY))
- tmp_dir = runtime->tmp_dir;
- else if (runtime->tmp_dir)
- tmp_dir = strjoina(runtime->tmp_dir, "/tmp");
+ if (context->private_tmp && runtime && runtime->shared) {
+ if (streq_ptr(runtime->shared->tmp_dir, RUN_SYSTEMD_EMPTY))
+ tmp_dir = runtime->shared->tmp_dir;
+ else if (runtime->shared->tmp_dir)
+ tmp_dir = strjoina(runtime->shared->tmp_dir, "/tmp");
- if (streq_ptr(runtime->var_tmp_dir, RUN_SYSTEMD_EMPTY))
- var_tmp_dir = runtime->var_tmp_dir;
- else if (runtime->var_tmp_dir)
- var_tmp_dir = strjoina(runtime->var_tmp_dir, "/tmp");
+ if (streq_ptr(runtime->shared->var_tmp_dir, RUN_SYSTEMD_EMPTY))
+ var_tmp_dir = runtime->shared->var_tmp_dir;
+ else if (runtime->shared->var_tmp_dir)
+ var_tmp_dir = strjoina(runtime->shared->var_tmp_dir, "/tmp");
}
ns_info = (NamespaceInfo) {
static int close_remaining_fds(
const ExecParameters *params,
const ExecRuntime *runtime,
- const DynamicCreds *dcreds,
int user_lookup_fd,
int socket_fd,
const int *fds, size_t n_fds) {
n_dont_close += n_fds;
}
- if (runtime) {
- append_socket_pair(dont_close, &n_dont_close, runtime->netns_storage_socket);
- append_socket_pair(dont_close, &n_dont_close, runtime->ipcns_storage_socket);
+ if (runtime && runtime->shared) {
+ append_socket_pair(dont_close, &n_dont_close, runtime->shared->netns_storage_socket);
+ append_socket_pair(dont_close, &n_dont_close, runtime->shared->ipcns_storage_socket);
}
- if (dcreds) {
- if (dcreds->user)
- append_socket_pair(dont_close, &n_dont_close, dcreds->user->storage_socket);
- if (dcreds->group)
- append_socket_pair(dont_close, &n_dont_close, dcreds->group->storage_socket);
+ if (runtime && runtime->dynamic_creds) {
+ if (runtime->dynamic_creds->user)
+ append_socket_pair(dont_close, &n_dont_close, runtime->dynamic_creds->user->storage_socket);
+ if (runtime->dynamic_creds->group)
+ append_socket_pair(dont_close, &n_dont_close, runtime->dynamic_creds->group->storage_socket);
}
if (user_lookup_fd >= 0)
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime,
- DynamicCreds *dcreds,
const CGroupContext *cgroup_context,
int socket_fd,
const int named_iofds[static 3],
}
#endif
- r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, keep_fds, n_keep_fds);
+ r = close_remaining_fds(params, runtime, user_lookup_fd, socket_fd, keep_fds, n_keep_fds);
if (r < 0) {
*exit_status = EXIT_FDS;
return log_unit_error_errno(unit, r, "Failed to close unwanted file descriptors: %m");
return log_unit_error_errno(unit, errno, "Failed to update environment: %m");
}
- if (context->dynamic_user && dcreds) {
+ if (context->dynamic_user && runtime && runtime->dynamic_creds) {
_cleanup_strv_free_ char **suggested_paths = NULL;
/* On top of that, make sure we bypass our own NSS module nss-systemd comprehensively for any NSS
return log_oom();
}
- r = dynamic_creds_realize(dcreds, suggested_paths, &uid, &gid);
+ r = dynamic_creds_realize(runtime->dynamic_creds, suggested_paths, &uid, &gid);
if (r < 0) {
*exit_status = EXIT_USER;
if (r == -EILSEQ)
return log_unit_error_errno(unit, SYNTHETIC_ERRNO(ESRCH), "GID validation failed for \""GID_FMT"\"", gid);
}
- if (dcreds->user)
- username = dcreds->user->name;
+ if (runtime->dynamic_creds->user)
+ username = runtime->dynamic_creds->user->name;
} else {
r = get_fixed_user(context, &username, &uid, &gid, &home, &shell);
}
}
- if (context->network_namespace_path && runtime && runtime->netns_storage_socket[0] >= 0) {
- r = open_shareable_ns_path(runtime->netns_storage_socket, context->network_namespace_path, CLONE_NEWNET);
+ if (context->network_namespace_path && runtime && runtime->shared && runtime->shared->netns_storage_socket[0] >= 0) {
+ r = open_shareable_ns_path(runtime->shared->netns_storage_socket, context->network_namespace_path, CLONE_NEWNET);
if (r < 0) {
*exit_status = EXIT_NETWORK;
return log_unit_error_errno(unit, r, "Failed to open network namespace path %s: %m", context->network_namespace_path);
}
}
- if (context->ipc_namespace_path && runtime && runtime->ipcns_storage_socket[0] >= 0) {
- r = open_shareable_ns_path(runtime->ipcns_storage_socket, context->ipc_namespace_path, CLONE_NEWIPC);
+ if (context->ipc_namespace_path && runtime && runtime->shared && runtime->shared->ipcns_storage_socket[0] >= 0) {
+ r = open_shareable_ns_path(runtime->shared->ipcns_storage_socket, context->ipc_namespace_path, CLONE_NEWIPC);
if (r < 0) {
*exit_status = EXIT_NAMESPACE;
return log_unit_error_errno(unit, r, "Failed to open IPC namespace path %s: %m", context->ipc_namespace_path);
}
}
- if (exec_needs_network_namespace(context) && runtime && runtime->netns_storage_socket[0] >= 0) {
+ if (exec_needs_network_namespace(context) && runtime && runtime->shared && runtime->shared->netns_storage_socket[0] >= 0) {
if (ns_type_supported(NAMESPACE_NET)) {
- r = setup_shareable_ns(runtime->netns_storage_socket, CLONE_NEWNET);
+ r = setup_shareable_ns(runtime->shared->netns_storage_socket, CLONE_NEWNET);
if (r < 0) {
if (ERRNO_IS_PRIVILEGE(r))
log_unit_warning_errno(unit, r,
log_unit_warning(unit, "PrivateNetwork=yes is configured, but the kernel does not support network namespaces, ignoring.");
}
- if (exec_needs_ipc_namespace(context) && runtime && runtime->ipcns_storage_socket[0] >= 0) {
+ if (exec_needs_ipc_namespace(context) && runtime && runtime->shared && runtime->shared->ipcns_storage_socket[0] >= 0) {
if (ns_type_supported(NAMESPACE_IPC)) {
- r = setup_shareable_ns(runtime->ipcns_storage_socket, CLONE_NEWIPC);
+ r = setup_shareable_ns(runtime->shared->ipcns_storage_socket, CLONE_NEWIPC);
if (r == -EPERM)
log_unit_warning_errno(unit, r,
"PrivateIPC=yes is configured, but IPC namespace setup failed, ignoring: %m");
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime,
- DynamicCreds *dcreds,
const CGroupContext *cgroup_context,
pid_t *ret) {
context,
params,
runtime,
- dcreds,
cgroup_context,
socket_fd,
named_iofds,
return NULL;
}
-static ExecRuntime* exec_runtime_free(ExecRuntime *rt, bool destroy) {
+static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
+ if (!rt)
+ return NULL;
+
+ if (rt->manager)
+ (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
+
+ 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);
+ safe_close_pair(rt->ipcns_storage_socket);
+ return mfree(rt);
+}
+
+DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
+
+ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
int r;
if (!rt)
return NULL;
- if (rt->manager)
- (void) hashmap_remove(rt->manager->exec_runtime_by_id, rt->id);
+ assert(rt->n_ref > 0);
+ rt->n_ref--;
- /* When destroy is true, then rm_rf tmp_dir and var_tmp_dir. */
+ if (rt->n_ref > 0)
+ return NULL;
- if (destroy && rt->tmp_dir && !streq(rt->tmp_dir, RUN_SYSTEMD_EMPTY)) {
+ if (rt->tmp_dir && !streq(rt->tmp_dir, RUN_SYSTEMD_EMPTY)) {
log_debug("Spawning thread to nuke %s", rt->tmp_dir);
r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
rt->tmp_dir = NULL;
}
- if (destroy && rt->var_tmp_dir && !streq(rt->var_tmp_dir, RUN_SYSTEMD_EMPTY)) {
+ if (rt->var_tmp_dir && !streq(rt->var_tmp_dir, RUN_SYSTEMD_EMPTY)) {
log_debug("Spawning thread to nuke %s", rt->var_tmp_dir);
r = asynchronous_job(remove_tmpdir_thread, 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);
- safe_close_pair(rt->ipcns_storage_socket);
- return mfree(rt);
-}
-
-static void exec_runtime_freep(ExecRuntime **rt) {
- (void) exec_runtime_free(*rt, false);
+ return exec_shared_runtime_free(rt);
}
-static int exec_runtime_allocate(ExecRuntime **ret, const char *id) {
+static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
_cleanup_free_ char *id_copy = NULL;
- ExecRuntime *n;
+ ExecSharedRuntime *n;
assert(ret);
if (!id_copy)
return -ENOMEM;
- n = new(ExecRuntime, 1);
+ n = new(ExecSharedRuntime, 1);
if (!n)
return -ENOMEM;
- *n = (ExecRuntime) {
+ *n = (ExecSharedRuntime) {
.id = TAKE_PTR(id_copy),
.netns_storage_socket = PIPE_EBADF,
.ipcns_storage_socket = PIPE_EBADF,
return 0;
}
-static int exec_runtime_add(
+static int exec_shared_runtime_add(
Manager *m,
const char *id,
char **tmp_dir,
char **var_tmp_dir,
int netns_storage_socket[2],
int ipcns_storage_socket[2],
- ExecRuntime **ret) {
+ ExecSharedRuntime **ret) {
- _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
+ _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
int r;
assert(m);
/* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
- r = exec_runtime_allocate(&rt, id);
+ r = exec_shared_runtime_allocate(&rt, id);
if (r < 0)
return r;
- r = hashmap_ensure_put(&m->exec_runtime_by_id, &string_hash_ops, rt->id, rt);
+ r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
if (r < 0)
return r;
if (ret)
*ret = rt;
- /* do not remove created ExecRuntime object when the operation succeeds. */
+ /* do not remove created ExecSharedRuntime object when the operation succeeds. */
TAKE_PTR(rt);
return 0;
}
-static int exec_runtime_make(
+static int exec_shared_runtime_make(
Manager *m,
const ExecContext *c,
const char *id,
- ExecRuntime **ret) {
+ ExecSharedRuntime **ret) {
_cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
_cleanup_close_pair_ int netns_storage_socket[2] = PIPE_EBADF, ipcns_storage_socket[2] = PIPE_EBADF;
assert(c);
assert(id);
- /* It is not necessary to create ExecRuntime object. */
+ /* It is not necessary to create ExecSharedRuntime object. */
if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && !c->private_tmp) {
*ret = NULL;
return 0;
return -errno;
}
- r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
+ r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
if (r < 0)
return r;
return 1;
}
-int exec_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecRuntime **ret) {
- ExecRuntime *rt;
+int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
+ ExecSharedRuntime *rt;
int r;
assert(m);
assert(id);
assert(ret);
- rt = hashmap_get(m->exec_runtime_by_id, id);
+ rt = hashmap_get(m->exec_shared_runtime_by_id, id);
if (rt)
- /* We already have an ExecRuntime object, let's increase the ref count and reuse it */
+ /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
goto ref;
if (!create) {
}
/* If not found, then create a new object. */
- r = exec_runtime_make(m, c, id, &rt);
+ r = exec_shared_runtime_make(m, c, id, &rt);
if (r < 0)
return r;
if (r == 0) {
- /* When r == 0, it is not necessary to create ExecRuntime object. */
+ /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
*ret = NULL;
return 0;
}
return 1;
}
-ExecRuntime *exec_runtime_unref(ExecRuntime *rt, bool destroy) {
- if (!rt)
- return NULL;
-
- assert(rt->n_ref > 0);
-
- rt->n_ref--;
- if (rt->n_ref > 0)
- return NULL;
-
- return exec_runtime_free(rt, destroy);
-}
-
-int exec_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
- ExecRuntime *rt;
+int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
+ ExecSharedRuntime *rt;
assert(m);
assert(f);
assert(fds);
- HASHMAP_FOREACH(rt, m->exec_runtime_by_id) {
+ HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
fprintf(f, "exec-runtime=%s", rt->id);
if (rt->tmp_dir)
return 0;
}
-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 exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
+ _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
+ ExecSharedRuntime *rt;
int r;
/* 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
+ * Even if the ExecSharedRuntime 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);
- /* Manager manages ExecRuntime objects by the unit id.
+ /* Manager manages ExecSharedRuntime 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;
}
- if (hashmap_ensure_allocated(&u->manager->exec_runtime_by_id, &string_hash_ops) < 0)
+ if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
return log_oom();
- rt = hashmap_get(u->manager->exec_runtime_by_id, u->id);
+ rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
if (!rt) {
- if (exec_runtime_allocate(&rt_create, u->id) < 0)
+ if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
return log_oom();
rt = rt_create;
} else
return 0;
- /* If the object is newly created, then put it to the hashmap which manages ExecRuntime objects. */
+ /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
if (rt_create) {
- r = hashmap_put(u->manager->exec_runtime_by_id, rt_create->id, rt_create);
+ r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
if (r < 0) {
log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
return 0;
return 1;
}
-int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
+int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
_cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
char *id = NULL;
int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
}
finalize:
- r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
+ r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
if (r < 0)
return log_debug_errno(r, "Failed to add exec-runtime: %m");
return 0;
}
-void exec_runtime_vacuum(Manager *m) {
- ExecRuntime *rt;
+void exec_shared_runtime_vacuum(Manager *m) {
+ ExecSharedRuntime *rt;
assert(m);
- /* Free unreferenced ExecRuntime objects. This is used after manager deserialization process. */
+ /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
- HASHMAP_FOREACH(rt, m->exec_runtime_by_id) {
+ HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
if (rt->n_ref > 0)
continue;
- (void) exec_runtime_free(rt, false);
+ (void) exec_shared_runtime_free(rt);
}
}
+int exec_runtime_make(ExecSharedRuntime *shared, DynamicCreds *creds, ExecRuntime **ret) {
+ _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
+
+ assert(ret);
+
+ if (!shared && !creds) {
+ *ret = NULL;
+ return 0;
+ }
+
+ rt = new(ExecRuntime, 1);
+ if (!rt)
+ return -ENOMEM;
+
+ *rt = (ExecRuntime) {
+ .shared = shared,
+ .dynamic_creds = creds,
+ };
+
+ *ret = TAKE_PTR(rt);
+ return 1;
+}
+
+ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
+ if (!rt)
+ return NULL;
+
+ exec_shared_runtime_unref(rt->shared);
+ dynamic_creds_unref(rt->dynamic_creds);
+ return mfree(rt);
+}
+
+ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
+ if (!rt)
+ return NULL;
+
+ rt->shared = exec_shared_runtime_destroy(rt->shared);
+ rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
+ return exec_runtime_free(rt);
+}
+
void exec_params_clear(ExecParameters *p) {
if (!p)
return;
typedef struct ExecStatus ExecStatus;
typedef struct ExecCommand ExecCommand;
typedef struct ExecContext ExecContext;
+typedef struct ExecSharedRuntime ExecSharedRuntime;
+typedef struct DynamicCreds DynamicCreds;
typedef struct ExecRuntime ExecRuntime;
typedef struct ExecParameters ExecParameters;
typedef struct Manager Manager;
* invocations of commands. Specifically, this allows sharing of /tmp and /var/tmp data as well as network namespaces
* between invocations of commands. This is a reference counted object, with one reference taken by each currently
* active command invocation that wants to share this runtime. */
-struct ExecRuntime {
+struct ExecSharedRuntime {
unsigned n_ref;
Manager *manager;
int ipcns_storage_socket[2];
};
+struct ExecRuntime {
+ ExecSharedRuntime *shared;
+ DynamicCreds *dynamic_creds;
+};
+
typedef enum ExecDirectoryType {
EXEC_DIRECTORY_RUNTIME = 0,
EXEC_DIRECTORY_STATE,
const ExecContext *context,
const ExecParameters *exec_params,
ExecRuntime *runtime,
- DynamicCreds *dynamic_creds,
const CGroupContext *cgroup_context,
pid_t *ret);
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix);
void exec_status_reset(ExecStatus *s);
-int exec_runtime_acquire(Manager *m, const ExecContext *c, const char *name, bool create, ExecRuntime **ret);
-ExecRuntime *exec_runtime_unref(ExecRuntime *r, bool destroy);
+int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *name, bool create, ExecSharedRuntime **ret);
+ExecSharedRuntime *exec_shared_runtime_destroy(ExecSharedRuntime *r);
+ExecSharedRuntime *exec_shared_runtime_unref(ExecSharedRuntime *r);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_unref);
+
+int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds);
+int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds);
+int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds);
+void exec_shared_runtime_vacuum(Manager *m);
-int exec_runtime_serialize(const Manager *m, FILE *f, FDSet *fds);
-int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds);
-int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds);
-void exec_runtime_vacuum(Manager *m);
+int exec_runtime_make(ExecSharedRuntime *shared, DynamicCreds *creds, ExecRuntime **ret);
+ExecRuntime* exec_runtime_free(ExecRuntime *rt);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecRuntime*, exec_runtime_free);
+ExecRuntime* exec_runtime_destroy(ExecRuntime *rt);
void exec_params_clear(ExecParameters *p);
manager_serialize_uid_refs(m, f);
manager_serialize_gid_refs(m, f);
- r = exec_runtime_serialize(m, f, fds);
+ r = exec_shared_runtime_serialize(m, f, fds);
if (r < 0)
return r;
else if ((val = startswith(l, "destroy-ipc-gid=")))
manager_deserialize_gid_refs_one(m, val);
else if ((val = startswith(l, "exec-runtime=")))
- (void) exec_runtime_deserialize_one(m, val, fds);
+ (void) exec_shared_runtime_deserialize_one(m, val, fds);
else if ((val = startswith(l, "subscribed="))) {
if (strv_extend(&m->deserialized_subscribed, val) < 0)
bus_done(m);
manager_varlink_done(m);
- exec_runtime_vacuum(m);
- hashmap_free(m->exec_runtime_by_id);
+ exec_shared_runtime_vacuum(m);
+ hashmap_free(m->exec_shared_runtime_by_id);
dynamic_user_vacuum(m, false);
hashmap_free(m->dynamic_users);
manager_clear_jobs_and_units(m);
lookup_paths_flush_generator(&m->lookup_paths);
lookup_paths_free(&m->lookup_paths);
- exec_runtime_vacuum(m);
+ exec_shared_runtime_vacuum(m);
dynamic_user_vacuum(m, false);
m->uid_refs = hashmap_free(m->uid_refs);
m->gid_refs = hashmap_free(m->gid_refs);
manager_vacuum_gid_refs(m);
/* Release any runtimes no longer referenced */
- exec_runtime_vacuum(m);
+ exec_shared_runtime_vacuum(m);
}
int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Hashmap *uid_refs;
Hashmap *gid_refs;
- /* ExecRuntime, indexed by their owner unit id */
- Hashmap *exec_runtime_by_id;
+ /* ExecSharedRuntime, indexed by their owner unit id */
+ Hashmap *exec_shared_runtime_by_id;
/* When the user hits C-A-D more than 7 times per 2s, do something immediately... */
RateLimit ctrl_alt_del_ratelimit;
mount_parameters_done(&m->parameters_proc_self_mountinfo);
mount_parameters_done(&m->parameters_fragment);
- m->exec_runtime = exec_runtime_unref(m->exec_runtime, false);
+ m->exec_runtime = exec_runtime_free(m->exec_runtime);
exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
m->control_command = NULL;
- dynamic_creds_unref(&m->dynamic_creds);
-
mount_unwatch_control_pid(m);
m->timer_event_source = sd_event_source_disable_unref(m->timer_event_source);
return r;
}
- if (!IN_SET(m->deserialized_state, MOUNT_DEAD, MOUNT_FAILED)) {
- (void) unit_setup_dynamic_creds(u);
+ if (!IN_SET(m->deserialized_state, MOUNT_DEAD, MOUNT_FAILED))
(void) unit_setup_exec_runtime(u);
- }
mount_set_state(m, m->deserialized_state);
return 0;
&m->exec_context,
&exec_params,
m->exec_runtime,
- &m->dynamic_creds,
&m->cgroup_context,
&pid);
if (r < 0)
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
- m->exec_runtime = exec_runtime_unref(m->exec_runtime, true);
+ m->exec_runtime = exec_runtime_destroy(m->exec_runtime);
unit_destroy_runtime_data(UNIT(m), &m->exec_context);
unit_unref_uid_gid(UNIT(m), true);
- dynamic_creds_destroy(&m->dynamic_creds);
-
/* Any dependencies based on /proc/self/mountinfo are now stale. Let's re-generate dependencies from
* .mount unit. */
(void) mount_add_non_exec_dependencies(m);
.cgroup_context_offset = offsetof(Mount, cgroup_context),
.kill_context_offset = offsetof(Mount, kill_context),
.exec_runtime_offset = offsetof(Mount, exec_runtime),
- .dynamic_creds_offset = offsetof(Mount, dynamic_creds),
.sections =
"Unit\0"
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
- DynamicCreds dynamic_creds;
MountState state, deserialized_state;
[SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
[SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
[SERVICE_FAILED] = UNIT_FAILED,
+ [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
+ [SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
[SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
[SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
[SERVICE_FAILED] = UNIT_FAILED,
+ [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
+ [SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
usec_t service_restart_usec(Service *s) {
unsigned n_restarts;
long double unit;
+ usec_t value;
assert(s);
* between job enqueuing and running is usually neglectable compared to the time
* we'll be sleeping. */
n_restarts = s->n_restarts +
- (IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART) ? 1 : 0);
+ (IN_SET(s->state, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART) ? 1 : 0);
/* n_restarts can equal to 0 if no restart has happened nor planned */
if (n_restarts <= 1 ||
s->restart_steps == 0 ||
s->restart_usec_max == USEC_INFINITY ||
- s->restart_usec == s->restart_usec_max)
- return s->restart_usec;
-
- if (n_restarts > s->restart_steps)
- return s->restart_usec_max;
-
- /* Enforced in service_verify() and above */
- assert(s->restart_usec_max > s->restart_usec);
+ s->restart_usec >= s->restart_usec_max)
+ value = s->restart_usec;
+ else if (n_restarts > s->restart_steps)
+ value = s->restart_usec_max;
+ else {
+ /* Enforced in service_verify() and above */
+ assert(s->restart_usec_max > s->restart_usec);
- unit = powl(s->restart_usec_max - s->restart_usec, 1.0L / s->restart_steps);
+ unit = powl(s->restart_usec_max - s->restart_usec, 1.0L / s->restart_steps);
+ value = usec_add(s->restart_usec, (usec_t) powl(unit, n_restarts - 1));
+ }
- return usec_add(s->restart_usec, (usec_t) powl(unit, n_restarts - 1));
+ log_unit_debug(UNIT(s), "Restart interval calculated as: %s", FORMAT_TIMESPAN(value, 0));
+ return value;
}
static void service_extend_event_source_timeout(Service *s, sd_event_source *source, usec_t extended) {
static void service_release_fd_store(Service *s) {
assert(s);
- if (s->n_keep_fd_store > 0)
- return;
-
log_unit_debug(UNIT(s), "Releasing all stored fds");
while (s->fd_store)
service_fd_store_unlink(s->fd_store);
assert(s);
+ /* Don't release resources if this is a transitionary failed/dead state */
+ if (IN_SET(s->state, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART))
+ return;
+
if (!s->fd_store && s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0)
return;
s->pid_file = mfree(s->pid_file);
s->status_text = mfree(s->status_text);
- s->exec_runtime = exec_runtime_unref(s->exec_runtime, false);
+ s->exec_runtime = exec_runtime_free(s->exec_runtime);
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
s->control_command = NULL;
s->main_command = NULL;
- dynamic_creds_unref(&s->dynamic_creds);
-
exit_status_set_free(&s->restart_prevent_status);
exit_status_set_free(&s->restart_force_status);
exit_status_set_free(&s->success_status);
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
}
- if (IN_SET(state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) {
+ if (IN_SET(state,
+ SERVICE_DEAD, SERVICE_FAILED,
+ SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART)) {
unit_unwatch_all_pids(UNIT(s));
unit_dequeue_rewatch_pids(UNIT(s));
}
return r;
}
- if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART, SERVICE_CLEANING)) {
+ if (!IN_SET(s->deserialized_state,
+ SERVICE_DEAD, SERVICE_FAILED,
+ SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART,
+ SERVICE_CLEANING)) {
(void) unit_enqueue_rewatch_pids(u);
- (void) unit_setup_dynamic_creds(u);
(void) unit_setup_exec_runtime(u);
}
&s->exec_context,
&exec_params,
s->exec_runtime,
- &s->dynamic_creds,
&s->cgroup_context,
&pid);
if (r < 0)
if (s->will_auto_restart)
return true;
- if (s->state == SERVICE_AUTO_RESTART)
+ if (IN_SET(s->state, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART))
return true;
return unit_will_restart_default(u);
}
static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) {
- ServiceState end_state;
+ ServiceState end_state, restart_state;
int r;
assert(s);
if (s->result == SERVICE_SUCCESS) {
unit_log_success(UNIT(s));
end_state = SERVICE_DEAD;
+ restart_state = SERVICE_DEAD_BEFORE_AUTO_RESTART;
} else if (s->result == SERVICE_SKIP_CONDITION) {
unit_log_skip(UNIT(s), service_result_to_string(s->result));
end_state = SERVICE_DEAD;
+ restart_state = SERVICE_DEAD_BEFORE_AUTO_RESTART;
} else {
unit_log_failure(UNIT(s), service_result_to_string(s->result));
end_state = SERVICE_FAILED;
+ restart_state = SERVICE_FAILED_BEFORE_AUTO_RESTART;
}
unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop);
s->will_auto_restart = true;
}
- /* Make sure service_release_resources() doesn't destroy our FD store, while we are changing through
- * SERVICE_FAILED/SERVICE_DEAD before entering into SERVICE_AUTO_RESTART. */
- s->n_keep_fd_store ++;
-
- service_set_state(s, end_state);
-
if (s->will_auto_restart) {
s->will_auto_restart = false;
+ /* We make two state changes here: one that maps to the high-level UNIT_INACTIVE/UNIT_FAILED
+ * state (i.e. a state indicating deactivation), and then one that that maps to the
+ * high-level UNIT_STARTING state (i.e. a state indicating activation). We do this so that
+ * external software can watch the state changes and see all service failures, even if they
+ * are only transitionary and followed by an automatic restart. We have fine-grained
+ * low-level states for this though so that software can distinguish the permanent UNIT_INACTIVE
+ * state from this transitionary UNIT_INACTIVE state by looking at the low-level states. */
+ service_set_state(s, restart_state);
+
r = service_arm_timer(s, /* relative= */ true, service_restart_usec(s));
- if (r < 0) {
- s->n_keep_fd_store--;
+ if (r < 0)
goto fail;
- }
service_set_state(s, SERVICE_AUTO_RESTART);
- } else
+ } else {
+ service_set_state(s, end_state);
+
/* If we shan't restart, then flush out the restart counter. But don't do that immediately, so that the
* user can still introspect the counter. Do so on the next start. */
s->flush_n_restarts = true;
+ }
/* The new state is in effect, let's decrease the fd store ref counter again. Let's also re-add us to the GC
* queue, so that the fd store is possibly gc'ed again */
- s->n_keep_fd_store--;
unit_add_to_gc_queue(UNIT(s));
/* The next restart might not be a manual stop, hence reset the flag indicating manual stops */
s->notify_access_override = _NOTIFY_ACCESS_INVALID;
/* We want fresh tmpdirs in case service is started again immediately */
- s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
+ s->exec_runtime = exec_runtime_destroy(s->exec_runtime);
/* Also, remove the runtime directory */
unit_destroy_runtime_data(UNIT(s), &s->exec_context);
/* Get rid of the IPC bits of the user */
unit_unref_uid_gid(UNIT(s), true);
- /* Release the user, and destroy it if we are the only remaining owner */
- dynamic_creds_destroy(&s->dynamic_creds);
-
/* Try to delete the pid file. At this point it will be
* out-of-date, and some software might be confused by it, so
* let's remove it. */
if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST))
return 0;
- /* A service that will be restarted must be stopped first to
- * trigger BindsTo and/or OnFailure dependencies. If a user
- * does not want to wait for the holdoff time to elapse, the
- * service should be manually restarted, not started. We
- * simply return EAGAIN here, so that any start jobs stay
- * queued, and assume that the auto restart timer will
- * eventually trigger the restart. */
- if (s->state == SERVICE_AUTO_RESTART)
+ /* A service that will be restarted must be stopped first to trigger BindsTo and/or OnFailure
+ * dependencies. If a user does not want to wait for the holdoff time to elapse, the service should
+ * be manually restarted, not started. We simply return EAGAIN here, so that any start jobs stay
+ * queued, and assume that the auto restart timer will eventually trigger the restart. */
+ if (IN_SET(s->state, SERVICE_AUTO_RESTART, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART))
return -EAGAIN;
assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED));
/* Don't create restart jobs from manual stops. */
s->forbid_restart = true;
- /* Already on it */
- if (IN_SET(s->state,
- SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
+ switch (s->state) {
+
+ case SERVICE_STOP:
+ case SERVICE_STOP_SIGTERM:
+ case SERVICE_STOP_SIGKILL:
+ case SERVICE_STOP_POST:
+ case SERVICE_FINAL_WATCHDOG:
+ case SERVICE_FINAL_SIGTERM:
+ case SERVICE_FINAL_SIGKILL:
+ /* Already on it */
return 0;
- /* A restart will be scheduled or is in progress. */
- if (s->state == SERVICE_AUTO_RESTART) {
+ case SERVICE_AUTO_RESTART:
+ /* A restart will be scheduled or is in progress. */
service_set_state(s, SERVICE_DEAD);
return 0;
- }
- /* If there's already something running we go directly into kill mode. */
- if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_STOP_WATCHDOG)) {
+ case SERVICE_CONDITION:
+ case SERVICE_START_PRE:
+ case SERVICE_START:
+ case SERVICE_START_POST:
+ case SERVICE_RELOAD:
+ case SERVICE_RELOAD_SIGNAL:
+ case SERVICE_RELOAD_NOTIFY:
+ case SERVICE_STOP_WATCHDOG:
+ /* If there's already something running we go directly into kill mode. */
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
return 0;
- }
- /* If we are currently cleaning, then abort it, brutally. */
- if (s->state == SERVICE_CLEANING) {
+ case SERVICE_CLEANING:
+ /* If we are currently cleaning, then abort it, brutally. */
service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS);
return 0;
+
+ case SERVICE_RUNNING:
+ case SERVICE_EXITED:
+ service_enter_stop(s, SERVICE_SUCCESS);
+ return 1;
+
+ case SERVICE_DEAD_BEFORE_AUTO_RESTART:
+ case SERVICE_FAILED_BEFORE_AUTO_RESTART:
+ case SERVICE_DEAD:
+ case SERVICE_FAILED:
+ default:
+ /* Unknown state, or unit_stop() should already have handled these */
+ assert_not_reached();
}
- assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED));
- service_enter_stop(s, SERVICE_SUCCESS);
- return 1;
}
static int service_reload(Unit *u) {
control_pid_good(s) > 0)
return false;
+ /* Only allow collection of actually dead services, i.e. not those that are in the transitionary
+ * SERVICE_DEAD_BEFORE_AUTO_RESTART/SERVICE_FAILED_BEFORE_AUTO_RESTART states. */
+ if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED))
+ return false;
+
return true;
}
switch (s->state) {
- /* Waiting for SIGCHLD is usually more interesting,
- * because it includes return codes/signals. Which is
- * why we ignore the cgroup events for most cases,
- * except when we don't know pid which to expect the
- * SIGCHLD for. */
+ /* Waiting for SIGCHLD is usually more interesting, because it includes return
+ * codes/signals. Which is why we ignore the cgroup events for most cases, except when we
+ * don't know pid which to expect the SIGCHLD for. */
case SERVICE_START:
if (IN_SET(s->type, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD) &&
* up the cgroup earlier and should do it now. */
case SERVICE_DEAD:
case SERVICE_FAILED:
+ case SERVICE_DEAD_BEFORE_AUTO_RESTART:
+ case SERVICE_FAILED_BEFORE_AUTO_RESTART:
+ case SERVICE_AUTO_RESTART:
unit_prune_cgroup(u);
break;
.cgroup_context_offset = offsetof(Service, cgroup_context),
.kill_context_offset = offsetof(Service, kill_context),
.exec_runtime_offset = offsetof(Service, exec_runtime),
- .dynamic_creds_offset = offsetof(Service, dynamic_creds),
.sections =
"Unit\0"
/* Runtime data of the execution context */
ExecRuntime *exec_runtime;
- DynamicCreds dynamic_creds;
pid_t main_pid, control_pid;
ServiceFDStore *fd_store;
size_t n_fd_store;
unsigned n_fd_store_max;
- unsigned n_keep_fd_store;
char *usb_function_descriptors;
char *usb_function_strings;
s->peers_by_address = set_free(s->peers_by_address);
- s->exec_runtime = exec_runtime_unref(s->exec_runtime, false);
+ s->exec_runtime = exec_runtime_free(s->exec_runtime);
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
s->control_command = NULL;
- dynamic_creds_unref(&s->dynamic_creds);
-
socket_unwatch_control_pid(s);
unit_ref_unset(&s->service);
if (s->exec_context.network_namespace_path &&
s->exec_runtime &&
- s->exec_runtime->netns_storage_socket[0] >= 0) {
- r = open_shareable_ns_path(s->exec_runtime->netns_storage_socket, s->exec_context.network_namespace_path, CLONE_NEWNET);
+ s->exec_runtime->shared &&
+ s->exec_runtime->shared->netns_storage_socket[0] >= 0) {
+ r = open_shareable_ns_path(s->exec_runtime->shared->netns_storage_socket, s->exec_context.network_namespace_path, CLONE_NEWNET);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to open network namespace path %s: %m", s->exec_context.network_namespace_path);
}
if (s->exec_context.ipc_namespace_path &&
s->exec_runtime &&
- s->exec_runtime->ipcns_storage_socket[0] >= 0) {
- r = open_shareable_ns_path(s->exec_runtime->ipcns_storage_socket, s->exec_context.ipc_namespace_path, CLONE_NEWIPC);
+ s->exec_runtime->shared &&
+ s->exec_runtime->shared->ipcns_storage_socket[0] >= 0) {
+ r = open_shareable_ns_path(s->exec_runtime->shared->ipcns_storage_socket, s->exec_context.ipc_namespace_path, CLONE_NEWIPC);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to open IPC namespace path %s: %m", s->exec_context.ipc_namespace_path);
}
if (exec_needs_network_namespace(&s->exec_context) &&
s->exec_runtime &&
- s->exec_runtime->netns_storage_socket[0] >= 0) {
+ s->exec_runtime->shared &&
+ s->exec_runtime->shared->netns_storage_socket[0] >= 0) {
if (ns_type_supported(NAMESPACE_NET)) {
- r = setup_shareable_ns(s->exec_runtime->netns_storage_socket, CLONE_NEWNET);
+ r = setup_shareable_ns(s->exec_runtime->shared->netns_storage_socket, CLONE_NEWNET);
if (r < 0) {
log_unit_error_errno(UNIT(s), r, "Failed to join network namespace: %m");
_exit(EXIT_NETWORK);
return r;
}
- if (!IN_SET(s->deserialized_state, SOCKET_DEAD, SOCKET_FAILED, SOCKET_CLEANING)) {
- (void) unit_setup_dynamic_creds(u);
+ if (!IN_SET(s->deserialized_state, SOCKET_DEAD, SOCKET_FAILED, SOCKET_CLEANING))
(void) unit_setup_exec_runtime(u);
- }
socket_set_state(s, s->deserialized_state);
return 0;
&s->exec_context,
&exec_params,
s->exec_runtime,
- &s->dynamic_creds,
&s->cgroup_context,
&pid);
if (r < 0)
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
- s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
+ s->exec_runtime = exec_runtime_destroy(s->exec_runtime);
unit_destroy_runtime_data(UNIT(s), &s->exec_context);
unit_unref_uid_gid(UNIT(s), true);
-
- dynamic_creds_destroy(&s->dynamic_creds);
}
static void socket_enter_signal(Socket *s, SocketState state, SocketResult f);
/* If the service is already active we cannot start the
* socket */
- if (!IN_SET(service->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART))
+ if (!IN_SET(service->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART))
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY), "Socket service %s already active, refusing.", UNIT(service)->id);
}
return;
if (IN_SET(SERVICE(other)->state,
- SERVICE_DEAD, SERVICE_FAILED,
+ SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
SERVICE_AUTO_RESTART))
socket_enter_listening(s);
.cgroup_context_offset = offsetof(Socket, cgroup_context),
.kill_context_offset = offsetof(Socket, kill_context),
.exec_runtime_offset = offsetof(Socket, exec_runtime),
- .dynamic_creds_offset = offsetof(Socket, dynamic_creds),
.sections =
"Unit\0"
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
- DynamicCreds dynamic_creds;
/* For Accept=no sockets refers to the one service we'll
* activate. For Accept=yes sockets is either NULL, or filled
s->parameters_fragment.what = mfree(s->parameters_fragment.what);
s->parameters_fragment.options = mfree(s->parameters_fragment.options);
- s->exec_runtime = exec_runtime_unref(s->exec_runtime, false);
+ s->exec_runtime = exec_runtime_free(s->exec_runtime);
exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
s->control_command = NULL;
- dynamic_creds_unref(&s->dynamic_creds);
-
swap_unwatch_control_pid(s);
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
return r;
}
- if (!IN_SET(new_state, SWAP_DEAD, SWAP_FAILED)) {
- (void) unit_setup_dynamic_creds(u);
+ if (!IN_SET(new_state, SWAP_DEAD, SWAP_FAILED))
(void) unit_setup_exec_runtime(u);
- }
swap_set_state(s, new_state);
return 0;
&s->exec_context,
&exec_params,
s->exec_runtime,
- &s->dynamic_creds,
&s->cgroup_context,
&pid);
if (r < 0)
unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop);
swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
- s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
+ s->exec_runtime = exec_runtime_destroy(s->exec_runtime);
unit_destroy_runtime_data(UNIT(s), &s->exec_context);
unit_unref_uid_gid(UNIT(s), true);
-
- dynamic_creds_destroy(&s->dynamic_creds);
}
static void swap_enter_active(Swap *s, SwapResult f) {
.cgroup_context_offset = offsetof(Swap, cgroup_context),
.kill_context_offset = offsetof(Swap, kill_context),
.exec_runtime_offset = offsetof(Swap, exec_runtime),
- .dynamic_creds_offset = offsetof(Swap, dynamic_creds),
.sections =
"Unit\0"
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
- DynamicCreds dynamic_creds;
SwapState state, deserialized_state;
continue;
}
- r = exec_runtime_deserialize_compat(u, l, v, fds);
+ r = exec_shared_runtime_deserialize_compat(u, l, v, fds);
if (r < 0) {
log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l);
continue;
}
int unit_setup_exec_runtime(Unit *u) {
+ _cleanup_(exec_shared_runtime_unrefp) ExecSharedRuntime *esr = NULL;
+ _cleanup_(dynamic_creds_unrefp) DynamicCreds *dcreds = NULL;
ExecRuntime **rt;
+ ExecContext *ec;
size_t offset;
Unit *other;
int r;
if (*rt)
return 0;
+ ec = unit_get_exec_context(u);
+ assert(ec);
+
/* Try to get it from somebody else */
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_JOINS_NAMESPACE_OF) {
- r = exec_runtime_acquire(u->manager, NULL, other->id, false, rt);
- if (r == 1)
- return 1;
+ r = exec_shared_runtime_acquire(u->manager, NULL, other->id, false, &esr);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ break;
}
- return exec_runtime_acquire(u->manager, unit_get_exec_context(u), u->id, true, rt);
-}
-
-int unit_setup_dynamic_creds(Unit *u) {
- ExecContext *ec;
- DynamicCreds *dcreds;
- size_t offset;
-
- assert(u);
+ if (!esr) {
+ r = exec_shared_runtime_acquire(u->manager, ec, u->id, true, &esr);
+ if (r < 0)
+ return r;
+ }
- offset = UNIT_VTABLE(u)->dynamic_creds_offset;
- assert(offset > 0);
- dcreds = (DynamicCreds*) ((uint8_t*) u + offset);
+ if (ec->dynamic_user) {
+ r = dynamic_creds_make(u->manager, ec->user, ec->group, &dcreds);
+ if (r < 0)
+ return r;
+ }
- ec = unit_get_exec_context(u);
- assert(ec);
+ r = exec_runtime_make(esr, dcreds, rt);
+ if (r < 0)
+ return r;
- if (!ec->dynamic_user)
- return 0;
+ TAKE_PTR(esr);
+ TAKE_PTR(dcreds);
- return dynamic_creds_acquire(dcreds, u->manager, ec->user, ec->group);
+ return r;
}
bool unit_type_supported(UnitType t) {
if (r < 0)
return r;
- r = unit_setup_dynamic_creds(u);
- if (r < 0)
- return r;
-
return 0;
}
size_t kill_context_offset;
/* If greater than 0, the offset into the object where the
- * pointer to ExecRuntime is found, if the unit type has
+ * pointer to ExecSharedRuntime is found, if the unit type has
* that */
size_t exec_runtime_offset;
- /* If greater than 0, the offset into the object where the pointer to DynamicCreds is found, if the unit type
- * has that. */
- size_t dynamic_creds_offset;
-
/* The name of the configuration file section with the private settings of this unit */
const char *private_section;
ExecRuntime *unit_get_exec_runtime(Unit *u) _pure_;
int unit_setup_exec_runtime(Unit *u);
-int unit_setup_dynamic_creds(Unit *u);
char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf);
char* unit_concat_strv(char **l, UnitWriteFlags flags);