#include "io-util.h"
#include "label.h"
#include "locale-setup.h"
+#include "load-fragment.h"
#include "log.h"
#include "macro.h"
#include "manager.h"
/* Let's remove some environment variables that we need ourselves to communicate with our clients */
strv_env_unset_many(
l,
+ "CACHE_DIRECTORY",
+ "CONFIGURATION_DIRECTORY",
"EXIT_CODE",
"EXIT_STATUS",
"INVOCATION_ID",
"LISTEN_FDNAMES",
"LISTEN_FDS",
"LISTEN_PID",
+ "LOGS_DIRECTORY",
"MAINPID",
"MANAGERPID",
"NOTIFY_SOCKET",
"PIDFILE",
"REMOTE_ADDR",
"REMOTE_PORT",
+ "RUNTIME_DIRECTORY",
"SERVICE_RESULT",
+ "STATE_DIRECTORY",
"WATCHDOG_PID",
"WATCHDOG_USEC",
NULL);
static void manager_free_unit_name_maps(Manager *m) {
m->unit_id_map = hashmap_free(m->unit_id_map);
m->unit_name_map = hashmap_free(m->unit_name_map);
- m->unit_path_cache = set_free_free(m->unit_path_cache);
+ m->unit_path_cache = set_free(m->unit_path_cache);
m->unit_cache_mtime = 0;
}
.watchdog_overridden[WATCHDOG_REBOOT] = USEC_INFINITY,
.watchdog_overridden[WATCHDOG_KEXEC] = USEC_INFINITY,
+ .show_status_overridden = _SHOW_STATUS_INVALID,
+
.notify_fd = -1,
.cgroups_agent_fd = -1,
.signal_fd = -1,
if (r < 0)
return r;
- r = hashmap_ensure_allocated(&m->jobs, NULL);
- if (r < 0)
- return r;
-
r = hashmap_ensure_allocated(&m->cgroup_unit, &path_hash_ops);
if (r < 0)
return r;
return n;
}
+bool manager_unit_file_maybe_loadable_from_cache(Unit *u) {
+ assert(u);
+
+ if (u->load_state != UNIT_NOT_FOUND)
+ return false;
+
+ if (u->manager->unit_cache_mtime == 0)
+ return false;
+
+ if (u->manager->unit_cache_mtime > u->fragment_loadtime)
+ return true;
+
+ return !lookup_paths_mtime_good(&u->manager->lookup_paths, u->manager->unit_cache_mtime);
+}
+
int manager_load_unit_prepare(
Manager *m,
const char *name,
ret = manager_get_unit(m, name);
if (ret) {
- *_ret = ret;
- return 1;
+ /* The time-based cache allows to start new units 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
+ * the cache was loaded. Also check if the last time an attempt to load the
+ * unit was made was before the most recent cache refresh, so that we know
+ * we need to try again - even if the cache is current, it might have been
+ * updated in a different context before we had a chance to retry loading
+ * this particular unit. */
+ if (manager_unit_file_maybe_loadable_from_cache(ret))
+ ret->load_state = UNIT_STUB;
+ else {
+ *_ret = ret;
+ return 1;
+ }
+ } else {
+ ret = cleanup_ret = unit_new(m, unit_vtable[t]->object_size);
+ if (!ret)
+ return -ENOMEM;
}
- ret = cleanup_ret = unit_new(m, unit_vtable[t]->object_size);
- if (!ret)
- return -ENOMEM;
-
if (path) {
- ret->fragment_path = strdup(path);
- if (!ret->fragment_path)
- return -ENOMEM;
+ r = free_and_strdup(&ret->fragment_path, path);
+ if (r < 0)
+ return r;
}
r = unit_add_name(ret, name);
switch (sfsi.ssi_signo - SIGRTMIN) {
case 20:
- manager_set_show_status(m, SHOW_STATUS_YES, "signal");
+ manager_override_show_status(m, SHOW_STATUS_YES, "signal");
break;
case 21:
- manager_set_show_status(m, SHOW_STATUS_NO, "signal");
+ manager_override_show_status(m, SHOW_STATUS_NO, "signal");
break;
case 22:
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata) {
Manager *m = userdata;
int r;
- uint64_t next;
assert(m);
assert(source);
manager_print_jobs_in_progress(m);
- next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_PERIOD_USEC;
- r = sd_event_source_set_time(source, next);
+ r = sd_event_source_set_time_relative(source, JOBS_IN_PROGRESS_PERIOD_USEC);
if (r < 0)
return r;
/* After switching root, udevd has not been started yet. So, enumeration results should not be emitted. */
(void) serialize_bool(f, "honor-device-enumeration", !switching_root);
- t = show_status_to_string(m->show_status);
- if (t)
- (void) serialize_item(f, "show-status", t);
+ if (m->show_status_overridden != _SHOW_STATUS_INVALID)
+ (void) serialize_item(f, "show-status-overridden",
+ show_status_to_string(m->show_status_overridden));
if (m->log_level_overridden)
(void) serialize_item_format(f, "log-level-override", "%i", log_get_max_level());
m->watchdog[t] = timeout;
}
-int manager_set_watchdog_overridden(Manager *m, WatchdogType t, usec_t timeout) {
+int manager_override_watchdog(Manager *m, WatchdogType t, usec_t timeout) {
int r = 0;
assert(m);
else
m->honor_device_enumeration = b;
- } else if ((val = startswith(l, "show-status="))) {
+ } else if ((val = startswith(l, "show-status-overridden="))) {
ShowStatus s;
s = show_status_from_string(val);
if (s < 0)
- log_notice("Failed to parse show-status flag '%s', ignoring.", val);
+ log_notice("Failed to parse show-status-overridden flag '%s', ignoring.", val);
else
- manager_set_show_status(m, s, "deserialization");
+ manager_override_show_status(m, s, "deserialize");
} else if ((val = startswith(l, "log-level-override="))) {
int level;
if (deserialize_usec(val, &t) < 0)
log_notice("Failed to parse runtime-watchdog-overridden value '%s', ignoring.", val);
else
- manager_set_watchdog_overridden(m, WATCHDOG_RUNTIME, t);
+ manager_override_watchdog(m, WATCHDOG_RUNTIME, t);
} else if ((val = startswith(l, "reboot-watchdog-overridden="))) {
usec_t t;
if (deserialize_usec(val, &t) < 0)
log_notice("Failed to parse reboot-watchdog-overridden value '%s', ignoring.", val);
else
- manager_set_watchdog_overridden(m, WATCHDOG_REBOOT, t);
+ manager_override_watchdog(m, WATCHDOG_REBOOT, t);
} else if ((val = startswith(l, "kexec-watchdog-overridden="))) {
usec_t t;
if (deserialize_usec(val, &t) < 0)
log_notice("Failed to parse kexec-watchdog-overridden value '%s', ignoring.", val);
else
- manager_set_watchdog_overridden(m, WATCHDOG_KEXEC, t);
+ manager_override_watchdog(m, WATCHDOG_KEXEC, t);
} else if (startswith(l, "env=")) {
r = deserialize_environment(l + 4, &m->client_environment);
else if ((val = startswith(l, "destroy-ipc-gid=")))
manager_deserialize_gid_refs_one(m, val);
else if ((val = startswith(l, "exec-runtime=")))
- exec_runtime_deserialize_one(m, val, fds);
+ (void) exec_runtime_deserialize_one(m, val, fds);
else if ((val = startswith(l, "subscribed="))) {
if (strv_extend(&m->deserialized_subscribed, val) < 0)
return;
}
+ /* The jobs hashmap tends to grow a lot during boot, and then it's not reused until shutdown. Let's
+ kill the hashmap if it is relatively large. */
+ if (hashmap_buckets(m->jobs) > hashmap_size(m->units) / 10)
+ m->jobs = hashmap_free(m->jobs);
+
manager_flip_auto_status(m, false, "boot finished");
/* Notify Type=idle units that we are done now */
log_open();
}
+static ShowStatus manager_get_show_status(Manager *m) {
+ assert(m);
+
+ if (MANAGER_IS_USER(m))
+ return _SHOW_STATUS_INVALID;
+
+ if (m->show_status_overridden != _SHOW_STATUS_INVALID)
+ return m->show_status_overridden;
+
+ return m->show_status;
+}
+
+bool manager_get_show_status_on(Manager *m) {
+ assert(m);
+
+ return show_status_on(manager_get_show_status(m));
+}
+
+static void set_show_status_marker(bool b) {
+ if (b)
+ (void) touch("/run/systemd/show-status");
+ else
+ (void) unlink("/run/systemd/show-status");
+}
+
void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason) {
assert(m);
+ assert(reason);
assert(mode >= 0 && mode < _SHOW_STATUS_MAX);
- if (!MANAGER_IS_SYSTEM(m))
+ if (MANAGER_IS_USER(m))
return;
if (mode == m->show_status)
return;
- bool enabled = IN_SET(mode, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
- log_debug("%s (%s) showing of status (%s).",
- enabled ? "Enabling" : "Disabling",
- strna(show_status_to_string(mode)),
- reason);
- m->show_status = mode;
+ if (m->show_status_overridden == _SHOW_STATUS_INVALID) {
+ bool enabled;
- if (enabled)
- (void) touch("/run/systemd/show-status");
- else
- (void) unlink("/run/systemd/show-status");
+ enabled = show_status_on(mode);
+ log_debug("%s (%s) showing of status (%s).",
+ enabled ? "Enabling" : "Disabling",
+ strna(show_status_to_string(mode)),
+ reason);
+
+ set_show_status_marker(enabled);
+ }
+
+ m->show_status = mode;
}
-static bool manager_get_show_status(Manager *m, StatusType type) {
+void manager_override_show_status(Manager *m, ShowStatus mode, const char *reason) {
assert(m);
+ assert(mode < _SHOW_STATUS_MAX);
- if (!MANAGER_IS_SYSTEM(m))
- return false;
+ if (MANAGER_IS_USER(m))
+ return;
- if (m->no_console_output)
- return false;
+ if (mode == m->show_status_overridden)
+ return;
- if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING))
- return false;
+ m->show_status_overridden = mode;
- /* If we cannot find out the status properly, just proceed. */
- if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
- return false;
+ if (mode == _SHOW_STATUS_INVALID)
+ mode = m->show_status;
- if (type == STATUS_TYPE_NOTICE && m->show_status != SHOW_STATUS_NO)
- return true;
+ log_debug("%s (%s) showing of status (%s).",
+ m->show_status_overridden != _SHOW_STATUS_INVALID ? "Overriding" : "Restoring",
+ strna(show_status_to_string(mode)),
+ reason);
- return show_status_on(m->show_status);
+ set_show_status_marker(show_status_on(mode));
}
const char *manager_get_confirm_spawn(Manager *m) {
return access("/run/systemd/confirm_spawn_disabled", F_OK) >= 0;
}
+static bool manager_should_show_status(Manager *m, StatusType type) {
+ assert(m);
+
+ if (!MANAGER_IS_SYSTEM(m))
+ return false;
+
+ if (m->no_console_output)
+ return false;
+
+ if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING))
+ return false;
+
+ /* If we cannot find out the status properly, just proceed. */
+ if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
+ return false;
+
+ if (type == STATUS_TYPE_NOTICE && m->show_status != SHOW_STATUS_NO)
+ return true;
+
+ return manager_get_show_status_on(m);
+}
+
void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) {
va_list ap;
/* If m is NULL, assume we're after shutdown and let the messages through. */
- if (m && !manager_get_show_status(m, type))
+ if (m && !manager_should_show_status(m, type))
return;
/* XXX We should totally drop the check for ephemeral here
size = set_size(m->failed_units);
if (failed) {
- r = set_ensure_allocated(&m->failed_units, NULL);
+ r = set_ensure_put(&m->failed_units, NULL, u);
if (r < 0)
return log_oom();
-
- if (set_put(m->failed_units, u) < 0)
- return log_oom();
} else
(void) set_remove(m->failed_units, u);