+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
#include <sys/wait.h>
#include <unistd.h>
-#ifdef HAVE_AUDIT
+#if HAVE_AUDIT
#include <libaudit.h>
#endif
#include "dirent-util.h"
#include "env-util.h"
#include "escape.h"
-#include "execute.h"
#include "exec-util.h"
+#include "execute.h"
#include "exit-status.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "hashmap.h"
#include "io-util.h"
+#include "label.h"
#include "locale-setup.h"
#include "log.h"
#include "macro.h"
assert(m);
assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
- if (m->test_run)
+ if (m->test_run_flags)
return 0;
/* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever
assert(m);
- if (m->test_run)
+ if (m->test_run_flags)
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. */
- if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM && errno != EINVAL)
+ if (reboot(RB_DISABLE_CAD) < 0 && !IN_SET(errno, EPERM, EINVAL))
log_warning_errno(errno, "Failed to enable ctrl-alt-del handling: %m");
fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
const char *suffix;
};
- static const struct table_entry paths_system[_EXEC_DIRECTORY_MAX] = {
+ 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_CONFIGURATION] = { SD_PATH_SYSTEM_CONFIGURATION, NULL },
};
- static const struct table_entry paths_user[_EXEC_DIRECTORY_MAX] = {
+ 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_CONFIGURATION, NULL },
- [EXEC_DIRECTORY_CACHE] = { SD_PATH_SYSTEM_STATE_CACHE, NULL },
- [EXEC_DIRECTORY_LOGS] = { SD_PATH_SYSTEM_CONFIGURATION, "log" },
- [EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_SYSTEM_CONFIGURATION, NULL },
+ [EXEC_DIRECTORY_CACHE] = { SD_PATH_USER_STATE_CACHE, NULL },
+ [EXEC_DIRECTORY_LOGS] = { SD_PATH_USER_CONFIGURATION, "log" },
+ [EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_USER_CONFIGURATION, NULL },
};
const struct table_entry *p;
else
p = paths_user;
- for (i = 0; i < _EXEC_DIRECTORY_MAX; i++) {
+ for (i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) {
r = sd_path_home(p[i].type, p[i].suffix, &m->prefix[i]);
if (r < 0)
return r;
return 0;
}
-int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
+int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
Manager *m;
int r;
m->default_timer_accuracy_usec = USEC_PER_MINUTE;
m->default_tasks_accounting = true;
m->default_tasks_max = UINT64_MAX;
+ m->default_timeout_start_usec = DEFAULT_TIMEOUT_USEC;
+ m->default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
+ m->default_restart_usec = DEFAULT_RESTART_USEC;
-#ifdef ENABLE_EFI
+#if ENABLE_EFI
if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0)
boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
#endif
m->unit_log_format_string = "UNIT=%s";
m->invocation_log_field = "INVOCATION_ID=";
- m->invocation_log_format_string = "INVOCATION_ID=" SD_ID128_FORMAT_STR;
+ m->invocation_log_format_string = "INVOCATION_ID=%s";
} else {
m->unit_log_field = "USER_UNIT=";
m->unit_log_format_string = "USER_UNIT=%s";
m->invocation_log_field = "USER_INVOCATION_ID=";
- m->invocation_log_format_string = "USER_INVOCATION_ID=" SD_ID128_FORMAT_STR;
+ m->invocation_log_format_string = "USER_INVOCATION_ID=%s";
}
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
m->have_ask_password = -EINVAL; /* we don't know */
m->first_boot = -1;
- m->test_run = test_run;
+ m->test_run_flags = test_run_flags;
/* Reboot immediately if the user hits C-A-D more often than 7x per 2s */
RATELIMIT_INIT(m->ctrl_alt_del_ratelimit, 2 * USEC_PER_SEC, 7);
goto fail;
}
+ if (MANAGER_IS_SYSTEM(m)) {
+ r = mkdir_label("/run/systemd/units", 0755);
+ if (r < 0 && r != -EEXIST)
+ goto fail;
+ }
+
/* Note that we do not set up the notify fd here. We do that after deserialization,
* since they might have gotten serialized across the reexec. */
static int manager_setup_notify(Manager *m) {
int r;
- if (m->test_run)
+ if (m->test_run_flags)
return 0;
if (m->notify_fd < 0) {
* to it. The system instance hence listens on this special socket, but the user instances listen on the system
* bus for these messages. */
- if (m->test_run)
+ if (m->test_run_flags)
return 0;
if (!MANAGER_IS_SYSTEM(m))
* SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification,
* and we collected the metadata the notification and SIGCHLD stuff offers first. 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-5);
+ r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-4);
if (r < 0)
return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m");
static int manager_connect_bus(Manager *m, bool reexecuting) {
bool try_bus_connect;
+ Unit *u = NULL;
assert(m);
- if (m->test_run)
+ if (m->test_run_flags)
return 0;
+ u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
+
try_bus_connect =
- reexecuting ||
- (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS"));
+ (u && SERVICE(u)->deserialized_state == SERVICE_RUNNING) &&
+ (reexecuting ||
+ (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS")));
/* Try to connect to the buses, if possible. */
return bus_init(m, try_bus_connect);
};
static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
- Iterator i;
Unit *other;
+ Iterator i;
+ void *v;
u->gc_marker = gc_marker + GC_OFFSET_GOOD;
/* Recursively mark referenced units as GOOD as well */
- SET_FOREACH(other, u->dependencies[UNIT_REFERENCES], i)
+ HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REFERENCES], i)
if (other->gc_marker == gc_marker + GC_OFFSET_UNSURE)
unit_gc_mark_good(other, gc_marker);
}
static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
- Iterator i;
Unit *other;
bool is_bad;
+ Iterator i;
+ void *v;
assert(u);
- if (u->gc_marker == gc_marker + GC_OFFSET_GOOD ||
- u->gc_marker == gc_marker + GC_OFFSET_BAD ||
- u->gc_marker == gc_marker + GC_OFFSET_UNSURE ||
- u->gc_marker == gc_marker + GC_OFFSET_IN_PATH)
+ if (IN_SET(u->gc_marker - gc_marker,
+ GC_OFFSET_GOOD, GC_OFFSET_BAD, GC_OFFSET_UNSURE, GC_OFFSET_IN_PATH))
return;
if (u->in_cleanup_queue)
is_bad = true;
- SET_FOREACH(other, u->dependencies[UNIT_REFERENCED_BY], i) {
+ HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REFERENCED_BY], i) {
unit_gc_sweep(other, gc_marker);
if (other->gc_marker == gc_marker + GC_OFFSET_GOOD)
n++;
- if (u->gc_marker == gc_marker + GC_OFFSET_BAD ||
- u->gc_marker == gc_marker + GC_OFFSET_UNSURE) {
+ if (IN_SET(u->gc_marker - gc_marker,
+ GC_OFFSET_BAD, GC_OFFSET_UNSURE)) {
if (u->id)
log_unit_debug(u, "Collecting.");
u->gc_marker = gc_marker + GC_OFFSET_BAD;
if (unit_vtable[c]->shutdown)
unit_vtable[c]->shutdown(m);
- /* If we reexecute ourselves, we keep the root cgroup
- * around */
+ /* If we reexecute ourselves, we keep the root cgroup around */
manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
lookup_paths_flush_generator(&m->lookup_paths);
hashmap_free(m->uid_refs);
hashmap_free(m->gid_refs);
- for (dt = 0; dt < _EXEC_DIRECTORY_MAX; dt++)
+ for (dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++)
m->prefix[dt] = mfree(m->prefix[dt]);
return mfree(m);
}
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
- int r, q;
+ int r;
assert(m);
- r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
+ /* If we are running in test mode, we still want to run the generators,
+ * but we should not touch the real generator directories. */
+ r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope,
+ m->test_run_flags ? LOOKUP_PATHS_TEMPORARY_GENERATED : 0,
+ NULL);
if (r < 0)
return r;
if (r < 0)
return r;
- /* Make sure the transient directory always exists, so that it remains in the search path */
- if (!m->test_run) {
- r = mkdir_p_label(m->lookup_paths.transient, 0755);
- if (r < 0)
- return r;
- }
+ /* Make sure the transient directory always exists, so that it remains
+ * in the search path */
+ r = mkdir_p_label(m->lookup_paths.transient, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create transient generator directory \"%s\": %m",
+ m->lookup_paths.transient);
dual_timestamp_get(&m->generators_start_timestamp);
r = manager_run_generators(m);
if (r < 0)
return r;
- if (m->first_boot && m->unit_file_scope == UNIT_FILE_SYSTEM) {
- q = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0);
- if (q < 0)
- log_full_errno(q == -EEXIST ? LOG_NOTICE : LOG_WARNING, q, "Failed to populate /etc with preset unit settings, ignoring: %m");
+ /* If this is the first boot, and we are in the host system, then preset everything */
+ if (m->first_boot > 0 &&
+ MANAGER_IS_SYSTEM(m) &&
+ !m->test_run_flags) {
+
+ r = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0);
+ if (r < 0)
+ log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r,
+ "Failed to populate /etc with preset unit settings, ignoring: %m");
else
log_info("Populated /etc with preset unit settings.");
}
if (serialization) {
r = manager_deserialize(m, serialization, fds);
if (r < 0)
- log_error_errno(r, "Deserialization failed: %m");
+ return log_error_errno(r, "Deserialization failed: %m");
}
/* Any fds left? Find some unit which wants them. This is
/* We might have deserialized the notify fd, but if we didn't
* then let's create the bus now */
- q = manager_setup_notify(m);
- if (q < 0 && r == 0)
- r = q;
+ r = manager_setup_notify(m);
+ if (r < 0)
+ /* No sense to continue without notifications, our children would fail anyway. */
+ return r;
- q = manager_setup_cgroups_agent(m);
- if (q < 0 && r == 0)
- r = q;
+ r = manager_setup_cgroups_agent(m);
+ if (r < 0)
+ /* Likewise, no sense to continue without empty cgroup notifications. */
+ return r;
- q = manager_setup_user_lookup_fd(m);
- if (q < 0 && r == 0)
- r = q;
+ r = manager_setup_user_lookup_fd(m);
+ if (r < 0)
+ /* This shouldn't fail, except if things are really broken. */
+ return r;
/* Let's connect to the bus now. */
(void) manager_connect_bus(m, !!serialization);
m->send_reloading_done = true;
}
- return r;
+ return 0;
}
int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) {
return -ENOMEM;
r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, false,
- mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS,
+ IN_SET(mode, JOB_IGNORE_DEPENDENCIES, JOB_IGNORE_REQUIREMENTS),
mode == JOB_IGNORE_DEPENDENCIES, e);
if (r < 0)
goto tr_abort;
buf[n] = 0;
manager_notify_cgroup_empty(m, buf);
- bus_forward_agent_released(m, buf);
+ (void) bus_forward_agent_released(m, buf);
return 0;
}
if (si.si_pid <= 0)
break;
- if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
+ if (IN_SET(si.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED)) {
_cleanup_free_ char *name = NULL;
Unit *u1, *u2, *u3;
if (manager_dispatch_cleanup_queue(m) > 0)
continue;
- if (manager_dispatch_cgroup_queue(m) > 0)
+ if (manager_dispatch_cgroup_realize_queue(m) > 0)
continue;
if (manager_dispatch_dbus_queue(m) > 0)
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
-#ifdef HAVE_AUDIT
+#if HAVE_AUDIT
_cleanup_free_ char *p = NULL;
const char *msg;
int audit_fd, r;
if (detect_container() > 0)
return;
- if (u->type != UNIT_SERVICE &&
- u->type != UNIT_MOUNT &&
- u->type != UNIT_SWAP)
+ if (!IN_SET(u->type, UNIT_SERVICE, UNIT_MOUNT, UNIT_SWAP))
return;
/* We set SOCK_NONBLOCK here so that we rather drop the
m->n_reloading++;
fprintf(f, "current-job-id=%"PRIu32"\n", m->current_job_id);
- fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
fprintf(f, "n-installed-jobs=%u\n", m->n_installed_jobs);
fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs);
+ fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
+ fprintf(f, "ready-sent=%s\n", yes_no(m->ready_sent));
dual_timestamp_serialize(f, "firmware-timestamp", &m->firmware_timestamp);
dual_timestamp_serialize(f, "loader-timestamp", &m->loader_timestamp);
else
m->taint_usr = m->taint_usr || b;
+ } else if ((val = startswith(l, "ready-sent="))) {
+ int b;
+
+ b = parse_boolean(val);
+ if (b < 0)
+ log_notice("Failed to parse ready-sent flag %s", val);
+ else
+ m->ready_sent = m->ready_sent || b;
+
} else if ((val = startswith(l, "firmware-timestamp=")))
dual_timestamp_deserialize(val, &m->firmware_timestamp);
else if ((val = startswith(l, "loader-timestamp=")))
char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec;
- if (m->test_run)
+ if (m->test_run_flags)
return;
if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
bus_manager_send_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec);
sd_notifyf(false,
- "READY=1\n"
- "STATUS=Startup finished in %s.",
+ m->ready_sent ? "STATUS=Startup finished in %s."
+ : "READY=1\n"
+ "STATUS=Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC));
+ m->ready_sent = true;
}
void manager_check_finished(Manager *m) {
if (m->exit_code != MANAGER_OK)
return;
+ /* For user managers, send out READY=1 as soon as we reach basic.target */
+ if (MANAGER_IS_USER(m) && !m->ready_sent) {
+ Unit *u;
+
+ u = manager_get_unit(m, SPECIAL_BASIC_TARGET);
+ if (u && !u->job) {
+ sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Reached " SPECIAL_BASIC_TARGET ".");
+ m->ready_sent = true;
+ }
+ }
+
if (hashmap_size(m->jobs) > 0) {
if (m->jobs_in_progress_event_source)
/* Ignore any failure, this is only for feedback */
const char **paths;
void* args[] = {&tmp, &tmp, &m->environment};
- if (m->test_run)
+ if (m->test_run_flags && !(m->test_run_flags & MANAGER_TEST_RUN_ENV_GENERATORS))
return 0;
paths = MANAGER_IS_SYSTEM(m) ? system_env_generator_binary_paths : user_env_generator_binary_paths;
assert(m);
- if (m->test_run)
+ if (m->test_run_flags && !(m->test_run_flags & MANAGER_TEST_RUN_GENERATORS))
return 0;
paths = generator_binary_paths(m->unit_file_scope);
l = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT);
if (l < 0) {
- if (errno == EINTR || errno == EAGAIN)
+ if (IN_SET(errno, EINTR, EAGAIN))
return 0;
return log_error_errno(errno, "Failed to read from user lookup fd: %m");