diff options
Diffstat (limited to 'src/core/manager.c')
-rw-r--r-- | src/core/manager.c | 671 |
1 files changed, 401 insertions, 270 deletions
diff --git a/src/core/manager.c b/src/core/manager.c index 88eebfc..90e72b0 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -9,7 +9,6 @@ #include <sys/mount.h> #include <sys/reboot.h> #include <sys/timerfd.h> -#include <sys/utsname.h> #include <sys/wait.h> #include <unistd.h> @@ -25,6 +24,7 @@ #include "alloc-util.h" #include "audit-fd.h" #include "boot-timestamps.h" +#include "build-path.h" #include "bus-common-errors.h" #include "bus-error.h" #include "bus-kernel.h" @@ -36,6 +36,7 @@ #include "constants.h" #include "core-varlink.h" #include "creds-util.h" +#include "daemon-util.h" #include "dbus-job.h" #include "dbus-manager.h" #include "dbus-unit.h" @@ -55,6 +56,7 @@ #include "inotify-util.h" #include "install.h" #include "io-util.h" +#include "iovec-util.h" #include "label-util.h" #include "load-fragment.h" #include "locale-setup.h" @@ -88,6 +90,7 @@ #include "strxcpyx.h" #include "sysctl-util.h" #include "syslog-util.h" +#include "taint.h" #include "terminal-util.h" #include "time-util.h" #include "transaction.h" @@ -122,6 +125,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int manager_dispatch_handoff_timestamp_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); static int manager_dispatch_sigchld(sd_event_source *source, void *userdata); @@ -263,12 +267,11 @@ static void manager_print_jobs_in_progress(Manager *m) { strempty(status_text)); } - sd_notifyf(false, - "STATUS=%sUser job %s/%s running (%s / %s)...", - job_of_n, - ident, - job_type_to_string(j->type), - time, limit); + (void) sd_notifyf(/* unset_environment= */ false, + "STATUS=%sUser job %s/%s running (%s / %s)...", + job_of_n, + ident, job_type_to_string(j->type), + time, limit); m->status_ready = false; } @@ -397,7 +400,7 @@ static int manager_setup_time_change(Manager *m) { return log_error_errno(r, "Failed to create time change event source: %m"); /* Schedule this slightly earlier than the .timer event sources */ - r = sd_event_source_set_priority(m->time_change_event_source, SD_EVENT_PRIORITY_NORMAL-1); + r = sd_event_source_set_priority(m->time_change_event_source, EVENT_PRIORITY_TIME_CHANGE); if (r < 0) return log_error_errno(r, "Failed to set priority of time change event sources: %m"); @@ -464,7 +467,7 @@ static int manager_setup_timezone_change(Manager *m) { return log_error_errno(r, "Failed to create timezone change event source: %m"); /* Schedule this slightly earlier than the .timer event sources */ - r = sd_event_source_set_priority(new_event, SD_EVENT_PRIORITY_NORMAL-1); + r = sd_event_source_set_priority(new_event, EVENT_PRIORITY_TIME_ZONE); if (r < 0) return log_error_errno(r, "Failed to set priority of timezone change event sources: %m"); @@ -482,21 +485,19 @@ static int enable_special_signals(Manager *m) { if (MANAGER_IS_TEST_RUN(m)) return 0; - /* Enable that we get SIGINT on control-alt-del. In containers - * this will fail with EPERM (older) or EINVAL (newer), so - * ignore that. */ + /* Enable that we get SIGINT on control-alt-del. In containers this will fail with EPERM (older) or + * EINVAL (newer), so ignore that. */ if (reboot(RB_DISABLE_CAD) < 0 && !IN_SET(errno, EPERM, EINVAL)) - log_warning_errno(errno, "Failed to enable ctrl-alt-del handling: %m"); + log_warning_errno(errno, "Failed to enable ctrl-alt-del handling, ignoring: %m"); fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) { - /* Support systems without virtual console */ - if (fd != -ENOENT) - log_warning_errno(errno, "Failed to open /dev/tty0: %m"); - } else { + if (fd < 0) + /* Support systems without virtual console (ENOENT) gracefully */ + log_full_errno(fd == -ENOENT ? LOG_DEBUG : LOG_WARNING, fd, "Failed to open /dev/tty0, ignoring: %m"); + else { /* Enable that we get SIGWINCH on kbrequest */ if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0) - log_warning_errno(errno, "Failed to enable kbrequest handling: %m"); + log_warning_errno(errno, "Failed to enable kbrequest handling, ignoring: %m"); } return 0; @@ -592,10 +593,21 @@ static int manager_setup_signals(Manager *m) { * notify processing can still figure out to which process/service a message belongs, before we reap the * process. Also, process this before handling cgroup notifications, so that we always collect child exit * status information before detecting that there's no process in a cgroup. */ - r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-6); + r = sd_event_source_set_priority(m->signal_event_source, EVENT_PRIORITY_SIGNALS); if (r < 0) return r; + /* Report to supervisor that we now process the above signals. We report this as level "2", to + * indicate that we support more than sysvinit's signals (of course, sysvinit never sent this + * message, but conceptually it makes sense to consider level "1" to be equivalent to sysvinit's + * signal handling). Also, by setting this to "2" people looking for this hopefully won't + * misunderstand this as a boolean concept. Signal level 2 shall refer to the signals PID 1 + * understands at the time of release of systemd v256, i.e. including basic SIGRTMIN+18 handling for + * memory pressure and stuff. When more signals are hooked up (or more SIGRTMIN+18 multiplex + * operations added, this level should be increased). */ + (void) sd_notify(/* unset_environment= */ false, + "X_SYSTEMD_SIGNALS_LEVEL=2"); + if (MANAGER_IS_SYSTEM(m)) return enable_special_signals(m); @@ -641,16 +653,13 @@ static char** sanitize_environment(char **l) { "TRIGGER_TIMER_REALTIME_USEC", "TRIGGER_UNIT", "WATCHDOG_PID", - "WATCHDOG_USEC", - NULL); + "WATCHDOG_USEC"); /* Let's order the environment alphabetically, just to make it pretty */ return strv_sort(l); } int manager_default_environment(Manager *m) { - int r; - assert(m); m->transient_environment = strv_free(m->transient_environment); @@ -661,21 +670,39 @@ int manager_default_environment(Manager *m) { * * The initial passed environment is untouched to keep /proc/self/environ valid; it is used * for tagging the init process inside containers. */ - m->transient_environment = strv_new("PATH=" DEFAULT_PATH); - if (!m->transient_environment) + char *path = strjoin("PATH=", default_PATH()); + if (!path) + return log_oom(); + + if (strv_consume(&m->transient_environment, path) < 0) return log_oom(); /* Import locale variables LC_*= from configuration */ (void) locale_setup(&m->transient_environment); } else { - /* The user manager passes its own environment along to its children, except for $PATH. */ + /* The user manager passes its own environment along to its children, except for $PATH and + * session envs. */ + m->transient_environment = strv_copy(environ); if (!m->transient_environment) return log_oom(); - r = strv_env_replace_strdup(&m->transient_environment, "PATH=" DEFAULT_USER_PATH); - if (r < 0) + char *path = strjoin("PATH=", default_user_PATH()); + if (!path) + return log_oom(); + + if (strv_env_replace_consume(&m->transient_environment, path) < 0) return log_oom(); + + /* Envvars set for our 'manager' class session are private and should not be propagated + * to children. Also it's likely that the graphical session will set these on their own. */ + strv_env_unset_many(m->transient_environment, + "XDG_SESSION_ID", + "XDG_SESSION_CLASS", + "XDG_SESSION_TYPE", + "XDG_SESSION_DESKTOP", + "XDG_SEAT", + "XDG_VTNR"); } sanitize_environment(m->transient_environment); @@ -689,18 +716,18 @@ static int manager_setup_prefix(Manager *m) { }; static const struct table_entry paths_system[_EXEC_DIRECTORY_TYPE_MAX] = { - [EXEC_DIRECTORY_RUNTIME] = { SD_PATH_SYSTEM_RUNTIME, NULL }, - [EXEC_DIRECTORY_STATE] = { SD_PATH_SYSTEM_STATE_PRIVATE, NULL }, - [EXEC_DIRECTORY_CACHE] = { SD_PATH_SYSTEM_STATE_CACHE, NULL }, - [EXEC_DIRECTORY_LOGS] = { SD_PATH_SYSTEM_STATE_LOGS, NULL }, + [EXEC_DIRECTORY_RUNTIME] = { SD_PATH_SYSTEM_RUNTIME, NULL }, + [EXEC_DIRECTORY_STATE] = { SD_PATH_SYSTEM_STATE_PRIVATE, NULL }, + [EXEC_DIRECTORY_CACHE] = { SD_PATH_SYSTEM_STATE_CACHE, NULL }, + [EXEC_DIRECTORY_LOGS] = { SD_PATH_SYSTEM_STATE_LOGS, NULL }, [EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_SYSTEM_CONFIGURATION, NULL }, }; static const struct table_entry paths_user[_EXEC_DIRECTORY_TYPE_MAX] = { - [EXEC_DIRECTORY_RUNTIME] = { SD_PATH_USER_RUNTIME, NULL }, - [EXEC_DIRECTORY_STATE] = { SD_PATH_USER_STATE_PRIVATE, NULL }, - [EXEC_DIRECTORY_CACHE] = { SD_PATH_USER_STATE_CACHE, NULL }, - [EXEC_DIRECTORY_LOGS] = { SD_PATH_USER_STATE_PRIVATE, "log" }, + [EXEC_DIRECTORY_RUNTIME] = { SD_PATH_USER_RUNTIME, NULL }, + [EXEC_DIRECTORY_STATE] = { SD_PATH_USER_STATE_PRIVATE, NULL }, + [EXEC_DIRECTORY_CACHE] = { SD_PATH_USER_STATE_CACHE, NULL }, + [EXEC_DIRECTORY_LOGS] = { SD_PATH_USER_STATE_PRIVATE, "log" }, [EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_USER_CONFIGURATION, NULL }, }; @@ -736,7 +763,7 @@ static int manager_setup_run_queue(Manager *m) { if (r < 0) return r; - r = sd_event_source_set_priority(m->run_queue_event_source, SD_EVENT_PRIORITY_IDLE); + r = sd_event_source_set_priority(m->run_queue_event_source, EVENT_PRIORITY_RUN_QUEUE); if (r < 0) return r; @@ -759,7 +786,7 @@ static int manager_setup_sigchld_event_source(Manager *m) { if (r < 0) return r; - r = sd_event_source_set_priority(m->sigchld_event_source, SD_EVENT_PRIORITY_NORMAL-7); + r = sd_event_source_set_priority(m->sigchld_event_source, EVENT_PRIORITY_SIGCHLD); if (r < 0) return r; @@ -861,6 +888,7 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags, *m = (Manager) { .runtime_scope = runtime_scope, .objective = _MANAGER_OBJECTIVE_INVALID, + .previous_objective = _MANAGER_OBJECTIVE_INVALID, .status_unit_format = STATUS_UNIT_FORMAT_DEFAULT, @@ -878,6 +906,7 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags, .cgroups_agent_fd = -EBADF, .signal_fd = -EBADF, .user_lookup_fds = EBADF_PAIR, + .handoff_timestamp_fds = EBADF_PAIR, .private_listen_fd = -EBADF, .dev_autofs_fd = -EBADF, .cgroup_inotify_fd = -EBADF, @@ -992,8 +1021,8 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags, return r; #if HAVE_LIBBPF - if (MANAGER_IS_SYSTEM(m) && lsm_bpf_supported(/* initialize = */ true)) { - r = lsm_bpf_setup(m); + if (MANAGER_IS_SYSTEM(m) && bpf_restrict_fs_supported(/* initialize = */ true)) { + r = bpf_restrict_fs_setup(m); if (r < 0) log_warning_errno(r, "Failed to setup LSM BPF, ignoring: %m"); } @@ -1013,42 +1042,19 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags, if (r < 0 && r != -EEXIST) return r; + } - m->executor_fd = open(SYSTEMD_EXECUTOR_BINARY_PATH, O_CLOEXEC|O_PATH); - if (m->executor_fd < 0) - return log_emergency_errno(errno, - "Failed to open executor binary '%s': %m", - SYSTEMD_EXECUTOR_BINARY_PATH); - } else if (!FLAGS_SET(test_run_flags, MANAGER_TEST_DONT_OPEN_EXECUTOR)) { - _cleanup_free_ char *self_exe = NULL, *executor_path = NULL; - _cleanup_close_ int self_dir_fd = -EBADF; - int level = LOG_DEBUG; - - /* Prefer sd-executor from the same directory as the test, e.g.: when running unit tests from the - * build directory. Fallback to working directory and then the installation path. */ - r = readlink_and_make_absolute("/proc/self/exe", &self_exe); - if (r < 0) - return r; - - self_dir_fd = open_parent(self_exe, O_CLOEXEC|O_PATH|O_DIRECTORY, 0); - if (self_dir_fd < 0) - return self_dir_fd; - - m->executor_fd = RET_NERRNO(openat(self_dir_fd, "systemd-executor", O_CLOEXEC|O_PATH)); - if (m->executor_fd == -ENOENT) - m->executor_fd = RET_NERRNO(openat(AT_FDCWD, "systemd-executor", O_CLOEXEC|O_PATH)); - if (m->executor_fd == -ENOENT) { - m->executor_fd = RET_NERRNO(open(SYSTEMD_EXECUTOR_BINARY_PATH, O_CLOEXEC|O_PATH)); - level = LOG_WARNING; /* Tests should normally use local builds */ - } + if (!FLAGS_SET(test_run_flags, MANAGER_TEST_DONT_OPEN_EXECUTOR)) { + m->executor_fd = pin_callout_binary(SYSTEMD_EXECUTOR_BINARY_PATH); if (m->executor_fd < 0) - return m->executor_fd; + return log_debug_errno(m->executor_fd, "Failed to pin executor binary: %m"); + _cleanup_free_ char *executor_path = NULL; r = fd_get_path(m->executor_fd, &executor_path); if (r < 0) return r; - log_full(level, "Using systemd-executor binary from '%s'.", executor_path); + log_debug("Using systemd-executor binary from '%s'.", executor_path); } /* Note that we do not set up the notify fd here. We do that after deserialization, @@ -1113,7 +1119,7 @@ static int manager_setup_notify(Manager *m) { /* Process notification messages a bit earlier than SIGCHLD, so that we can still identify to which * service an exit message belongs. */ - r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-8); + r = sd_event_source_set_priority(m->notify_event_source, EVENT_PRIORITY_NOTIFY); if (r < 0) return log_error_errno(r, "Failed to set priority of notify event source: %m"); @@ -1187,7 +1193,7 @@ static int manager_setup_cgroups_agent(Manager *m) { /* Process cgroups notifications early. Note that when the agent notification is received * we'll just enqueue the unit in the cgroup empty queue, hence pick a high priority than * that. Also see handling of cgroup inotify for the unified cgroup stuff. */ - r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-9); + r = sd_event_source_set_priority(m->cgroups_agent_event_source, EVENT_PRIORITY_CGROUP_AGENT); if (r < 0) return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m"); @@ -1236,13 +1242,13 @@ static int manager_setup_user_lookup_fd(Manager *m) { if (!m->user_lookup_event_source) { r = sd_event_add_io(m->event, &m->user_lookup_event_source, m->user_lookup_fds[0], EPOLLIN, manager_dispatch_user_lookup_fd, m); if (r < 0) - return log_error_errno(errno, "Failed to allocate user lookup event source: %m"); + return log_error_errno(r, "Failed to allocate user lookup event source: %m"); /* Process even earlier than the notify event source, so that we always know first about valid UID/GID * resolutions */ - r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-11); + r = sd_event_source_set_priority(m->user_lookup_event_source, EVENT_PRIORITY_USER_LOOKUP); if (r < 0) - return log_error_errno(errno, "Failed to set priority of user lookup event source: %m"); + return log_error_errno(r, "Failed to set priority of user lookup event source: %m"); (void) sd_event_source_set_description(m->user_lookup_event_source, "user-lookup"); } @@ -1250,6 +1256,49 @@ static int manager_setup_user_lookup_fd(Manager *m) { return 0; } +static int manager_setup_handoff_timestamp_fd(Manager *m) { + int r; + + assert(m); + + /* Set up the socket pair used for passing timestamps back when the executor processes we fork + * off invokes execve(), i.e. when we hand off control to our payload processes. */ + + if (m->handoff_timestamp_fds[0] < 0) { + m->handoff_timestamp_event_source = sd_event_source_disable_unref(m->handoff_timestamp_event_source); + safe_close_pair(m->handoff_timestamp_fds); + + if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, m->handoff_timestamp_fds) < 0) + return log_error_errno(errno, "Failed to allocate handoff timestamp socket: %m"); + + /* Make sure children never have to block */ + (void) fd_increase_rxbuf(m->handoff_timestamp_fds[0], NOTIFY_RCVBUF_SIZE); + + r = setsockopt_int(m->handoff_timestamp_fds[0], SOL_SOCKET, SO_PASSCRED, true); + if (r < 0) + return log_error_errno(r, "SO_PASSCRED failed: %m"); + + /* Mark the receiving socket as O_NONBLOCK (but leave sending side as-is) */ + r = fd_nonblock(m->handoff_timestamp_fds[0], true); + if (r < 0) + return log_error_errno(r, "Failed to make handoff timestamp socket O_NONBLOCK: %m"); + } + + if (!m->handoff_timestamp_event_source) { + r = sd_event_add_io(m->event, &m->handoff_timestamp_event_source, m->handoff_timestamp_fds[0], EPOLLIN, manager_dispatch_handoff_timestamp_fd, m); + if (r < 0) + return log_error_errno(r, "Failed to allocate handoff timestamp event source: %m"); + + r = sd_event_source_set_priority(m->handoff_timestamp_event_source, EVENT_PRIORITY_HANDOFF_TIMESTAMP); + if (r < 0) + return log_error_errno(r, "Failed to set priority of handoff timestamp event source: %m"); + + (void) sd_event_source_set_description(m->handoff_timestamp_event_source, "handoff-timestamp"); + } + + return 0; +} + static unsigned manager_dispatch_cleanup_queue(Manager *m) { Unit *u; unsigned n = 0; @@ -1664,12 +1713,14 @@ Manager* manager_free(Manager *m) { sd_event_source_unref(m->jobs_in_progress_event_source); sd_event_source_unref(m->run_queue_event_source); sd_event_source_unref(m->user_lookup_event_source); + sd_event_source_unref(m->handoff_timestamp_event_source); sd_event_source_unref(m->memory_pressure_event_source); safe_close(m->signal_fd); safe_close(m->notify_fd); safe_close(m->cgroups_agent_fd); safe_close_pair(m->user_lookup_fds); + safe_close_pair(m->handoff_timestamp_fds); manager_close_ask_password(m); @@ -1679,7 +1730,7 @@ Manager* manager_free(Manager *m) { free(m->notify_socket); - lookup_paths_free(&m->lookup_paths); + lookup_paths_done(&m->lookup_paths); strv_free(m->transient_environment); strv_free(m->client_environment); @@ -1691,8 +1742,10 @@ Manager* manager_free(Manager *m) { unit_defaults_done(&m->defaults); - assert(hashmap_isempty(m->units_requiring_mounts_for)); - hashmap_free(m->units_requiring_mounts_for); + FOREACH_ARRAY(map, m->units_needing_mounts_for, _UNIT_MOUNT_DEPENDENCY_TYPE_MAX) { + assert(hashmap_isempty(*map)); + hashmap_free(*map); + } hashmap_free(m->uid_refs); hashmap_free(m->gid_refs); @@ -1708,7 +1761,7 @@ Manager* manager_free(Manager *m) { m->fw_ctx = fw_ctx_free(m->fw_ctx); #if BPF_FRAMEWORK - lsm_bpf_destroy(m->restrict_fs); + bpf_restrict_fs_destroy(m->restrict_fs); #endif safe_close(m->executor_fd); @@ -1802,7 +1855,7 @@ static void manager_distribute_fds(Manager *m, FDSet *fds) { HASHMAP_FOREACH(u, m->units) { - if (fdset_size(fds) <= 0) + if (fdset_isempty(fds)) break; if (!UNIT_VTABLE(u)->distribute_fds) @@ -1973,6 +2026,20 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *roo return log_error_errno(r, "Deserialization failed: %m"); } + if (m->previous_objective >= 0) { + if (IN_SET(m->previous_objective, MANAGER_REEXECUTE, MANAGER_SOFT_REBOOT, MANAGER_SWITCH_ROOT)) + log_debug("Launching as effect of a '%s' operation.", + manager_objective_to_string(m->previous_objective)); + else + log_warning("Got unexpected previous objective '%s', ignoring.", + manager_objective_to_string(m->previous_objective)); + } + + /* If we are in a new soft-reboot iteration bump the counter now before starting units, so + * that they can reliably read it. We get the previous objective from serialized state. */ + if (m->previous_objective == MANAGER_SOFT_REBOOT) + m->soft_reboots_count++; + /* Any fds left? Find some unit which wants them. This is useful to allow container managers to pass * some file descriptors to us pre-initialized. This enables socket-based activation of entire * containers. */ @@ -1994,6 +2061,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *roo /* This shouldn't fail, except if things are really broken. */ return r; + r = manager_setup_handoff_timestamp_fd(m); + if (r < 0) + /* This shouldn't fail, except if things are really broken. */ + return r; + /* Connect to the bus if we are good for it */ manager_setup_bus(m); @@ -2203,8 +2275,8 @@ static int manager_dispatch_target_deps_queue(Manager *m) { if (n_targets < 0) return n_targets; - for (int i = 0; i < n_targets; i++) { - r = unit_add_default_target_dependency(u, targets[i]); + FOREACH_ARRAY(i, targets, n_targets) { + r = unit_add_default_target_dependency(u, *i); if (r < 0) return r; } @@ -2303,7 +2375,7 @@ int manager_load_unit_prepare( Unit *unit = manager_get_unit(m, name); if (unit) { - /* The time-based cache allows to start new units without daemon-reload, + /* The time-based cache allows new units to be started without daemon-reload, * but if they are already referenced (because of dependencies or ordering) * then we have to force a load of the fragment. As an optimization, check * first if anything in the usual paths was modified since the last time @@ -2403,7 +2475,7 @@ void manager_clear_jobs(Manager *m) { job_finish_and_invalidate(j, JOB_CANCELED, false, false); } -void manager_unwatch_pidref(Manager *m, PidRef *pid) { +void manager_unwatch_pidref(Manager *m, const PidRef *pid) { assert(m); for (;;) { @@ -2586,22 +2658,70 @@ static void manager_invoke_notify_message( UNIT_VTABLE(u)->notify_message(u, ucred, tags, fds); else if (DEBUG_LOGGING) { - _cleanup_free_ char *buf = NULL, *x = NULL, *y = NULL; + _cleanup_free_ char *joined = strv_join(tags, ", "); + char buf[CELLESCAPE_DEFAULT_LENGTH]; + + log_unit_debug(u, "Got notification message from unexpected unit type, ignoring: %s", + joined ? cellescape(buf, sizeof(buf), joined) : "(null)"); + } +} + +static int manager_get_units_for_pidref(Manager *m, const PidRef *pidref, Unit ***ret_units) { + /* Determine array of every unit that is interested in the specified process */ + + assert(m); + assert(pidref_is_set(pidref)); - buf = strv_join(tags, ", "); - if (buf) - x = ellipsize(buf, 20, 90); - if (x) - y = cescape(x); + Unit *u1, *u2, **array; + u1 = manager_get_unit_by_pidref_cgroup(m, pidref); + u2 = hashmap_get(m->watch_pids, pidref); + array = hashmap_get(m->watch_pids_more, pidref); + + size_t n = 0; + if (u1) + n++; + if (u2) + n++; + if (array) + for (size_t j = 0; array[j]; j++) + n++; + + assert(n <= INT_MAX); /* Make sure we can reasonably return the counter as "int" */ + + if (ret_units) { + _cleanup_free_ Unit **units = NULL; + + if (n > 0) { + units = new(Unit*, n + 1); + if (!units) + return -ENOMEM; + + /* We return a dense array, and put the "main" unit first, i.e. unit in whose cgroup + * the process currently is. Note that we do not bother with filtering duplicates + * here. */ + + size_t i = 0; + if (u1) + units[i++] = u1; + if (u2) + units[i++] = u2; + if (array) + for (size_t j = 0; array[j]; j++) + units[i++] = array[j]; + assert(i == n); + + units[i] = NULL; /* end array in an extra NULL */ + } - log_unit_debug(u, "Got notification message \"%s\", ignoring.", strnull(y)); + *ret_units = TAKE_PTR(units); } + + return (int) n; } static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - - _cleanup_fdset_free_ FDSet *fds = NULL; Manager *m = ASSERT_PTR(userdata); + _cleanup_fdset_free_ FDSet *fds = NULL; char buf[NOTIFY_BUFFER_MAX+1]; struct iovec iovec = { .iov_base = buf, @@ -2618,12 +2738,9 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t struct cmsghdr *cmsg; struct ucred *ucred = NULL; - _cleanup_free_ Unit **array_copy = NULL; _cleanup_strv_free_ char **tags = NULL; - Unit *u1, *u2, **array; int r, *fd_array = NULL; size_t n_fds = 0; - bool found = false; ssize_t n; assert(m->notify_fd == fd); @@ -2711,39 +2828,22 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t PidRef pidref = PIDREF_MAKE_FROM_PID(ucred->pid); /* Notify every unit that might be interested, which might be multiple. */ - u1 = manager_get_unit_by_pidref_cgroup(m, &pidref); - u2 = hashmap_get(m->watch_pids, &pidref); - array = hashmap_get(m->watch_pids_more, &pidref); - if (array) { - size_t k = 0; + _cleanup_free_ Unit **array = NULL; - while (array[k]) - k++; - - array_copy = newdup(Unit*, array, k+1); - if (!array_copy) - log_oom(); - } - /* And now invoke the per-unit callbacks. Note that manager_invoke_notify_message() will handle - * duplicate units make sure we only invoke each unit's handler once. */ - if (u1) { - manager_invoke_notify_message(m, u1, ucred, tags, fds); - found = true; - } - if (u2) { - manager_invoke_notify_message(m, u2, ucred, tags, fds); - found = true; + int n_array = manager_get_units_for_pidref(m, &pidref, &array); + if (n_array < 0) { + log_warning_errno(n_array, "Failed to determine units for PID " PID_FMT ", ignoring: %m", ucred->pid); + return 0; } - if (array_copy) - for (size_t i = 0; array_copy[i]; i++) { - manager_invoke_notify_message(m, array_copy[i], ucred, tags, fds); - found = true; - } - - if (!found) - log_warning("Cannot find unit for notify message of PID "PID_FMT", ignoring.", ucred->pid); + if (n_array == 0) + log_debug("Cannot find unit for notify message of PID "PID_FMT", ignoring.", ucred->pid); + else + /* And now invoke the per-unit callbacks. Note that manager_invoke_notify_message() will handle + * duplicate units – making sure we only invoke each unit's handler once. */ + FOREACH_ARRAY(u, array, n_array) + manager_invoke_notify_message(m, *u, ucred, tags, fds); - if (fdset_size(fds) > 0) + if (!fdset_isempty(fds)) log_warning("Got extra auxiliary fds with notification message, closing them."); return 0; @@ -2792,10 +2892,7 @@ static int manager_dispatch_sigchld(sd_event_source *source, void *userdata) { goto turn_off; if (IN_SET(si.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED)) { - _cleanup_free_ Unit **array_copy = NULL; _cleanup_free_ char *name = NULL; - Unit *u1, *u2, **array; - (void) pid_get_comm(si.si_pid, &name); log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)", @@ -2813,41 +2910,27 @@ static int manager_dispatch_sigchld(sd_event_source *source, void *userdata) { * pidfd here any more even if we wanted (since the process just exited). */ PidRef pidref = PIDREF_MAKE_FROM_PID(si.si_pid); - /* And now figure out the unit this belongs to, it might be multiple... */ - u1 = manager_get_unit_by_pidref_cgroup(m, &pidref); - u2 = hashmap_get(m->watch_pids, &pidref); - array = hashmap_get(m->watch_pids_more, &pidref); - if (array) { - size_t n = 0; - - /* Count how many entries the array has */ - while (array[n]) - n++; - - /* Make a copy of the array so that we don't trip up on the array changing beneath us */ - array_copy = newdup(Unit*, array, n+1); - if (!array_copy) - log_oom(); - } - - /* Finally, execute them all. Note that u1, u2 and the array might contain duplicates, but - * that's fine, manager_invoke_sigchld_event() will ensure we only invoke the handlers once for - * each iteration. */ - if (u1) { - /* We check for oom condition, in case we got SIGCHLD before the oom notification. - * We only do this for the cgroup the PID belonged to. */ - (void) unit_check_oom(u1); + /* And now figure out the units this belongs to, there might be multiple... */ + _cleanup_free_ Unit **array = NULL; + int n_array = manager_get_units_for_pidref(m, &pidref, &array); + if (n_array < 0) + log_warning_errno(n_array, "Failed to get units for process " PID_FMT ", ignoring: %m", si.si_pid); + else if (n_array == 0) + log_debug("Got SIGCHLD for process " PID_FMT " we weren't interested in, ignoring.", si.si_pid); + else { + /* We check for an OOM condition, in case we got SIGCHLD before the OOM notification. + * We only do this for the cgroup the PID belonged to, which is the f */ + (void) unit_check_oom(array[0]); /* We check if systemd-oomd performed a kill so that we log and notify appropriately */ - (void) unit_check_oomd_kill(u1); + (void) unit_check_oomd_kill(array[0]); - manager_invoke_sigchld_event(m, u1, &si); + /* Finally, execute them all. Note that the array might contain duplicates, but that's fine, + * manager_invoke_sigchld_event() will ensure we only invoke the handlers once for each + * iteration. */ + FOREACH_ARRAY(u, array, n_array) + manager_invoke_sigchld_event(m, *u, &si); } - if (u2) - manager_invoke_sigchld_event(m, u2, &si); - if (array_copy) - for (size_t i = 0; array_copy[i]; i++) - manager_invoke_sigchld_event(m, array_copy[i], &si); } /* And now, we actually reap the zombie. */ @@ -2878,8 +2961,8 @@ static void manager_start_special(Manager *m, const char *name, JobMode mode) { log_info("Activating special unit %s...", s); - sd_notifyf(false, - "STATUS=Activating special unit %s...", s); + (void) sd_notifyf(/* unset_environment= */ false, + "STATUS=Activating special unit %s...", s); m->status_ready = false; } @@ -2986,7 +3069,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t r = manager_get_dump_string(m, /* patterns= */ NULL, &dump); if (r < 0) { - log_warning_errno(errno, "Failed to acquire manager dump: %m"); + log_warning_errno(r, "Failed to acquire manager dump: %m"); break; } @@ -3008,9 +3091,9 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t const char *target; JobMode mode; } target_table[] = { - [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE }, - [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE }, - [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE }, + [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE }, + [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE }, + [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE }, [3] = { SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY }, [4] = { SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY }, [5] = { SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY }, @@ -3077,7 +3160,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t r = manager_get_dump_jobs_string(m, /* patterns= */ NULL, " ", &dump_jobs); if (r < 0) { - log_warning_errno(errno, "Failed to acquire manager jobs dump: %m"); + log_warning_errno(r, "Failed to acquire manager jobs dump: %m"); break; } @@ -3371,16 +3454,18 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { const char *msg; int audit_fd, r; + assert(m); + assert(u); + if (!MANAGER_IS_SYSTEM(m)) return; - audit_fd = get_audit_fd(); - if (audit_fd < 0) + /* Don't generate audit events if the service was already started and we're just deserializing */ + if (MANAGER_IS_RELOADING(m)) return; - /* Don't generate audit events if the service was already - * started and we're just deserializing */ - if (MANAGER_IS_RELOADING(m)) + audit_fd = get_audit_fd(); + if (audit_fd < 0) return; r = unit_name_to_prefix_and_instance(u->id, &p); @@ -3399,21 +3484,22 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { log_warning_errno(errno, "Failed to send audit message, ignoring: %m"); } #endif - } void manager_send_unit_plymouth(Manager *m, Unit *u) { _cleanup_free_ char *message = NULL; int c, r; - /* Don't generate plymouth events if the service was already - * started and we're just deserializing */ - if (MANAGER_IS_RELOADING(m)) - return; + assert(m); + assert(u); if (!MANAGER_IS_SYSTEM(m)) return; + /* Don't generate plymouth events if the service was already started and we're just deserializing */ + if (MANAGER_IS_RELOADING(m)) + return; + if (detect_container() > 0) return; @@ -3431,6 +3517,27 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { "Failed to communicate with plymouth: %m"); } +void manager_send_unit_supervisor(Manager *m, Unit *u, bool active) { + assert(m); + assert(u); + + /* Notify a "supervisor" process about our progress, i.e. a container manager, hypervisor, or + * surrounding service manager. */ + + if (MANAGER_IS_RELOADING(m)) + return; + + if (!UNIT_VTABLE(u)->notify_supervisor) + return; + + if (in_initrd()) /* Only send these once we left the initrd */ + return; + + (void) sd_notifyf(/* unset_environment= */ false, + active ? "X_SYSTEMD_UNIT_ACTIVE=%s" : "X_SYSTEMD_UNIT_INACTIVE=%s", + u->id); +} + usec_t manager_get_watchdog(Manager *m, WatchdogType t) { assert(m); @@ -3566,7 +3673,7 @@ int manager_reload(Manager *m) { manager_clear_jobs_and_units(m); lookup_paths_flush_generator(&m->lookup_paths); - lookup_paths_free(&m->lookup_paths); + lookup_paths_done(&m->lookup_paths); exec_shared_runtime_vacuum(m); dynamic_user_vacuum(m, false); m->uid_refs = hashmap_free(m->uid_refs); @@ -3601,6 +3708,7 @@ int manager_reload(Manager *m) { (void) manager_setup_notify(m); (void) manager_setup_cgroups_agent(m); (void) manager_setup_user_lookup_fd(m); + (void) manager_setup_handoff_timestamp_fd(m); /* Third, fire things up! */ manager_coldplug(m); @@ -3645,8 +3753,6 @@ bool manager_unit_inactive_or_pending(Manager *m, const char *name) { } static void log_taint_string(Manager *m) { - _cleanup_free_ char *taint = NULL; - assert(m); if (MANAGER_IS_USER(m) || m->taint_logged) @@ -3654,7 +3760,7 @@ static void log_taint_string(Manager *m) { m->taint_logged = true; /* only check for taint once */ - taint = manager_taint_string(m); + _cleanup_free_ char *taint = taint_string(); if (isempty(taint)) return; @@ -3670,7 +3776,19 @@ static void manager_notify_finished(Manager *m) { if (MANAGER_IS_TEST_RUN(m)) return; - if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) { + if (MANAGER_IS_SYSTEM(m) && m->soft_reboots_count > 0) { + /* The soft-reboot case, where we only report data for the last reboot */ + firmware_usec = loader_usec = initrd_usec = kernel_usec = 0; + total_usec = userspace_usec = usec_sub_unsigned(m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic, + m->timestamps[MANAGER_TIMESTAMP_SHUTDOWN_START].monotonic); + + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_MESSAGE_STARTUP_FINISHED_STR, + "USERSPACE_USEC="USEC_FMT, userspace_usec, + LOG_MESSAGE("Soft-reboot finished in %s, counter is now at %u.", + FORMAT_TIMESPAN(total_usec, USEC_PER_MSEC), + m->soft_reboots_count)); + } else if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) { char buf[FORMAT_TIMESPAN_MAX + STRLEN(" (firmware) + ") + FORMAT_TIMESPAN_MAX + STRLEN(" (loader) + ")] = {}; char *p = buf; @@ -3740,7 +3858,7 @@ static void manager_notify_finished(Manager *m) { log_taint_string(m); } -static void user_manager_send_ready(Manager *m) { +static void manager_send_ready_user_scope(Manager *m) { int r; assert(m); @@ -3749,7 +3867,7 @@ static void user_manager_send_ready(Manager *m) { if (!MANAGER_IS_USER(m) || m->ready_sent) return; - r = sd_notify(false, + r = sd_notify(/* unset_environment= */ false, "READY=1\n" "STATUS=Reached " SPECIAL_BASIC_TARGET "."); if (r < 0) @@ -3759,14 +3877,19 @@ static void user_manager_send_ready(Manager *m) { m->status_ready = false; } -static void manager_send_ready(Manager *m) { +static void manager_send_ready_system_scope(Manager *m) { int r; + assert(m); + + if (!MANAGER_IS_SYSTEM(m)) + return; + + /* Skip the notification if nothing changed. */ if (m->ready_sent && m->status_ready) - /* Skip the notification if nothing changed. */ return; - r = sd_notify(false, + r = sd_notify(/* unset_environment= */ false, "READY=1\n" "STATUS=Ready."); if (r < 0) @@ -3790,7 +3913,7 @@ static void manager_check_basic_target(Manager *m) { return; /* For user managers, send out READY=1 as soon as we reach basic.target */ - user_manager_send_ready(m); + manager_send_ready_user_scope(m); /* Log the taint string as soon as we reach basic.target */ log_taint_string(m); @@ -3808,7 +3931,7 @@ void manager_check_finished(Manager *m) { manager_check_basic_target(m); - if (hashmap_size(m->jobs) > 0) { + if (!hashmap_isempty(m->jobs)) { if (m->jobs_in_progress_event_source) /* Ignore any failure, this is only for feedback */ (void) sd_event_source_set_time(m->jobs_in_progress_event_source, @@ -3821,7 +3944,7 @@ void manager_check_finished(Manager *m) { if (hashmap_buckets(m->jobs) > hashmap_size(m->units) / 10) m->jobs = hashmap_free(m->jobs); - manager_send_ready(m); + manager_send_ready_system_scope(m); /* Notify Type=idle units that we are done now */ manager_close_idle_pipe(m); @@ -3851,9 +3974,7 @@ void manager_send_reloading(Manager *m) { assert(m); /* Let whoever invoked us know that we are now reloading */ - (void) sd_notifyf(/* unset= */ false, - "RELOADING=1\n" - "MONOTONIC_USEC=" USEC_FMT "\n", now(CLOCK_MONOTONIC)); + (void) notify_reloading_full(/* status = */ NULL); /* And ensure that we'll send READY=1 again as soon as we are ready again */ m->ready_sent = false; @@ -3878,8 +3999,8 @@ static int manager_run_environment_generators(Manager *m) { _cleanup_strv_free_ char **paths = NULL; void* args[] = { [STDOUT_GENERATE] = &tmp, - [STDOUT_COLLECT] = &tmp, - [STDOUT_CONSUME] = &m->transient_environment, + [STDOUT_COLLECT] = &tmp, + [STDOUT_CONSUME] = &m->transient_environment, }; int r; @@ -4040,7 +4161,7 @@ static int manager_run_generators(Manager *m) { /* On some systems /tmp/ doesn't exist, and on some other systems we cannot create it at all. Avoid * trying to mount a private tmpfs on it as there's no one size fits all. */ - if (is_dir("/tmp", /* follow= */ false) > 0) + if (is_dir("/tmp", /* follow= */ false) > 0 && !MANAGER_IS_TEST_RUN(m)) flags |= FORK_PRIVATE_TMP; r = safe_fork("(sd-gens)", flags, NULL); @@ -4373,7 +4494,7 @@ void manager_override_show_status(Manager *m, ShowStatus mode, const char *reaso set_show_status_marker(show_status_on(mode)); } -const char *manager_get_confirm_spawn(Manager *m) { +const char* manager_get_confirm_spawn(Manager *m) { static int last_errno = 0; struct stat st; int r; @@ -4478,14 +4599,15 @@ void manager_status_printf(Manager *m, StatusType type, const char *status, cons va_end(ap); } -Set* manager_get_units_requiring_mounts_for(Manager *m, const char *path) { +Set* manager_get_units_needing_mounts_for(Manager *m, const char *path, UnitMountDependencyType t) { assert(m); assert(path); + assert(t >= 0 && t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX); if (path_equal(path, "/")) path = ""; - return hashmap_get(m->units_requiring_mounts_for, path); + return hashmap_get(m->units_needing_mounts_for[t], path); } int manager_update_failed_units(Manager *m, Unit *u, bool failed) { @@ -4542,7 +4664,7 @@ ManagerState manager_state(Manager *m) { } /* Are there any failed units? If so, we are in degraded mode */ - if (set_size(m->failed_units) > 0) + if (!set_isempty(m->failed_units)) return MANAGER_DEGRADED; return MANAGER_RUNNING; @@ -4701,20 +4823,19 @@ static void manager_vacuum(Manager *m) { exec_shared_runtime_vacuum(m); } -int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { +static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { struct buffer { uid_t uid; gid_t gid; char unit_name[UNIT_NAME_MAX+1]; } _packed_ buffer; - Manager *m = userdata; + Manager *m = ASSERT_PTR(userdata); ssize_t l; size_t n; Unit *u; - assert_se(source); - assert_se(m); + assert(source); /* Invoked whenever a child process succeeded resolving its user/group to use and sent us the * resulting UID/GID in a datagram. We parse the datagram here and pass it off to the unit, so that @@ -4763,76 +4884,71 @@ int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t re return 0; } -static int short_uid_range(const char *path) { - _cleanup_(uid_range_freep) UidRange *p = NULL; - int r; - - assert(path); - - /* Taint systemd if we the UID range assigned to this environment doesn't at least cover 0…65534, - * i.e. from root to nobody. */ - - r = uid_range_load_userns(&p, path); - if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) - return false; - if (r < 0) - return log_debug_errno(r, "Failed to load %s: %m", path); - - return !uid_range_covers(p, 0, 65535); -} - -char* manager_taint_string(const Manager *m) { - /* Returns a "taint string", e.g. "local-hwclock:var-run-bad". Only things that are detected at - * runtime should be tagged here. For stuff that is known during compilation, emit a warning in the - * configuration phase. */ - - assert(m); - - const char* stage[12] = {}; - size_t n = 0; - - _cleanup_free_ char *usrbin = NULL; - if (readlink_malloc("/bin", &usrbin) < 0 || !PATH_IN_SET(usrbin, "usr/bin", "/usr/bin")) - stage[n++] = "unmerged-usr"; +static int manager_dispatch_handoff_timestamp_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = ASSERT_PTR(userdata); + usec_t ts[2] = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; + struct msghdr msghdr = { + .msg_iov = &IOVEC_MAKE(ts, sizeof(ts)), + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + ssize_t n; - if (access("/proc/cgroups", F_OK) < 0) - stage[n++] = "cgroups-missing"; + assert(source); - if (cg_all_unified() == 0) - stage[n++] = "cgroupsv1"; + n = recvmsg_safe(m->handoff_timestamp_fds[0], &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC|MSG_TRUNC); + if (ERRNO_IS_NEG_TRANSIENT(n)) + return 0; /* Spurious wakeup, try again */ + if (n == -EXFULL) { + log_warning("Got message with truncated control, ignoring."); + return 0; + } + if (n < 0) + return log_error_errno(n, "Failed to receive handoff timestamp message: %m"); - if (clock_is_localtime(NULL) > 0) - stage[n++] = "local-hwclock"; + if (msghdr.msg_flags & MSG_TRUNC) { + log_warning("Got truncated handoff timestamp message, ignoring."); + return 0; + } + if (n != sizeof(ts)) { + log_warning("Got handoff timestamp message of unexpected size %zi (expected %zu), ignoring.", n, sizeof(ts)); + return 0; + } - if (os_release_support_ended(NULL, /* quiet= */ true, NULL) > 0) - stage[n++] = "support-ended"; + struct ucred *ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred); + if (!ucred || !pid_is_valid(ucred->pid)) { + log_warning("Received notify message without valid credentials. Ignoring."); + return 0; + } - _cleanup_free_ char *destination = NULL; - if (readlink_malloc("/var/run", &destination) < 0 || - !PATH_IN_SET(destination, "../run", "/run")) - stage[n++] = "var-run-bad"; + log_debug("Got handoff timestamp event for PID " PID_FMT ".", ucred->pid); - _cleanup_free_ char *overflowuid = NULL, *overflowgid = NULL; - if (read_one_line_file("/proc/sys/kernel/overflowuid", &overflowuid) >= 0 && - !streq(overflowuid, "65534")) - stage[n++] = "overflowuid-not-65534"; - if (read_one_line_file("/proc/sys/kernel/overflowgid", &overflowgid) >= 0 && - !streq(overflowgid, "65534")) - stage[n++] = "overflowgid-not-65534"; + _cleanup_free_ Unit **units = NULL; + int n_units = manager_get_units_for_pidref(m, &PIDREF_MAKE_FROM_PID(ucred->pid), &units); + if (n_units < 0) { + log_warning_errno(n_units, "Unable to determine units for PID " PID_FMT ", ignoring: %m", ucred->pid); + return 0; + } + if (n_units == 0) { + log_debug("Got handoff timestamp for process " PID_FMT " we are not interested in, ignoring.", ucred->pid); + return 0; + } - struct utsname uts; - assert_se(uname(&uts) >= 0); - if (strverscmp_improved(uts.release, KERNEL_BASELINE_VERSION) < 0) - stage[n++] = "old-kernel"; + dual_timestamp dt = { + .realtime = ts[0], + .monotonic = ts[1], + }; - if (short_uid_range("/proc/self/uid_map") > 0) - stage[n++] = "short-uid-range"; - if (short_uid_range("/proc/self/gid_map") > 0) - stage[n++] = "short-gid-range"; + FOREACH_ARRAY(u, units, n_units) { + if (!UNIT_VTABLE(*u)->notify_handoff_timestamp) + continue; - assert(n < ELEMENTSOF(stage) - 1); /* One extra for NULL terminator */ + UNIT_VTABLE(*u)->notify_handoff_timestamp(*u, ucred, &dt); + } - return strv_join((char**) stage, ":"); + return 0; } void manager_ref_console(Manager *m) { @@ -4988,14 +5104,13 @@ LogTarget manager_get_executor_log_target(Manager *m) { assert(m); /* If journald is not available tell sd-executor to go to kmsg, as it might be starting journald */ + if (!MANAGER_IS_TEST_RUN(m) && !manager_journal_is_running(m)) + return LOG_TARGET_KMSG; - if (manager_journal_is_running(m)) - return log_get_target(); - - return LOG_TARGET_KMSG; + return log_get_target(); } -static const char *const manager_state_table[_MANAGER_STATE_MAX] = { +static const char* const manager_state_table[_MANAGER_STATE_MAX] = { [MANAGER_INITIALIZING] = "initializing", [MANAGER_STARTING] = "starting", [MANAGER_RUNNING] = "running", @@ -5006,7 +5121,22 @@ static const char *const manager_state_table[_MANAGER_STATE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState); -static const char *const manager_timestamp_table[_MANAGER_TIMESTAMP_MAX] = { +static const char* const manager_objective_table[_MANAGER_OBJECTIVE_MAX] = { + [MANAGER_OK] = "ok", + [MANAGER_EXIT] = "exit", + [MANAGER_RELOAD] = "reload", + [MANAGER_REEXECUTE] = "reexecute", + [MANAGER_REBOOT] = "reboot", + [MANAGER_SOFT_REBOOT] = "soft-reboot", + [MANAGER_POWEROFF] = "poweroff", + [MANAGER_HALT] = "halt", + [MANAGER_KEXEC] = "kexec", + [MANAGER_SWITCH_ROOT] = "switch-root", +}; + +DEFINE_STRING_TABLE_LOOKUP(manager_objective, ManagerObjective); + +static const char* const manager_timestamp_table[_MANAGER_TIMESTAMP_MAX] = { [MANAGER_TIMESTAMP_FIRMWARE] = "firmware", [MANAGER_TIMESTAMP_LOADER] = "loader", [MANAGER_TIMESTAMP_KERNEL] = "kernel", @@ -5026,6 +5156,7 @@ static const char *const manager_timestamp_table[_MANAGER_TIMESTAMP_MAX] = { [MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH] = "initrd-generators-finish", [MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START] = "initrd-units-load-start", [MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH] = "initrd-units-load-finish", + [MANAGER_TIMESTAMP_SHUTDOWN_START] = "shutdown-start", }; DEFINE_STRING_TABLE_LOOKUP(manager_timestamp, ManagerTimestamp); |