]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/manager.c
tree-wide: make use of new relative time events in sd-event.h
[thirdparty/systemd.git] / src / core / manager.c
index ac5a3b68051612ab8fcb8781be1b4e945a2c19e2..3a8df8245c3b59ec75ee519ce2ea836d179d2703 100644 (file)
@@ -50,6 +50,7 @@
 #include "io-util.h"
 #include "label.h"
 #include "locale-setup.h"
+#include "load-fragment.h"
 #include "log.h"
 #include "macro.h"
 #include "manager.h"
@@ -588,6 +589,8 @@ static char** sanitize_environment(char **l) {
         /* 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",
@@ -595,13 +598,16 @@ static char** sanitize_environment(char **l) {
                         "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);
@@ -679,18 +685,12 @@ static int manager_setup_prefix(Manager *m) {
                 [EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_USER_CONFIGURATION, NULL },
         };
 
-        const struct table_entry *p;
-        ExecDirectoryType i;
-        int r;
-
         assert(m);
 
-        if (MANAGER_IS_SYSTEM(m))
-                p = paths_system;
-        else
-                p = paths_user;
+        const struct table_entry *p = MANAGER_IS_SYSTEM(m) ? paths_system : paths_user;
+        int r;
 
-        for (i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) {
+        for (ExecDirectoryType i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) {
                 r = sd_path_lookup(p[i].type, p[i].suffix, &m->prefix[i]);
                 if (r < 0)
                         return r;
@@ -702,7 +702,7 @@ static int manager_setup_prefix(Manager *m) {
 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;
 }
 
@@ -784,6 +784,8 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager
                 .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,
@@ -839,10 +841,6 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager
         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;
@@ -1344,15 +1342,12 @@ static void manager_clear_jobs_and_units(Manager *m) {
 }
 
 Manager* manager_free(Manager *m) {
-        ExecDirectoryType dt;
-        UnitType c;
-
         if (!m)
                 return NULL;
 
         manager_clear_jobs_and_units(m);
 
-        for (c = 0; c < _UNIT_TYPE_MAX; c++)
+        for (UnitType c = 0; c < _UNIT_TYPE_MAX; c++)
                 if (unit_vtable[c]->shutdown)
                         unit_vtable[c]->shutdown(m);
 
@@ -1423,22 +1418,20 @@ Manager* manager_free(Manager *m) {
         hashmap_free(m->uid_refs);
         hashmap_free(m->gid_refs);
 
-        for (dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++)
+        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++)
                 m->prefix[dt] = mfree(m->prefix[dt]);
 
         return mfree(m);
 }
 
 static void manager_enumerate_perpetual(Manager *m) {
-        UnitType 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++) {
+        for (UnitType c = 0; c < _UNIT_TYPE_MAX; c++) {
                 if (!unit_type_supported(c)) {
                         log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
                         continue;
@@ -1450,15 +1443,13 @@ static void manager_enumerate_perpetual(Manager *m) {
 }
 
 static void manager_enumerate(Manager *m) {
-        UnitType 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++) {
+        for (UnitType c = 0; c < _UNIT_TYPE_MAX; c++) {
                 if (!unit_type_supported(c)) {
                         log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
                         continue;
@@ -1946,6 +1937,21 @@ unsigned manager_dispatch_load_queue(Manager *m) {
         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,
@@ -1986,18 +1992,31 @@ int manager_load_unit_prepare(
 
         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);
@@ -2760,11 +2779,11 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
                 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:
@@ -2883,15 +2902,13 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
 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;
 
@@ -3220,9 +3237,9 @@ int manager_serialize(
         /* 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());
@@ -3400,7 +3417,7 @@ void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout) {
                 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);
@@ -3569,14 +3586,14 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         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;
@@ -3602,7 +3619,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         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;
@@ -3610,7 +3627,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         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;
@@ -3618,7 +3635,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         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);
@@ -3671,7 +3688,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                 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)
@@ -3976,6 +3993,11 @@ void manager_check_finished(Manager *m) {
                 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 */
@@ -4260,49 +4282,78 @@ void manager_recheck_journal(Manager *m) {
         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) {
@@ -4377,12 +4428,34 @@ bool manager_is_confirm_spawn_disabled(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
@@ -4417,12 +4490,9 @@ int manager_update_failed_units(Manager *m, Unit *u, bool failed) {
         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);