summaryrefslogtreecommitdiffstats
path: root/src/core/service.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/service.c')
-rw-r--r--src/core/service.c787
1 files changed, 421 insertions, 366 deletions
diff --git a/src/core/service.c b/src/core/service.c
index ffe92d2..8ec27c4 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -24,6 +24,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
+#include "io-util.h"
#include "load-dropin.h"
#include "load-fragment.h"
#include "log.h"
@@ -34,6 +35,7 @@
#include "path-util.h"
#include "process-util.h"
#include "random-util.h"
+#include "selinux-util.h"
#include "serialize.h"
#include "service.h"
#include "signal-util.h"
@@ -49,61 +51,61 @@
#define service_spawn(...) service_spawn_internal(__func__, __VA_ARGS__)
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
- [SERVICE_DEAD] = UNIT_INACTIVE,
- [SERVICE_CONDITION] = UNIT_ACTIVATING,
- [SERVICE_START_PRE] = UNIT_ACTIVATING,
- [SERVICE_START] = UNIT_ACTIVATING,
- [SERVICE_START_POST] = UNIT_ACTIVATING,
- [SERVICE_RUNNING] = UNIT_ACTIVE,
- [SERVICE_EXITED] = UNIT_ACTIVE,
- [SERVICE_RELOAD] = UNIT_RELOADING,
- [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
- [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
- [SERVICE_STOP] = UNIT_DEACTIVATING,
- [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
- [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
- [SERVICE_FAILED] = UNIT_FAILED,
- [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
+ [SERVICE_DEAD] = UNIT_INACTIVE,
+ [SERVICE_CONDITION] = UNIT_ACTIVATING,
+ [SERVICE_START_PRE] = UNIT_ACTIVATING,
+ [SERVICE_START] = UNIT_ACTIVATING,
+ [SERVICE_START_POST] = UNIT_ACTIVATING,
+ [SERVICE_RUNNING] = UNIT_ACTIVE,
+ [SERVICE_EXITED] = UNIT_ACTIVE,
+ [SERVICE_RELOAD] = UNIT_RELOADING,
+ [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
+ [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
+ [SERVICE_STOP] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
+ [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_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
- [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
- [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
- [SERVICE_CLEANING] = UNIT_MAINTENANCE,
+ [SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
+ [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
+ [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
+ [SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
/* For Type=idle we never want to delay any other jobs, hence we
* consider idle jobs active as soon as we start working on them */
static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = {
- [SERVICE_DEAD] = UNIT_INACTIVE,
- [SERVICE_CONDITION] = UNIT_ACTIVE,
- [SERVICE_START_PRE] = UNIT_ACTIVE,
- [SERVICE_START] = UNIT_ACTIVE,
- [SERVICE_START_POST] = UNIT_ACTIVE,
- [SERVICE_RUNNING] = UNIT_ACTIVE,
- [SERVICE_EXITED] = UNIT_ACTIVE,
- [SERVICE_RELOAD] = UNIT_RELOADING,
- [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
- [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
- [SERVICE_STOP] = UNIT_DEACTIVATING,
- [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
- [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
- [SERVICE_FAILED] = UNIT_FAILED,
- [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
+ [SERVICE_DEAD] = UNIT_INACTIVE,
+ [SERVICE_CONDITION] = UNIT_ACTIVE,
+ [SERVICE_START_PRE] = UNIT_ACTIVE,
+ [SERVICE_START] = UNIT_ACTIVE,
+ [SERVICE_START_POST] = UNIT_ACTIVE,
+ [SERVICE_RUNNING] = UNIT_ACTIVE,
+ [SERVICE_EXITED] = UNIT_ACTIVE,
+ [SERVICE_RELOAD] = UNIT_RELOADING,
+ [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
+ [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
+ [SERVICE_STOP] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
+ [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_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
- [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
- [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
- [SERVICE_CLEANING] = UNIT_MAINTENANCE,
+ [SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
+ [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
+ [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
+ [SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
@@ -114,6 +116,25 @@ static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t ev
static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
static void service_enter_reload_by_notify(Service *s);
+static bool SERVICE_STATE_WITH_MAIN_PROCESS(ServiceState state) {
+ return IN_SET(state,
+ SERVICE_START, SERVICE_START_POST,
+ SERVICE_RUNNING,
+ SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
+ SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL);
+}
+
+static bool SERVICE_STATE_WITH_CONTROL_PROCESS(ServiceState state) {
+ return IN_SET(state,
+ SERVICE_CONDITION,
+ SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
+ SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+ SERVICE_CLEANING);
+}
+
static void service_init(Unit *u) {
Service *s = SERVICE(u);
@@ -151,25 +172,17 @@ static void service_init(Unit *u) {
static void service_unwatch_control_pid(Service *s) {
assert(s);
-
- if (!pidref_is_set(&s->control_pid))
- return;
-
- unit_unwatch_pidref(UNIT(s), &s->control_pid);
- pidref_done(&s->control_pid);
+ unit_unwatch_pidref_done(UNIT(s), &s->control_pid);
}
static void service_unwatch_main_pid(Service *s) {
assert(s);
-
- if (!pidref_is_set(&s->main_pid))
- return;
-
- unit_unwatch_pidref(UNIT(s), &s->main_pid);
- pidref_done(&s->main_pid);
+ unit_unwatch_pidref_done(UNIT(s), &s->main_pid);
}
static void service_unwatch_pid_file(Service *s) {
+ assert(s);
+
if (!s->pid_file_pathspec)
return;
@@ -179,42 +192,41 @@ static void service_unwatch_pid_file(Service *s) {
s->pid_file_pathspec = mfree(s->pid_file_pathspec);
}
-static int service_set_main_pidref(Service *s, PidRef *pidref) {
+static int service_set_main_pidref(Service *s, PidRef pidref_consume, const dual_timestamp *start_timestamp) {
+ _cleanup_(pidref_done) PidRef pidref = pidref_consume;
int r;
assert(s);
- /* Takes ownership of the specified pidref on success, but not on failure. */
+ /* Takes ownership of the specified pidref on both success and failure. */
- if (!pidref_is_set(pidref))
+ if (!pidref_is_set(&pidref))
return -ESRCH;
- if (pidref->pid <= 1)
+ if (pidref.pid <= 1)
return -EINVAL;
- if (pidref_is_self(pidref))
+ if (pidref_is_self(&pidref))
return -EINVAL;
- if (pidref_equal(&s->main_pid, pidref) && s->main_pid_known) {
- pidref_done(pidref);
+ if (s->main_pid_known && pidref_equal(&s->main_pid, &pidref))
return 0;
- }
- if (!pidref_equal(&s->main_pid, pidref)) {
+ if (!pidref_equal(&s->main_pid, &pidref)) {
service_unwatch_main_pid(s);
- exec_status_start(&s->main_exec_status, pidref->pid);
+ exec_status_start(&s->main_exec_status, pidref.pid, start_timestamp);
}
- s->main_pid = TAKE_PIDREF(*pidref);
+ s->main_pid = TAKE_PIDREF(pidref);
s->main_pid_known = true;
r = pidref_is_my_child(&s->main_pid);
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "Can't determine if process "PID_FMT" is our child, assuming it is not: %m", s->main_pid.pid);
- else if (r == 0)
+ else if (r == 0) // FIXME: Supervise through pidfd here
log_unit_warning(UNIT(s), "Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", s->main_pid.pid);
-
s->main_pid_alien = r <= 0;
+
return 0;
}
@@ -290,7 +302,7 @@ static void service_start_watchdog(Service *s) {
/* Let's process everything else which might be a sign
* of living before we consider a service died. */
- r = sd_event_source_set_priority(s->watchdog_event_source, SD_EVENT_PRIORITY_IDLE);
+ r = sd_event_source_set_priority(s->watchdog_event_source, EVENT_PRIORITY_SERVICE_WATCHDOG);
}
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "Failed to install watchdog timer: %m");
@@ -429,7 +441,7 @@ static void service_release_fd_store(Service *s) {
static void service_release_stdio_fd(Service *s) {
assert(s);
- if (s->stdin_fd < 0 && s->stdout_fd < 0 && s->stdout_fd < 0)
+ if (s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0)
return;
log_unit_debug(UNIT(s), "Releasing stdin/stdout/stderr file descriptors.");
@@ -438,10 +450,9 @@ static void service_release_stdio_fd(Service *s) {
s->stdout_fd = asynchronous_close(s->stdout_fd);
s->stderr_fd = asynchronous_close(s->stderr_fd);
}
-static void service_done(Unit *u) {
- Service *s = SERVICE(u);
- assert(s);
+static void service_done(Unit *u) {
+ Service *s = ASSERT_PTR(SERVICE(u));
open_file_free_many(&s->open_files);
@@ -449,6 +460,7 @@ static void service_done(Unit *u) {
s->status_text = mfree(s->status_text);
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;
@@ -511,7 +523,8 @@ static int service_add_fd_store(Service *s, int fd_in, const char *name, bool do
if (fstat(fd, &st) < 0)
return -errno;
- log_unit_debug(UNIT(s), "Trying to stash fd for dev=" DEVNUM_FORMAT_STR "/inode=%" PRIu64, DEVNUM_FORMAT_VAL(st.st_dev), (uint64_t) st.st_ino);
+ log_unit_debug(UNIT(s), "Trying to stash fd for dev=" DEVNUM_FORMAT_STR "/inode=%" PRIu64,
+ DEVNUM_FORMAT_VAL(st.st_dev), (uint64_t) st.st_ino);
if (s->n_fd_store >= s->n_fd_store_max)
/* Our store is full. Use this errno rather than E[NM]FILE to distinguish from the case
@@ -545,17 +558,16 @@ static int service_add_fd_store(Service *s, int fd_in, const char *name, bool do
r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fs->fd, 0, on_fd_store_io, fs);
if (r < 0 && r != -EPERM) /* EPERM indicates fds that aren't pollable, which is OK */
return r;
- else if (r >= 0)
+ if (r >= 0)
(void) sd_event_source_set_description(fs->event_source, "service-fd-store");
}
+ log_unit_debug(UNIT(s), "Added fd %i (%s) to fd store.", fs->fd, fs->fdname);
+
fs->service = s;
- LIST_PREPEND(fd_store, s->fd_store, fs);
+ LIST_PREPEND(fd_store, s->fd_store, TAKE_PTR(fs));
s->n_fd_store++;
- log_unit_debug(UNIT(s), "Added fd %i (%s) to fd store.", fs->fd, fs->fdname);
-
- TAKE_PTR(fs);
return 1; /* fd newly stored */
}
@@ -654,9 +666,6 @@ static int service_verify(Service *s) {
if (s->type == SERVICE_ONESHOT && IN_SET(s->restart, SERVICE_RESTART_ALWAYS, SERVICE_RESTART_ON_SUCCESS))
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has Restart= set to either always or on-success, which isn't allowed for Type=oneshot services. Refusing.");
- if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status))
- return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has RestartForceExitStatus= set, which isn't allowed for Type=oneshot services. Refusing.");
-
if (s->type == SERVICE_ONESHOT && s->exit_type == SERVICE_EXIT_CGROUP)
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has ExitType=cgroup set, which isn't allowed for Type=oneshot services. Refusing.");
@@ -856,7 +865,7 @@ static int service_add_extras(Service *s) {
}
static int service_load(Unit *u) {
- Service *s = SERVICE(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
int r;
r = unit_load_fragment_and_dropin(u, true);
@@ -901,21 +910,19 @@ static void service_dump_fdstore(Service *s, FILE *f, const char *prefix) {
"%s%s '%s' (type=%s; dev=" DEVNUM_FORMAT_STR "; inode=%" PRIu64 "; rdev=" DEVNUM_FORMAT_STR "; path=%s; access=%s)\n",
prefix, i == s->fd_store ? "File Descriptor Store Entry:" : " ",
i->fdname,
- inode_type_to_string(st.st_mode),
+ strna(inode_type_to_string(st.st_mode)),
DEVNUM_FORMAT_VAL(st.st_dev),
(uint64_t) st.st_ino,
DEVNUM_FORMAT_VAL(st.st_rdev),
strna(path),
- accmode_to_string(flags));
+ strna(accmode_to_string(flags)));
}
}
static void service_dump(Unit *u, FILE *f, const char *prefix) {
- Service *s = SERVICE(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
const char *prefix2;
- assert(s);
-
prefix = strempty(prefix);
prefix2 = strjoina(prefix, "\t");
@@ -1016,8 +1023,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
if (!s->exec_command[c])
continue;
- fprintf(f, "%s-> %s:\n",
- prefix, service_exec_command_to_string(c));
+ fprintf(f, "%s%s %s:\n",
+ prefix, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), service_exec_command_to_string(c));
exec_command_dump_list(s->exec_command[c], f, prefix2);
}
@@ -1159,7 +1166,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
} else
log_unit_debug(UNIT(s), "Main PID loaded: "PID_FMT, pidref.pid);
- r = service_set_main_pidref(s, &pidref);
+ r = service_set_main_pidref(s, TAKE_PIDREF(pidref), /* start_timestamp = */ NULL);
if (r < 0)
return r;
@@ -1189,7 +1196,7 @@ static void service_search_main_pid(Service *s) {
return;
log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid.pid);
- if (service_set_main_pidref(s, &pid) < 0)
+ if (service_set_main_pidref(s, TAKE_PIDREF(pid), /* start_timestamp = */ NULL) < 0)
return;
r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
@@ -1224,22 +1231,12 @@ static void service_set_state(Service *s, ServiceState state) {
SERVICE_CLEANING))
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
- if (!IN_SET(state,
- SERVICE_START, SERVICE_START_POST,
- SERVICE_RUNNING,
- SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
- SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
+ if (!SERVICE_STATE_WITH_MAIN_PROCESS(state)) {
service_unwatch_main_pid(s);
s->main_command = NULL;
}
- if (!IN_SET(state,
- SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
- SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
- SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
- SERVICE_CLEANING)) {
+ if (!SERVICE_STATE_WITH_CONTROL_PROCESS(state)) {
service_unwatch_control_pid(s);
s->control_command = NULL;
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
@@ -1326,12 +1323,7 @@ static int service_coldplug(Unit *u) {
if (pidref_is_set(&s->main_pid) &&
pidref_is_unwaited(&s->main_pid) > 0 &&
- (IN_SET(s->deserialized_state,
- SERVICE_START, SERVICE_START_POST,
- SERVICE_RUNNING,
- SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
- SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
+ SERVICE_STATE_WITH_MAIN_PROCESS(s->deserialized_state)) {
r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
if (r < 0)
return r;
@@ -1339,12 +1331,7 @@ static int service_coldplug(Unit *u) {
if (pidref_is_set(&s->control_pid) &&
pidref_is_unwaited(&s->control_pid) > 0 &&
- IN_SET(s->deserialized_state,
- SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
- SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
- SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
- SERVICE_CLEANING)) {
+ SERVICE_STATE_WITH_CONTROL_PROCESS(s->deserialized_state)) {
r = unit_watch_pidref(UNIT(s), &s->control_pid, /* exclusive= */ false);
if (r < 0)
return r;
@@ -1357,6 +1344,7 @@ static int service_coldplug(Unit *u) {
SERVICE_DEAD_RESOURCES_PINNED)) {
(void) unit_enqueue_rewatch_pids(u);
(void) unit_setup_exec_runtime(u);
+ (void) unit_setup_cgroup_runtime(u);
}
if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY))
@@ -1418,13 +1406,12 @@ static int service_collect_fds(
UNIT_FOREACH_DEPENDENCY(u, UNIT(s), UNIT_ATOM_TRIGGERED_BY) {
_cleanup_free_ int *cfds = NULL;
- Socket *sock;
int cn_fds;
-
- if (u->type != UNIT_SOCKET)
- continue;
+ Socket *sock;
sock = SOCKET(u);
+ if (!sock)
+ continue;
cn_fds = socket_collect_fds(sock, &cfds);
if (cn_fds < 0)
@@ -1436,18 +1423,8 @@ static int service_collect_fds(
if (!rfds) {
rfds = TAKE_PTR(cfds);
rn_socket_fds = cn_fds;
- } else {
- int *t;
-
- t = reallocarray(rfds, rn_socket_fds + cn_fds, sizeof(int));
- if (!t)
- return -ENOMEM;
-
- memcpy(t + rn_socket_fds, cfds, cn_fds * sizeof(int));
-
- rfds = t;
- rn_socket_fds += cn_fds;
- }
+ } else if (!GREEDY_REALLOC_APPEND(rfds, rn_socket_fds, cfds, cn_fds))
+ return -ENOMEM;
r = strv_extend_n(&rfd_names, socket_fdname(sock), cn_fds);
if (r < 0)
@@ -1510,9 +1487,10 @@ static int service_allocate_exec_fd_event_source(
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to allocate exec_fd event source: %m");
- /* This is a bit lower priority than SIGCHLD, as that carries a lot more interesting failure information */
+ /* This is a bit higher priority than SIGCHLD, to make sure we don't confuse the case "failed to
+ * start" from the case "succeeded to start, but failed immediately after". */
- r = sd_event_source_set_priority(source, SD_EVENT_PRIORITY_NORMAL-3);
+ r = sd_event_source_set_priority(source, EVENT_PRIORITY_EXEC_FD);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to adjust priority of exec_fd event source: %m");
@@ -1602,12 +1580,52 @@ static Service *service_get_triggering_service(Service *s) {
return NULL;
}
+static ExecFlags service_exec_flags(ServiceExecCommand command_id, ExecFlags cred_flag) {
+ /* All service main/control processes honor sandboxing and namespacing options (except those
+ explicitly excluded in service_spawn()) */
+ ExecFlags flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT;
+
+ assert(command_id >= 0);
+ assert(command_id < _SERVICE_EXEC_COMMAND_MAX);
+ assert((cred_flag & ~(EXEC_SETUP_CREDENTIALS_FRESH|EXEC_SETUP_CREDENTIALS)) == 0);
+ assert((cred_flag != 0) == (command_id == SERVICE_EXEC_START));
+
+ /* Control processes spawned before main process also get tty access */
+ if (IN_SET(command_id, SERVICE_EXEC_CONDITION, SERVICE_EXEC_START_PRE, SERVICE_EXEC_START))
+ flags |= EXEC_APPLY_TTY_STDIN;
+
+ /* All start phases get access to credentials. ExecStartPre= gets a new credential store upon
+ * every invocation, so that updating credential files through it works. When the first main process
+ * starts, passed creds become stable. Also see 'cred_flag'. */
+ if (command_id == SERVICE_EXEC_START_PRE)
+ flags |= EXEC_SETUP_CREDENTIALS_FRESH;
+ if (command_id == SERVICE_EXEC_START_POST)
+ flags |= EXEC_SETUP_CREDENTIALS;
+
+ if (IN_SET(command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_START))
+ flags |= EXEC_SETENV_MONITOR_RESULT;
+
+ if (command_id == SERVICE_EXEC_START)
+ return flags|cred_flag|EXEC_PASS_FDS|EXEC_SET_WATCHDOG;
+
+ flags |= EXEC_IS_CONTROL;
+
+ /* Put control processes spawned later than main process under .control sub-cgroup if appropriate */
+ if (!IN_SET(command_id, SERVICE_EXEC_CONDITION, SERVICE_EXEC_START_PRE))
+ flags |= EXEC_CONTROL_CGROUP;
+
+ if (IN_SET(command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST))
+ flags |= EXEC_SETENV_RESULT;
+
+ return flags;
+}
+
static int service_spawn_internal(
const char *caller,
Service *s,
ExecCommand *c,
- usec_t timeout,
ExecFlags flags,
+ usec_t timeout,
PidRef *ret_pid) {
_cleanup_(exec_params_shallow_clear) ExecParameters exec_params = EXEC_PARAMETERS_INIT(flags);
@@ -1615,7 +1633,6 @@ static int service_spawn_internal(
_cleanup_strv_free_ char **final_env = NULL, **our_env = NULL;
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
size_t n_env = 0;
- pid_t pid;
int r;
assert(caller);
@@ -1631,7 +1648,7 @@ static int service_spawn_internal(
assert(!s->exec_fd_event_source);
- if (flags & EXEC_IS_CONTROL) {
+ if (FLAGS_SET(exec_params.flags, EXEC_IS_CONTROL)) {
/* If this is a control process, mask the permissions/chroot application if this is requested. */
if (s->permissions_start_only)
exec_params.flags &= ~EXEC_APPLY_SANDBOXING;
@@ -1639,7 +1656,7 @@ static int service_spawn_internal(
exec_params.flags &= ~EXEC_APPLY_CHROOT;
}
- if ((flags & EXEC_PASS_FDS) ||
+ if (FLAGS_SET(exec_params.flags, EXEC_PASS_FDS) ||
s->exec_context.std_input == EXEC_INPUT_SOCKET ||
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
@@ -1654,10 +1671,12 @@ static int service_spawn_internal(
exec_params.open_files = s->open_files;
+ exec_params.flags |= EXEC_PASS_FDS;
+
log_unit_debug(UNIT(s), "Passing %zu fds to service", exec_params.n_socket_fds + exec_params.n_storage_fds);
}
- if (!FLAGS_SET(flags, EXEC_IS_CONTROL) && s->type == SERVICE_EXEC) {
+ if (!FLAGS_SET(exec_params.flags, EXEC_IS_CONTROL) && s->type == SERVICE_EXEC) {
r = service_allocate_exec_fd(s, &exec_fd_source, &exec_params.exec_fd);
if (r < 0)
return r;
@@ -1671,7 +1690,7 @@ static int service_spawn_internal(
if (!our_env)
return -ENOMEM;
- if (service_exec_needs_notify_socket(s, flags)) {
+ if (service_exec_needs_notify_socket(s, exec_params.flags)) {
if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0)
return -ENOMEM;
@@ -1730,10 +1749,10 @@ static int service_spawn_internal(
Service *env_source = NULL;
const char *monitor_prefix;
- if (flags & EXEC_SETENV_RESULT) {
+ if (FLAGS_SET(exec_params.flags, EXEC_SETENV_RESULT)) {
env_source = s;
monitor_prefix = "";
- } else if (flags & EXEC_SETENV_MONITOR_RESULT) {
+ } else if (FLAGS_SET(exec_params.flags, EXEC_SETENV_MONITOR_RESULT)) {
env_source = service_get_triggering_service(s);
monitor_prefix = "MONITOR_";
}
@@ -1751,18 +1770,15 @@ static int service_spawn_internal(
r = asprintf(our_env + n_env++, "%sEXIT_STATUS=%i", monitor_prefix, env_source->main_exec_status.status);
else
r = asprintf(our_env + n_env++, "%sEXIT_STATUS=%s", monitor_prefix, signal_to_string(env_source->main_exec_status.status));
-
if (r < 0)
return -ENOMEM;
}
if (env_source != s) {
- if (!sd_id128_is_null(UNIT(env_source)->invocation_id)) {
- r = asprintf(our_env + n_env++, "%sINVOCATION_ID=" SD_ID128_FORMAT_STR,
- monitor_prefix, SD_ID128_FORMAT_VAL(UNIT(env_source)->invocation_id));
- if (r < 0)
+ if (!sd_id128_is_null(UNIT(env_source)->invocation_id))
+ if (asprintf(our_env + n_env++, "%sINVOCATION_ID=" SD_ID128_FORMAT_STR,
+ monitor_prefix, SD_ID128_FORMAT_VAL(UNIT(env_source)->invocation_id)) < 0)
return -ENOMEM;
- }
if (asprintf(our_env + n_env++, "%sUNIT=%s", monitor_prefix, UNIT(env_source)->id) < 0)
return -ENOMEM;
@@ -1806,17 +1822,13 @@ static int service_spawn_internal(
&exec_params,
s->exec_runtime,
&s->cgroup_context,
- &pid);
+ &pidref);
if (r < 0)
return r;
s->exec_fd_event_source = TAKE_PTR(exec_fd_source);
s->exec_fd_hot = false;
- r = pidref_set_pid(&pidref, pid);
- if (r < 0)
- return r;
-
r = unit_watch_pidref(UNIT(s), &pidref, /* exclusive= */ true);
if (r < 0)
return r;
@@ -1864,10 +1876,10 @@ static int cgroup_good(Service *s) {
/* Returns 0 if the cgroup is empty or doesn't exist, > 0 if it is exists and is populated, < 0 if we can't
* figure it out */
- if (!UNIT(s)->cgroup_path)
+ if (!s->cgroup_runtime || !s->cgroup_runtime->cgroup_path)
return 0;
- r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path);
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_runtime->cgroup_path);
if (r < 0)
return r;
@@ -1876,6 +1888,7 @@ static int cgroup_good(Service *s) {
static bool service_shall_restart(Service *s, const char **reason) {
assert(s);
+ assert(reason);
/* Don't restart after manual stops */
if (s->forbid_restart) {
@@ -1891,6 +1904,13 @@ static bool service_shall_restart(Service *s, const char **reason) {
/* Restart if the exit code/status are configured as restart triggers */
if (exit_status_set_test(&s->restart_force_status, s->main_exec_status.code, s->main_exec_status.status)) {
+ /* Don't allow Type=oneshot services to restart on success. Note that Restart=always/on-success
+ * is already rejected in service_verify. */
+ if (s->type == SERVICE_ONESHOT && s->result == SERVICE_SUCCESS) {
+ *reason = "service type and exit status";
+ return false;
+ }
+
*reason = "forced by exit status";
return true;
}
@@ -1962,7 +1982,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
} else if (s->result == SERVICE_SKIP_CONDITION) {
unit_log_skip(UNIT(s), service_result_to_string(s->result));
end_state = service_determine_dead_state(s);
- restart_state = SERVICE_DEAD_BEFORE_AUTO_RESTART;
+ restart_state = _SERVICE_STATE_INVALID; /* Never restart if skipped due to condition failure */
} else {
unit_log_failure(UNIT(s), service_result_to_string(s->result));
end_state = SERVICE_FAILED;
@@ -1984,8 +2004,10 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
if (allow_restart) {
usec_t restart_usec_next;
+ assert(restart_state >= 0 && restart_state < _SERVICE_STATE_MAX);
+
/* 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
+ * state (i.e. a state indicating deactivation), and then one 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
@@ -1999,8 +2021,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
r = service_arm_timer(s, /* relative= */ true, restart_usec_next);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to install restart timer: %m");
- service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ false);
- return;
+ return service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ false);
}
log_unit_debug(UNIT(s), "Next restart interval calculated as: %s", FORMAT_TIMESPAN(restart_usec_next, 0));
@@ -2064,8 +2085,8 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
r = service_spawn(s,
s->control_command,
+ service_exec_flags(s->control_command_id, /* cred_flag = */ 0),
s->timeout_stop_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL|EXEC_SETENV_RESULT|EXEC_CONTROL_CGROUP,
&s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'stop-post' task: %m");
@@ -2118,13 +2139,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
(void) unit_enqueue_rewatch_pids(UNIT(s));
kill_operation = state_to_kill_operation(s, state);
- r = unit_kill_context(
- UNIT(s),
- &s->kill_context,
- kill_operation,
- &s->main_pid,
- &s->control_pid,
- s->main_pid_alien);
+ r = unit_kill_context(UNIT(s), kill_operation);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
goto fail;
@@ -2193,8 +2208,8 @@ static void service_enter_stop(Service *s, ServiceResult f) {
r = service_spawn(s,
s->control_command,
+ service_exec_flags(s->control_command_id, /* cred_flag = */ 0),
s->timeout_stop_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_SETENV_RESULT|EXEC_CONTROL_CGROUP,
&s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'stop' task: %m");
@@ -2209,6 +2224,7 @@ static void service_enter_stop(Service *s, ServiceResult f) {
static bool service_good(Service *s) {
int main_pid_ok;
+
assert(s);
if (s->type == SERVICE_DBUS && !s->bus_name_good)
@@ -2265,6 +2281,7 @@ static void service_enter_running(Service *s, ServiceResult f) {
static void service_enter_start_post(Service *s) {
int r;
+
assert(s);
service_unwatch_control_pid(s);
@@ -2277,8 +2294,8 @@ static void service_enter_start_post(Service *s) {
r = service_spawn(s,
s->control_command,
+ service_exec_flags(s->control_command_id, /* cred_flag = */ 0),
s->timeout_start_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP,
&s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'start-post' task: %m");
@@ -2387,43 +2404,44 @@ static void service_enter_start(Service *s) {
r = service_spawn(s,
c,
+ service_exec_flags(SERVICE_EXEC_START, EXEC_SETUP_CREDENTIALS_FRESH),
timeout,
- EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_WRITE_CREDENTIALS|EXEC_SETENV_MONITOR_RESULT,
&pidref);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'start' task: %m");
goto fail;
}
- if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE)) {
- /* For simple services we immediately start
- * the START_POST binaries. */
+ assert(pidref.pid == c->exec_status.pid);
- (void) service_set_main_pidref(s, &pidref);
- service_enter_start_post(s);
-
- } else if (s->type == SERVICE_FORKING) {
+ switch (s->type) {
- /* For forking services we wait until the start
- * process exited. */
+ case SERVICE_SIMPLE:
+ case SERVICE_IDLE:
+ /* For simple services we immediately start the START_POST binaries. */
+ (void) service_set_main_pidref(s, TAKE_PIDREF(pidref), &c->exec_status.start_timestamp);
+ return service_enter_start_post(s);
+ case SERVICE_FORKING:
+ /* For forking services we wait until the start process exited. */
pidref_done(&s->control_pid);
s->control_pid = TAKE_PIDREF(pidref);
- service_set_state(s, SERVICE_START);
-
- } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD, SERVICE_EXEC)) {
+ return service_set_state(s, SERVICE_START);
+
+ case SERVICE_ONESHOT: /* For oneshot services we wait until the start process exited, too, but it is our main process. */
+ case SERVICE_EXEC:
+ case SERVICE_DBUS:
+ case SERVICE_NOTIFY:
+ case SERVICE_NOTIFY_RELOAD:
+ /* For D-Bus services we know the main pid right away, but wait for the bus name to appear
+ * on the bus. 'notify' and 'exec' services wait for readiness notification and EOF
+ * on exec_fd, respectively. */
+ (void) service_set_main_pidref(s, TAKE_PIDREF(pidref), &c->exec_status.start_timestamp);
+ return service_set_state(s, SERVICE_START);
- /* For oneshot services we wait until the start process exited, too, but it is our main process. */
-
- /* For D-Bus services we know the main pid right away, but wait for the bus name to appear on the
- * bus. 'notify' and 'exec' services are similar. */
-
- (void) service_set_main_pidref(s, &pidref);
- service_set_state(s, SERVICE_START);
- } else
+ default:
assert_not_reached();
-
- return;
+ }
fail:
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
@@ -2447,8 +2465,8 @@ static void service_enter_start_pre(Service *s) {
r = service_spawn(s,
s->control_command,
+ service_exec_flags(s->control_command_id, /* cred_flag = */ 0),
s->timeout_start_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN|EXEC_SETENV_MONITOR_RESULT|EXEC_WRITE_CREDENTIALS,
&s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'start-pre' task: %m");
@@ -2484,10 +2502,9 @@ static void service_enter_condition(Service *s) {
r = service_spawn(s,
s->control_command,
+ service_exec_flags(s->control_command_id, /* cred_flag = */ 0),
s->timeout_start_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN,
&s->control_pid);
-
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'exec-condition' task: %m");
goto fail;
@@ -2527,11 +2544,9 @@ static void service_enter_restart(Service *s) {
/* Count the jobs we enqueue for restarting. This counter is maintained as long as the unit isn't
* fully stopped, i.e. as long as it remains up or remains in auto-start states. The user can reset
* the counter explicitly however via the usual "systemctl reset-failure" logic. */
- s->n_restarts ++;
+ s->n_restarts++;
s->flush_n_restarts = false;
- s->notify_access_override = _NOTIFY_ACCESS_INVALID;
-
log_unit_struct(UNIT(s), LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_UNIT_RESTART_SCHEDULED_STR,
LOG_UNIT_INVOCATION_ID(UNIT(s)),
@@ -2595,8 +2610,8 @@ static void service_enter_reload(Service *s) {
r = service_spawn(s,
s->control_command,
+ service_exec_flags(s->control_command_id, /* cred_flag = */ 0),
s->timeout_start_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP,
&s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'reload' task: %m");
@@ -2651,13 +2666,8 @@ static void service_run_next_control(Service *s) {
r = service_spawn(s,
s->control_command,
+ service_exec_flags(s->control_command_id, /* cred_flag = */ 0),
timeout,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|
- (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD) ? EXEC_WRITE_CREDENTIALS : 0)|
- (IN_SET(s->control_command_id, SERVICE_EXEC_CONDITION, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)|
- (IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0)|
- (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_START) ? EXEC_SETENV_MONITOR_RESULT : 0)|
- (IN_SET(s->control_command_id, SERVICE_EXEC_START_POST, SERVICE_EXEC_RELOAD, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_CONTROL_CGROUP : 0),
&s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn next control task: %m");
@@ -2688,8 +2698,8 @@ static void service_run_next_main(Service *s) {
r = service_spawn(s,
s->main_command,
+ service_exec_flags(SERVICE_EXEC_START, EXEC_SETUP_CREDENTIALS),
s->timeout_start_usec,
- EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_SETENV_MONITOR_RESULT|EXEC_WRITE_CREDENTIALS,
&pidref);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn next main task: %m");
@@ -2697,7 +2707,7 @@ static void service_run_next_main(Service *s) {
return;
}
- (void) service_set_main_pidref(s, &pidref);
+ (void) service_set_main_pidref(s, TAKE_PIDREF(pidref), &s->main_command->exec_status.start_timestamp);
}
static int service_start(Unit *u) {
@@ -2755,16 +2765,16 @@ static int service_start(Unit *u) {
s->flush_n_restarts = false;
}
- u->reset_accounting = true;
+ CGroupRuntime *crt = unit_get_cgroup_runtime(u);
+ if (crt)
+ crt->reset_accounting = true;
service_enter_condition(s);
return 1;
}
static int service_stop(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
+ Service *s = ASSERT_PTR(SERVICE(u));
/* Don't create restart jobs from manual stops. */
s->forbid_restart = true;
@@ -2821,9 +2831,7 @@ static int service_stop(Unit *u) {
}
static int service_reload(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
+ Service *s = ASSERT_PTR(SERVICE(u));
assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED));
@@ -2832,9 +2840,7 @@ static int service_reload(Unit *u) {
}
static bool service_can_reload(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
+ Service *s = ASSERT_PTR(SERVICE(u));
return s->exec_command[SERVICE_EXEC_RELOAD] ||
s->type == SERVICE_NOTIFY_RELOAD;
@@ -2858,14 +2864,13 @@ static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, const
}
static int service_serialize_exec_command(Unit *u, FILE *f, const ExecCommand *command) {
+ Service *s = ASSERT_PTR(SERVICE(u));
_cleanup_free_ char *args = NULL, *p = NULL;
- Service *s = SERVICE(u);
const char *type, *key;
ServiceExecCommand id;
size_t length = 0;
unsigned idx;
- assert(s);
assert(f);
if (!command)
@@ -2927,10 +2932,9 @@ static int service_serialize_exec_command(Unit *u, FILE *f, const ExecCommand *c
}
static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
- Service *s = SERVICE(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
int r;
- assert(u);
assert(f);
assert(fds);
@@ -2996,13 +3000,14 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
if (!c)
return log_oom();
- (void) serialize_item_format(f, "fd-store-fd", "%i \"%s\" %i", copy, c, fs->do_poll);
+ (void) serialize_item_format(f, "fd-store-fd", "%i \"%s\" %s", copy, c, one_zero(fs->do_poll));
}
if (s->main_exec_status.pid > 0) {
(void) serialize_item_format(f, "main-exec-status-pid", PID_FMT, s->main_exec_status.pid);
(void) serialize_dual_timestamp(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
(void) serialize_dual_timestamp(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
+ (void) serialize_dual_timestamp(f, "main-exec-status-handoff", &s->main_exec_status.handoff_timestamp);
if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
(void) serialize_item_format(f, "main-exec-status-code", "%i", s->main_exec_status.code);
@@ -3033,14 +3038,14 @@ int service_deserialize_exec_command(
const char *key,
const char *value) {
- Service *s = SERVICE(u);
- int r;
- unsigned idx = 0, i;
- bool control, found = false, last = false;
- ServiceExecCommand id = _SERVICE_EXEC_COMMAND_INVALID;
+ Service *s = ASSERT_PTR(SERVICE(u));
ExecCommand *command = NULL;
+ ServiceExecCommand id = _SERVICE_EXEC_COMMAND_INVALID;
_cleanup_free_ char *path = NULL;
_cleanup_strv_free_ char **argv = NULL;
+ unsigned idx = 0, i;
+ bool control, found = false, last = false;
+ int r;
enum ExecCommandState {
STATE_EXEC_COMMAND_TYPE,
@@ -3051,7 +3056,6 @@ int service_deserialize_exec_command(
_STATE_EXEC_COMMAND_INVALID = -EINVAL,
} state;
- assert(s);
assert(key);
assert(value);
@@ -3096,7 +3100,7 @@ int service_deserialize_exec_command(
case STATE_EXEC_COMMAND_ARGS:
r = strv_extend(&argv, arg);
if (r < 0)
- return -ENOMEM;
+ return r;
break;
default:
assert_not_reached();
@@ -3139,10 +3143,9 @@ int service_deserialize_exec_command(
}
static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Service *s = SERVICE(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
int r;
- assert(u);
assert(key);
assert(value);
assert(fds);
@@ -3179,10 +3182,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
(void) deserialize_pidref(fds, value, &s->control_pid);
} else if (streq(key, "main-pid")) {
- _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ PidRef pidref;
if (!pidref_is_set(&s->main_pid) && deserialize_pidref(fds, value, &pidref) >= 0)
- (void) service_set_main_pidref(s, &pidref);
+ (void) service_set_main_pidref(s, pidref, /* start_timestamp = */ NULL);
} else if (streq(key, "main-pid-known")) {
int b;
@@ -3239,9 +3242,9 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
_cleanup_close_ int fd = -EBADF;
int do_poll;
- r = extract_first_word(&value, &fdv, NULL, 0);
- if (r <= 0) {
- log_unit_debug(u, "Failed to parse fd-store-fd value, ignoring: %s", value);
+ r = extract_many_words(&value, " ", EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE, &fdv, &fdn, &fdp);
+ if (r < 2 || r > 3) {
+ log_unit_debug(u, "Failed to deserialize fd-store-fd, ignoring: %s", value);
return 0;
}
@@ -3249,24 +3252,17 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
if (fd < 0)
return 0;
- r = extract_first_word(&value, &fdn, NULL, EXTRACT_CUNESCAPE | EXTRACT_UNQUOTE);
- if (r <= 0) {
- log_unit_debug(u, "Failed to parse fd-store-fd value, ignoring: %s", value);
- return 0;
- }
-
- r = extract_first_word(&value, &fdp, NULL, 0);
- if (r == 0) {
- /* If the value is not present, we assume the default */
- do_poll = 1;
- } else if (r < 0 || (r = safe_atoi(fdp, &do_poll)) < 0) {
- log_unit_debug_errno(u, r, "Failed to parse fd-store-fd value \"%s\", ignoring: %m", value);
+ do_poll = r == 3 ? parse_boolean(fdp) : true;
+ if (do_poll < 0) {
+ log_unit_debug_errno(u, do_poll,
+ "Failed to deserialize fd-store-fd do_poll, ignoring: %s", fdp);
return 0;
}
r = service_add_fd_store(s, fd, fdn, do_poll);
if (r < 0) {
- log_unit_debug_errno(u, r, "Failed to store deserialized fd %i, ignoring: %m", fd);
+ log_unit_debug_errno(u, r,
+ "Failed to store deserialized fd '%s', ignoring: %m", fdn);
return 0;
}
@@ -3296,6 +3292,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
deserialize_dual_timestamp(value, &s->main_exec_status.start_timestamp);
else if (streq(key, "main-exec-status-exit"))
deserialize_dual_timestamp(value, &s->main_exec_status.exit_timestamp);
+ else if (streq(key, "main-exec-status-handoff"))
+ deserialize_dual_timestamp(value, &s->main_exec_status.handoff_timestamp);
else if (streq(key, "notify-access-override")) {
NotifyAccess notify_access;
@@ -3383,13 +3381,12 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
}
static UnitActiveState service_active_state(Unit *u) {
+ Service *s = ASSERT_PTR(SERVICE(u));
const UnitActiveState *table;
- assert(u);
-
- table = SERVICE(u)->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table;
+ table = s->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table;
- return table[SERVICE(u)->state];
+ return table[s->state];
}
static const char *service_sub_state_to_string(Unit *u) {
@@ -3399,9 +3396,7 @@ static const char *service_sub_state_to_string(Unit *u) {
}
static bool service_may_gc(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
+ Service *s = ASSERT_PTR(SERVICE(u));
/* Never clean up services that still have a process around, even if the service is formally dead. Note that
* unit_may_gc() already checked our cgroup for us, we just check our two additional PIDs, too, in case they
@@ -3422,6 +3417,7 @@ static bool service_may_gc(Unit *u) {
static int service_retry_pid_file(Service *s) {
int r;
+ assert(s);
assert(s->pid_file);
assert(IN_SET(s->state, SERVICE_START, SERVICE_START_POST));
@@ -3438,6 +3434,8 @@ static int service_retry_pid_file(Service *s) {
static int service_watch_pid_file(Service *s) {
int r;
+ assert(s);
+
log_unit_debug(UNIT(s), "Setting watch for PID file %s", s->pid_file_pathspec->path);
r = path_spec_watch(s->pid_file_pathspec, service_dispatch_inotify_io);
@@ -3457,6 +3455,7 @@ static int service_watch_pid_file(Service *s) {
static int service_demand_pid_file(Service *s) {
_cleanup_free_ PathSpec *ps = NULL;
+ assert(s);
assert(s->pid_file);
assert(!s->pid_file_pathspec);
@@ -3485,11 +3484,8 @@ static int service_demand_pid_file(Service *s) {
static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata) {
PathSpec *p = ASSERT_PTR(userdata);
- Service *s;
+ Service *s = ASSERT_PTR(SERVICE(p->unit));
- s = SERVICE(p->unit);
-
- assert(s);
assert(fd >= 0);
assert(IN_SET(s->state, SERVICE_START, SERVICE_START_POST));
assert(s->pid_file_pathspec);
@@ -3515,20 +3511,19 @@ fail:
}
static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t events, void *userdata) {
- Service *s = SERVICE(userdata);
-
- assert(s);
+ Service *s = ASSERT_PTR(SERVICE(userdata));
log_unit_debug(UNIT(s), "got exec-fd event");
/* If Type=exec is set, we'll consider a service started successfully the instant we invoked execve()
- * successfully for it. We implement this through a pipe() towards the child, which the kernel automatically
- * closes for us due to O_CLOEXEC on execve() in the child, which then triggers EOF on the pipe in the
- * parent. We need to be careful however, as there are other reasons that we might cause the child's side of
- * the pipe to be closed (for example, a simple exit()). To deal with that we'll ignore EOFs on the pipe unless
- * the child signalled us first that it is about to call the execve(). It does so by sending us a simple
- * non-zero byte via the pipe. We also provide the child with a way to inform us in case execve() failed: if it
- * sends a zero byte we'll ignore POLLHUP on the fd again. */
+ * successfully for it. We implement this through a pipe() towards the child, which the kernel
+ * automatically closes for us due to O_CLOEXEC on execve() in the child, which then triggers EOF on
+ * the pipe in the parent. We need to be careful however, as there are other reasons that we might
+ * cause the child's side of the pipe to be closed (for example, a simple exit()). To deal with that
+ * we'll ignore EOFs on the pipe unless the child signalled us first that it is about to call the
+ * execve(). It does so by sending us a simple non-zero byte via the pipe. We also provide the child
+ * with a way to inform us in case execve() failed: if it sends a zero byte we'll ignore POLLHUP on
+ * the fd again. */
for (;;) {
uint8_t x;
@@ -3541,8 +3536,7 @@ static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t ev
return log_unit_error_errno(UNIT(s), errno, "Failed to read from exec_fd: %m");
}
- if (n == 0) { /* EOF → the event we are waiting for */
-
+ if (n == 0) { /* EOF → the event we are waiting for in case of Type=exec */
s->exec_fd_event_source = sd_event_source_disable_unref(s->exec_fd_event_source);
if (s->exec_fd_hot) { /* Did the child tell us to expect EOF now? */
@@ -3561,16 +3555,13 @@ static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t ev
/* A byte was read → this turns on/off the exec fd logic */
assert(n == sizeof(x));
+
s->exec_fd_hot = x;
}
-
- return 0;
}
static void service_notify_cgroup_empty_event(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
log_unit_debug(u, "Control group is empty.");
@@ -3647,7 +3638,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
}
static void service_notify_cgroup_oom_event(Unit *u, bool managed_oom) {
- Service *s = SERVICE(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
if (managed_oom)
log_unit_debug(u, "Process(es) of control group were killed by systemd-oomd.");
@@ -3702,12 +3693,12 @@ static void service_notify_cgroup_oom_event(Unit *u, bool managed_oom) {
}
static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
+ Service *s = ASSERT_PTR(SERVICE(u));
bool notify_dbus = true;
- Service *s = SERVICE(u);
ServiceResult f;
ExitClean clean_mode;
+ int r;
- assert(s);
assert(pid >= 0);
/* Oneshot services and non-SERVICE_EXEC_START commands should not be
@@ -3918,7 +3909,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_command->command_next &&
f == SERVICE_SUCCESS) {
- /* There is another command to * execute, so let's do that. */
+ /* There is another command to execute, so let's do that. */
log_unit_debug(u, "Running next control command for state %s.", service_state_to_string(s->state));
service_run_next_control(s);
@@ -3959,7 +3950,6 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
if (s->pid_file) {
bool has_start_post;
- int r;
/* Let's try to load the pid file here if we can.
* The PID file might actually be created by a START_POST
@@ -3986,8 +3976,6 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
}
if (s->pid_file) {
- int r;
-
r = service_load_pid_file(s, true);
if (r < 0) {
r = service_demand_pid_file(s);
@@ -4076,9 +4064,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
}
static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) {
- Service *s = SERVICE(userdata);
+ Service *s = ASSERT_PTR(SERVICE(userdata));
- assert(s);
assert(source == s->timer_event_source);
switch (s->state) {
@@ -4275,10 +4262,9 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
}
static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata) {
- Service *s = SERVICE(userdata);
+ Service *s = ASSERT_PTR(SERVICE(userdata));
usec_t watchdog_usec;
- assert(s);
assert(source == s->watchdog_event_source);
watchdog_usec = service_get_watchdog_usec(s);
@@ -4295,35 +4281,49 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
return 0;
}
-static bool service_notify_message_authorized(Service *s, pid_t pid, FDSet *fds) {
+static void service_force_watchdog(Service *s) {
assert(s);
+ if (!UNIT(s)->manager->service_watchdogs)
+ return;
+
+ log_unit_error(UNIT(s), "Watchdog request (last status: %s)!",
+ s->status_text ?: "<unset>");
+
+ service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_WATCHDOG);
+}
+
+static bool service_notify_message_authorized(Service *s, pid_t pid) {
+ assert(s);
+ assert(pid_is_valid(pid));
+
NotifyAccess notify_access = service_get_notify_access(s);
if (notify_access == NOTIFY_NONE) {
- log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception is disabled.", pid);
+ /* Warn level only if no notifications are expected */
+ log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception is disabled", pid);
return false;
}
if (notify_access == NOTIFY_MAIN && pid != s->main_pid.pid) {
if (pidref_is_set(&s->main_pid))
- log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid.pid);
+ log_unit_debug(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid.pid);
else
- log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", pid);
+ log_unit_debug(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", pid);
return false;
}
if (notify_access == NOTIFY_EXEC && pid != s->main_pid.pid && pid != s->control_pid.pid) {
if (pidref_is_set(&s->main_pid) && pidref_is_set(&s->control_pid))
- log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT" and control PID "PID_FMT,
- pid, s->main_pid.pid, s->control_pid.pid);
+ log_unit_debug(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT" and control PID "PID_FMT,
+ pid, s->main_pid.pid, s->control_pid.pid);
else if (pidref_is_set(&s->main_pid))
- log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid.pid);
+ log_unit_debug(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid.pid);
else if (pidref_is_set(&s->control_pid))
- log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for control PID "PID_FMT, pid, s->control_pid.pid);
+ log_unit_debug(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for control PID "PID_FMT, pid, s->control_pid.pid);
else
- log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID and control PID which are currently not known", pid);
+ log_unit_debug(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID and control PID which are currently not known", pid);
return false;
}
@@ -4331,44 +4331,35 @@ static bool service_notify_message_authorized(Service *s, pid_t pid, FDSet *fds)
return true;
}
-static void service_force_watchdog(Service *s) {
- if (!UNIT(s)->manager->service_watchdogs)
- return;
-
- log_unit_error(UNIT(s), "Watchdog request (last status: %s)!",
- s->status_text ?: "<unset>");
-
- service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_WATCHDOG);
-}
-
static void service_notify_message(
Unit *u,
const struct ucred *ucred,
char * const *tags,
FDSet *fds) {
- Service *s = SERVICE(u);
- bool notify_dbus = false;
- usec_t monotonic_usec = USEC_INFINITY;
- const char *e;
+ Service *s = ASSERT_PTR(SERVICE(u));
int r;
- assert(u);
assert(ucred);
- if (!service_notify_message_authorized(s, ucred->pid, fds))
+ if (!service_notify_message_authorized(s, ucred->pid))
return;
if (DEBUG_LOGGING) {
- _cleanup_free_ char *cc = NULL;
-
- cc = strv_join(tags, ", ");
+ _cleanup_free_ char *cc = strv_join(tags, ", ");
log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", ucred->pid, empty_to_na(cc));
}
+ usec_t monotonic_usec = USEC_INFINITY;
+ bool notify_dbus = false;
+ const char *e;
+
/* Interpret MAINPID= */
e = strv_find_startswith(tags, "MAINPID=");
- if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY)) {
+ if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING,
+ SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
+ SERVICE_STOP, SERVICE_STOP_SIGTERM)) {
+
_cleanup_(pidref_done) PidRef new_main_pid = PIDREF_NULL;
r = pidref_set_pidstr(&new_main_pid, e);
@@ -4384,10 +4375,10 @@ static void service_notify_message(
log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid.pid);
r = 1;
} else
- log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid);
+ log_unit_warning(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid);
}
if (r > 0) {
- (void) service_set_main_pidref(s, &new_main_pid);
+ (void) service_set_main_pidref(s, TAKE_PIDREF(new_main_pid), /* start_timestamp = */ NULL);
r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
if (r < 0)
@@ -4585,11 +4576,36 @@ static void service_notify_message(
unit_add_to_dbus_queue(u);
}
+static void service_handoff_timestamp(
+ Unit *u,
+ const struct ucred *ucred,
+ const dual_timestamp *ts) {
+
+ Service *s = ASSERT_PTR(SERVICE(u));
+
+ assert(ucred);
+ assert(ts);
+
+ if (s->main_pid.pid == ucred->pid) {
+ if (s->main_command)
+ exec_status_handoff(&s->main_command->exec_status, ucred, ts);
+
+ exec_status_handoff(&s->main_exec_status, ucred, ts);
+ } else if (s->control_pid.pid == ucred->pid && s->control_command)
+ exec_status_handoff(&s->control_command->exec_status, ucred, ts);
+ else
+ return;
+
+ unit_add_to_dbus_queue(u);
+}
+
static int service_get_timeout(Unit *u, usec_t *timeout) {
- Service *s = SERVICE(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
uint64_t t;
int r;
+ assert(timeout);
+
if (!s->timer_event_source)
return 0;
@@ -4604,7 +4620,7 @@ static int service_get_timeout(Unit *u, usec_t *timeout) {
}
static usec_t service_get_timeout_start_usec(Unit *u) {
- Service *s = SERVICE(ASSERT_PTR(u));
+ Service *s = ASSERT_PTR(SERVICE(u));
return s->timeout_start_usec;
}
@@ -4624,16 +4640,14 @@ static bool pick_up_pid_from_bus_name(Service *s) {
}
static int bus_name_pid_lookup_callback(sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
+ Service *s = ASSERT_PTR(SERVICE(userdata));
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
const sd_bus_error *e;
- Unit *u = ASSERT_PTR(userdata);
uint32_t pid;
- Service *s;
int r;
assert(reply);
- s = SERVICE(u);
s->bus_name_pid_lookup_slot = sd_bus_slot_unref(s->bus_name_pid_lookup_slot);
if (!s->bus_name || !pick_up_pid_from_bus_name(s))
@@ -4658,20 +4672,17 @@ static int bus_name_pid_lookup_callback(sd_bus_message *reply, void *userdata, s
return 1;
}
- log_unit_debug(u, "D-Bus name %s is now owned by process " PID_FMT, s->bus_name, pidref.pid);
+ log_unit_debug(UNIT(s), "D-Bus name %s is now owned by process " PID_FMT, s->bus_name, pidref.pid);
- (void) service_set_main_pidref(s, &pidref);
+ (void) service_set_main_pidref(s, TAKE_PIDREF(pidref), /* start_timestamp = */ NULL);
(void) unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
return 1;
}
static void service_bus_name_owner_change(Unit *u, const char *new_owner) {
-
- Service *s = SERVICE(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
int r;
- assert(s);
-
if (new_owner)
log_unit_debug(u, "D-Bus name %s now owned by %s", s->bus_name, new_owner);
else
@@ -4721,7 +4732,7 @@ int service_set_socket_fd(
Service *s,
int fd,
Socket *sock,
- SocketPeer *peer,
+ SocketPeer *peer, /* reference to object is donated to us on success */
bool selinux_context_net) {
_cleanup_free_ char *peer_text = NULL;
@@ -4729,6 +4740,7 @@ int service_set_socket_fd(
assert(s);
assert(fd >= 0);
+ assert(sock);
/* This is called by the socket code when instantiating a new service for a stream socket and the socket needs
* to be configured. We take ownership of the passed fd on success. */
@@ -4760,12 +4772,13 @@ int service_set_socket_fd(
return r;
}
- r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false, UNIT_DEPENDENCY_IMPLICIT);
+ r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_TRIGGERED_BY, UNIT(sock), false, UNIT_DEPENDENCY_IMPLICIT);
if (r < 0)
- return r;
+ return log_unit_debug_errno(UNIT(s), r,
+ "Failed to add After=/TriggeredBy= dependencies on socket unit: %m");
s->socket_fd = fd;
- s->socket_peer = socket_peer_ref(peer);
+ s->socket_peer = peer;
s->socket_fd_selinux_context_net = selinux_context_net;
unit_ref_set(&s->accept_socket, UNIT(s), UNIT(sock));
@@ -4773,9 +4786,7 @@ int service_set_socket_fd(
}
static void service_reset_failed(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
+ Service *s = ASSERT_PTR(SERVICE(u));
if (s->state == SERVICE_FAILED)
service_set_state(s, service_determine_dead_state(s));
@@ -4787,8 +4798,13 @@ static void service_reset_failed(Unit *u) {
s->flush_n_restarts = false;
}
-static PidRef* service_main_pid(Unit *u) {
- return &ASSERT_PTR(SERVICE(u))->main_pid;
+static PidRef* service_main_pid(Unit *u, bool *ret_is_alien) {
+ Service *s = ASSERT_PTR(SERVICE(u));
+
+ if (ret_is_alien)
+ *ret_is_alien = s->main_pid_alien;
+
+ return &s->main_pid;
}
static PidRef* service_control_pid(Unit *u) {
@@ -4796,9 +4812,7 @@ static PidRef* service_control_pid(Unit *u) {
}
static bool service_needs_console(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
+ Service *s = ASSERT_PTR(SERVICE(u));
/* We provide our own implementation of this here, instead of relying of the generic implementation
* unit_needs_console() provides, since we want to return false if we are in SERVICE_EXITED state. */
@@ -4826,9 +4840,7 @@ static bool service_needs_console(Unit *u) {
}
static int service_exit_status(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
if (s->main_exec_status.pid <= 0 ||
!dual_timestamp_is_set(&s->main_exec_status.exit_timestamp))
@@ -4841,20 +4853,17 @@ static int service_exit_status(Unit *u) {
}
static const char* service_status_text(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
+ Service *s = ASSERT_PTR(SERVICE(u));
return s->status_text;
}
static int service_clean(Unit *u, ExecCleanMask mask) {
+ Service *s = ASSERT_PTR(SERVICE(u));
_cleanup_strv_free_ char **l = NULL;
bool may_clean_fdstore = false;
- Service *s = SERVICE(u);
int r;
- assert(s);
assert(mask != 0);
if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_DEAD_RESOURCES_PINNED))
@@ -4910,11 +4919,10 @@ fail:
}
static int service_can_clean(Unit *u, ExecCleanMask *ret) {
- Service *s = SERVICE(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
ExecCleanMask mask = 0;
int r;
- assert(s);
assert(ret);
r = exec_context_get_clean_mask(&s->exec_context, &mask);
@@ -4928,10 +4936,12 @@ static int service_can_clean(Unit *u, ExecCleanMask *ret) {
return 0;
}
-static const char *service_finished_job(Unit *u, JobType t, JobResult result) {
+static const char* service_finished_job(Unit *u, JobType t, JobResult result) {
+ Service *s = ASSERT_PTR(SERVICE(u));
+
if (t == JOB_START &&
result == JOB_DONE &&
- SERVICE(u)->type == SERVICE_ONESHOT)
+ s->type == SERVICE_ONESHOT)
return "Finished %s.";
/* Fall back to generic */
@@ -4939,11 +4949,9 @@ static const char *service_finished_job(Unit *u, JobType t, JobResult result) {
}
static int service_can_start(Unit *u) {
- Service *s = SERVICE(u);
+ Service *s = ASSERT_PTR(SERVICE(u));
int r;
- assert(s);
-
/* Make sure we don't enter a busy loop of some kind. */
r = unit_test_start_limit(u);
if (r < 0) {
@@ -4955,7 +4963,7 @@ static int service_can_start(Unit *u) {
}
static void service_release_resources(Unit *u) {
- Service *s = SERVICE(ASSERT_PTR(u));
+ Service *s = ASSERT_PTR(SERVICE(u));
/* Invoked by the unit state engine, whenever it realizes that unit is dead and there's no job
* anymore for it, and it hence is a good idea to release resources */
@@ -4978,6 +4986,52 @@ static void service_release_resources(Unit *u) {
service_set_state(s, SERVICE_DEAD);
}
+int service_determine_exec_selinux_label(Service *s, char **ret) {
+ int r;
+
+ assert(s);
+ assert(ret);
+
+ if (!mac_selinux_use())
+ return -ENODATA;
+
+ /* Returns the SELinux label used for execution of the main service binary */
+
+ if (s->exec_context.selinux_context)
+ /* Prefer the explicitly configured label if there is one */
+ return strdup_to(ret, s->exec_context.selinux_context);
+
+ if (s->exec_context.root_image ||
+ s->exec_context.n_extension_images > 0 ||
+ !strv_isempty(s->exec_context.extension_directories)) /* We cannot chase paths through images */
+ return log_unit_debug_errno(UNIT(s), SYNTHETIC_ERRNO(ENODATA), "Service with RootImage=, ExtensionImages= or ExtensionDirectories= set, cannot determine socket SELinux label before activation, ignoring.");
+
+ ExecCommand *c = s->exec_command[SERVICE_EXEC_START];
+ if (!c)
+ return -ENODATA;
+
+ _cleanup_free_ char *path = NULL;
+ r = chase(c->path, s->exec_context.root_directory, CHASE_PREFIX_ROOT, &path, NULL);
+ if (r < 0) {
+ log_unit_debug_errno(UNIT(s), r, "Failed to resolve service binary '%s', ignoring.", c->path);
+ return -ENODATA;
+ }
+
+ r = mac_selinux_get_create_label_from_exe(path, ret);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) {
+ log_unit_debug_errno(UNIT(s), r, "Reading SELinux label off binary '%s' is not supported, ignoring.", path);
+ return -ENODATA;
+ }
+ if (ERRNO_IS_NEG_PRIVILEGE(r)) {
+ log_unit_debug_errno(UNIT(s), r, "Can't read SELinux label off binary '%s', due to privileges, ignoring.", path);
+ return -ENODATA;
+ }
+ if (r < 0)
+ return log_unit_debug_errno(UNIT(s), r, "Failed to read SELinux label off binary '%s': %m", path);
+
+ return 0;
+}
+
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
@@ -4992,7 +5046,7 @@ DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);
static const char* const service_restart_mode_table[_SERVICE_RESTART_MODE_MAX] = {
[SERVICE_RESTART_MODE_NORMAL] = "normal",
- [SERVICE_RESTART_MODE_DIRECT] = "direct",
+ [SERVICE_RESTART_MODE_DIRECT] = "direct",
};
DEFINE_STRING_TABLE_LOOKUP(service_restart_mode, ServiceRestartMode);
@@ -5080,6 +5134,7 @@ const UnitVTable service_vtable = {
.cgroup_context_offset = offsetof(Service, cgroup_context),
.kill_context_offset = offsetof(Service, kill_context),
.exec_runtime_offset = offsetof(Service, exec_runtime),
+ .cgroup_runtime_offset = offsetof(Service, cgroup_runtime),
.sections =
"Unit\0"
@@ -5110,8 +5165,7 @@ const UnitVTable service_vtable = {
.clean = service_clean,
.can_clean = service_can_clean,
- .freeze = unit_freeze_vtable_common,
- .thaw = unit_thaw_vtable_common,
+ .freezer_action = unit_cgroup_freezer_action,
.serialize = service_serialize,
.deserialize_item = service_deserialize_item,
@@ -5130,6 +5184,7 @@ const UnitVTable service_vtable = {
.notify_cgroup_empty = service_notify_cgroup_empty_event,
.notify_cgroup_oom = service_notify_cgroup_oom_event,
.notify_message = service_notify_message,
+ .notify_handoff_timestamp = service_handoff_timestamp,
.main_pid = service_main_pid,
.control_pid = service_control_pid,