]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/manager.c
Merge pull request #13904 from keur/job_mode_triggering
[thirdparty/systemd.git] / src / core / manager.c
index 3d3c3b0cbac6570ccc71afced59558ce37d0e3c1..aaf894c9ecf450f5b745308c71497f3149245c7e 100644 (file)
@@ -3,8 +3,6 @@
 #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>
@@ -35,6 +33,7 @@
 #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"
@@ -71,6 +69,7 @@
 #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"
@@ -293,10 +292,12 @@ static int manager_check_ask_password(Manager *m) {
                 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,
@@ -602,6 +603,8 @@ static char** sanitize_environment(char **l) {
 }
 
 int manager_default_environment(Manager *m) {
+        int r;
+
         assert(m);
 
         m->transient_environment = strv_free(m->transient_environment);
@@ -615,16 +618,29 @@ int manager_default_environment(Manager *m) {
                  * /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);
 
@@ -673,6 +689,13 @@ static int manager_setup_prefix(Manager *m) {
         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;
 
@@ -792,7 +815,7 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager
         }
 
         /* 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)
@@ -814,6 +837,10 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager
         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;
@@ -1274,7 +1301,7 @@ static void manager_clear_jobs_and_units(Manager *m) {
         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);
@@ -1323,6 +1350,8 @@ Manager* manager_free(Manager *m) {
         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);
 
@@ -1356,7 +1385,7 @@ Manager* manager_free(Manager *m) {
         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);
@@ -1460,56 +1489,6 @@ static void manager_catchup(Manager *m) {
         }
 }
 
-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 = path_join(*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;
@@ -1665,11 +1644,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
 
         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 */
@@ -1763,6 +1738,9 @@ int manager_add_job(
         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);
@@ -1783,6 +1761,12 @@ int manager_add_job(
                         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;
@@ -2164,7 +2148,7 @@ static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) {
         assert(source);
         assert(m);
 
-        while ((j = m->run_queue)) {
+        while ((j = prioq_peek(m->run_queue))) {
                 assert(j->installed);
                 assert(j->in_run_queue);
 
@@ -2880,16 +2864,12 @@ static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t use
 }
 
 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. */
@@ -3561,11 +3541,10 @@ int manager_reload(Manager *m) {
         (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);
@@ -4054,6 +4033,19 @@ static bool manager_journal_is_running(Manager *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);
@@ -4112,10 +4104,11 @@ static bool manager_get_show_status(Manager *m, StatusType type) {
 
 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
@@ -4131,25 +4124,26 @@ const char *manager_get_confirm_spawn(Manager *m) {
          * 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";
 }