#include <errno.h>
#include <fcntl.h>
#include <linux/kd.h>
-#include <signal.h>
-#include <string.h>
#include <sys/epoll.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include "dbus-manager.h"
#include "dbus-unit.h"
#include "dbus.h"
+#include "def.h"
#include "dirent-util.h"
#include "env-util.h"
#include "escape.h"
#include "fs-util.h"
#include "hashmap.h"
#include "io-util.h"
+#include "install.h"
#include "label.h"
#include "locale-setup.h"
#include "log.h"
#include "macro.h"
#include "manager.h"
#include "memory-util.h"
-#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-lookup.h"
#include "path-util.h"
-#include "plymouth-util.h"
#include "process-util.h"
#include "ratelimit.h"
#include "rlimit-util.h"
#include "string-util.h"
#include "strv.h"
#include "strxcpyx.h"
+#include "sysctl-util.h"
#include "syslog-util.h"
#include "terminal-util.h"
#include "time-util.h"
"%sA %s job is running for %s (%s / %s)",
strempty(job_of_n),
job_type_to_string(j->type),
- unit_description(j->unit),
+ unit_status_string(j->unit),
time, limit);
}
if (m->ask_password_inotify_fd < 0)
return log_error_errno(errno, "Failed to create inotify object: %m");
- if (inotify_add_watch(m->ask_password_inotify_fd, "/run/systemd/ask-password", IN_CREATE|IN_DELETE|IN_MOVE) < 0) {
- log_error_errno(errno, "Failed to watch \"/run/systemd/ask-password\": %m");
+ r = inotify_add_watch_and_warn(m->ask_password_inotify_fd,
+ "/run/systemd/ask-password",
+ IN_CREATE|IN_DELETE|IN_MOVE);
+ if (r < 0) {
manager_close_ask_password(m);
- return -errno;
+ return r;
}
r = sd_event_add_io(m->event, &m->ask_password_event_source,
}
int manager_default_environment(Manager *m) {
+ int r;
+
assert(m);
m->transient_environment = strv_free(m->transient_environment);
* /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)
+ return log_oom();
/* Import locale variables LC_*= from configuration */
(void) locale_setup(&m->transient_environment);
- } else
+ } else {
+ _cleanup_free_ char *k = NULL;
+
/* The user manager passes its own environment
- * along to its children. */
+ * along to its children, except for $PATH. */
m->transient_environment = strv_copy(environ);
+ if (!m->transient_environment)
+ return log_oom();
- if (!m->transient_environment)
- return log_oom();
+ k = strdup("PATH=" DEFAULT_USER_PATH);
+ if (!k)
+ return log_oom();
+
+ r = strv_env_replace(&m->transient_environment, k);
+ if (r < 0)
+ return log_oom();
+ TAKE_PTR(k);
+ }
sanitize_environment(m->transient_environment);
return 0;
}
+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_cache_mtime = 0;
+}
+
static int manager_setup_run_queue(Manager *m) {
int r;
.unit_file_scope = scope,
.objective = _MANAGER_OBJECTIVE_INVALID,
+ .status_unit_format = STATUS_UNIT_FORMAT_DEFAULT,
+
.default_timer_accuracy_usec = USEC_PER_MINUTE,
.default_memory_accounting = MEMORY_ACCOUNTING_DEFAULT,
.default_tasks_accounting = true,
}
/* 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);
+ m->ctrl_alt_del_ratelimit = (RateLimit) { .interval = 2 * USEC_PER_SEC, .burst = 7 };
r = manager_default_environment(m);
if (r < 0)
if (r < 0)
return r;
+ r = prioq_ensure_allocated(&m->run_queue, compare_job_priority);
+ if (r < 0)
+ return r;
+
r = manager_setup_prefix(m);
if (r < 0)
return r;
fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE);
- m->notify_socket = strappend(m->prefix[EXEC_DIRECTORY_RUNTIME], "/systemd/notify");
+ m->notify_socket = path_join(m->prefix[EXEC_DIRECTORY_RUNTIME], "systemd/notify");
if (!m->notify_socket)
return log_oom();
manager_dispatch_cleanup_queue(m);
assert(!m->load_queue);
- assert(!m->run_queue);
+ assert(prioq_isempty(m->run_queue));
assert(!m->dbus_unit_queue);
assert(!m->dbus_job_queue);
assert(!m->cleanup_queue);
hashmap_free(m->watch_pids);
hashmap_free(m->watch_bus);
+ prioq_free(m->run_queue);
+
set_free(m->startup_units);
set_free(m->failed_units);
strv_free(m->client_environment);
hashmap_free(m->cgroup_unit);
- set_free_free(m->unit_path_cache);
+ manager_free_unit_name_maps(m);
free(m->switch_root);
free(m->switch_root_init);
assert(m);
+ if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
+ return;
+
/* Let's ask every type to load all units from disk/kernel that it might know */
for (c = 0; c < _UNIT_TYPE_MAX; c++) {
if (!unit_type_supported(c)) {
assert(m);
+ if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
+ return;
+
/* Let's ask every type to load all units from disk/kernel that it might know */
for (c = 0; c < _UNIT_TYPE_MAX; c++) {
if (!unit_type_supported(c)) {
}
}
-static void manager_build_unit_path_cache(Manager *m) {
- char **i;
- int r;
-
- assert(m);
-
- set_free_free(m->unit_path_cache);
-
- m->unit_path_cache = set_new(&path_hash_ops);
- if (!m->unit_path_cache) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* This simply builds a list of files we know exist, so that
- * we don't always have to go to disk */
-
- STRV_FOREACH(i, m->lookup_paths.search_path) {
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
-
- d = opendir(*i);
- if (!d) {
- if (errno != ENOENT)
- log_warning_errno(errno, "Failed to open directory %s, ignoring: %m", *i);
- continue;
- }
-
- FOREACH_DIRENT(de, d, r = -errno; goto fail) {
- char *p;
-
- p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name);
- if (!p) {
- r = -ENOMEM;
- goto fail;
- }
-
- r = set_consume(m->unit_path_cache, p);
- if (r < 0)
- goto fail;
- }
- }
-
- return;
-
-fail:
- log_warning_errno(r, "Failed to build unit path cache, proceeding without: %m");
- m->unit_path_cache = set_free_free(m->unit_path_cache);
-}
-
static void manager_distribute_fds(Manager *m, FDSet *fds) {
Iterator i;
Unit *u;
manager_preset_all(m);
- r = lookup_paths_reduce(&m->lookup_paths);
- if (r < 0)
- log_warning_errno(r, "Failed to reduce unit file paths, ignoring: %m");
-
- manager_build_unit_path_cache(m);
+ lookup_paths_log(&m->lookup_paths);
{
/* This block is (optionally) done with the reloading counter bumped */
if (mode == JOB_ISOLATE && !unit->allow_isolate)
return sd_bus_error_setf(error, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated.");
+ if (mode == JOB_TRIGGERING && type != JOB_STOP)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "--job-mode=triggering is only valid for stop.");
+
log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
type = job_type_collapse(type, unit);
goto tr_abort;
}
+ if (mode == JOB_TRIGGERING) {
+ r = transaction_add_triggering_jobs(tr, unit);
+ if (r < 0)
+ goto tr_abort;
+ }
+
r = transaction_activate(tr, m, mode, affected_jobs, error);
if (r < 0)
goto tr_abort;
assert(f);
for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
- char buf[FORMAT_TIMESTAMP_MAX];
+ const dual_timestamp *t = m->timestamps + q;
+ char buf[CONST_MAX(FORMAT_TIMESPAN_MAX, FORMAT_TIMESTAMP_MAX)];
- if (dual_timestamp_is_set(m->timestamps + q))
+ if (dual_timestamp_is_set(t))
fprintf(f, "%sTimestamp %s: %s\n",
strempty(prefix),
manager_timestamp_to_string(q),
- format_timestamp(buf, sizeof(buf), m->timestamps[q].realtime));
+ timestamp_is_set(t->realtime) ? format_timestamp(buf, sizeof buf, t->realtime) :
+ format_timespan(buf, sizeof buf, t->monotonic, 1));
}
manager_dump_units(m, f, prefix);
assert(source);
assert(m);
- while ((j = m->run_queue)) {
+ while ((j = prioq_peek(m->run_queue))) {
assert(j->installed);
assert(j->in_run_queue);
}
int manager_loop(Manager *m) {
+ RateLimit rl = { .interval = 1*USEC_PER_SEC, .burst = 50000 };
int r;
- RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 50000);
-
assert(m);
assert(m->objective == MANAGER_OK); /* Ensure manager_startup() has been called */
- /* Release the path cache */
- m->unit_path_cache = set_free_free(m->unit_path_cache);
-
manager_check_finished(m);
/* There might still be some zombies hanging around from before we were exec()'ed. Let's reap them. */
while (m->objective == MANAGER_OK) {
usec_t wait_usec;
- if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m))
+ if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m))
watchdog_ping();
if (!ratelimit_below(&rl)) {
continue;
/* Sleep for half the watchdog time */
- if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) {
+ if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m)) {
wait_usec = m->runtime_watchdog / 2;
if (wait_usec <= 0)
wait_usec = 1;
(void) manager_run_environment_generators(m);
(void) manager_run_generators(m);
- r = lookup_paths_reduce(&m->lookup_paths);
- if (r < 0)
- log_warning_errno(r, "Failed to reduce unit file paths, ignoring: %m");
+ lookup_paths_log(&m->lookup_paths);
- manager_build_unit_path_cache(m);
+ /* We flushed out generated files, for which we don't watch mtime, so we should flush the old map. */
+ manager_free_unit_name_maps(m);
/* First, enumerate what we can from kernel and suchlike */
manager_enumerate_perpetual(m);
return true;
}
+void disable_printk_ratelimit(void) {
+ /* Disable kernel's printk ratelimit.
+ *
+ * Logging to /dev/kmsg is most useful during early boot and shutdown, where normal logging
+ * mechanisms are not available. The semantics of this sysctl are such that any kernel command-line
+ * setting takes precedence. */
+ int r;
+
+ r = sysctl_write("kernel/printk_devkmsg", "on");
+ if (r < 0)
+ log_debug_errno(r, "Failed to set sysctl kernel.printk_devkmsg=on: %m");
+}
+
void manager_recheck_journal(Manager *m) {
assert(m);
const char *manager_get_confirm_spawn(Manager *m) {
static int last_errno = 0;
- const char *vc = m->confirm_spawn;
struct stat st;
int r;
+ assert(m);
+
/* Here's the deal: we want to test the validity of the console but don't want
* PID1 to go through the whole console process which might block. But we also
* want to warn the user only once if something is wrong with the console so we
* reason the configured console is not ready, we fallback to the default
* console. */
- if (!vc || path_equal(vc, "/dev/console"))
- return vc;
+ if (!m->confirm_spawn || path_equal(m->confirm_spawn, "/dev/console"))
+ return m->confirm_spawn;
- r = stat(vc, &st);
- if (r < 0)
+ if (stat(m->confirm_spawn, &st) < 0) {
+ r = -errno;
goto fail;
+ }
if (!S_ISCHR(st.st_mode)) {
- errno = ENOTTY;
+ r = -ENOTTY;
goto fail;
}
last_errno = 0;
- return vc;
+ return m->confirm_spawn;
+
fail:
- if (last_errno != errno) {
- last_errno = errno;
- log_warning_errno(errno, "Failed to open %s: %m, using default console", vc);
- }
+ if (last_errno != r)
+ last_errno = log_warning_errno(r, "Failed to open %s, using default console: %m", m->confirm_spawn);
+
return "/dev/console";
}