chiark / gitweb /
Only disable output on console during boot if needed
[elogind.git] / src / core / manager.c
index 51f03de0986f213d9955a290a6535d558d728245..c99a022cd5390ffc42d66848c223d3c4d6f0ab8a 100644 (file)
@@ -55,6 +55,7 @@
 #include "util.h"
 #include "mkdir.h"
 #include "ratelimit.h"
+#include "locale-setup.h"
 #include "mount-setup.h"
 #include "unit-name.h"
 #include "dbus-unit.h"
@@ -69,7 +70,7 @@
 #include "cgroup-util.h"
 #include "path-util.h"
 #include "audit-fd.h"
-#include "efivars.h"
+#include "boot-timestamps.h"
 #include "env-util.h"
 
 /* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
@@ -235,7 +236,7 @@ static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned po
                 *p++ = '*';
                 if (pos < width-1)
                         p = mempset(p, ' ', width-1-pos);
-                p = stpcpy(p, ANSI_HIGHLIGHT_OFF);
+                strcpy(p, ANSI_HIGHLIGHT_OFF);
         }
 }
 
@@ -256,6 +257,7 @@ static void manager_print_jobs_in_progress(Manager *m) {
         /* m->n_running_jobs must be consistent with the contents of m->jobs,
          * so the above loop must have succeeded in finding j. */
         assert(counter == print_nr + 1);
+        assert(j);
 
         cylon_pos = m->jobs_in_progress_iteration % 14;
         if (cylon_pos >= 8)
@@ -273,6 +275,54 @@ static void manager_print_jobs_in_progress(Manager *m) {
         m->jobs_in_progress_iteration++;
 }
 
+static int manager_watch_idle_pipe(Manager *m) {
+        struct epoll_event ev = {
+                .events = EPOLLIN,
+                .data.ptr = &m->idle_pipe_watch,
+        };
+        int r;
+
+        if (m->idle_pipe_watch.type != WATCH_INVALID)
+                return 0;
+
+        if (m->idle_pipe[2] < 0)
+                return 0;
+
+        m->idle_pipe_watch.type = WATCH_IDLE_PIPE;
+        m->idle_pipe_watch.fd = m->idle_pipe[2];
+        if (m->idle_pipe_watch.fd < 0) {
+                log_error("Failed to create timerfd: %m");
+                r = -errno;
+                goto err;
+        }
+
+        if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->idle_pipe_watch.fd, &ev) < 0) {
+                log_error("Failed to add idle_pipe fd to epoll: %m");
+                r = -errno;
+                goto err;
+        }
+
+        log_debug("Set up idle_pipe watch.");
+
+        return 0;
+
+err:
+        if (m->idle_pipe_watch.fd >= 0)
+                close_nointr_nofail(m->idle_pipe_watch.fd);
+        watch_init(&m->idle_pipe_watch);
+        return r;
+}
+
+static void manager_unwatch_idle_pipe(Manager *m) {
+        if (m->idle_pipe_watch.type != WATCH_IDLE_PIPE)
+                return;
+
+        assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->idle_pipe_watch.fd, NULL) >= 0);
+        watch_init(&m->idle_pipe_watch);
+
+        log_debug("Closed idle_pipe watch.");
+}
+
 static int manager_setup_time_change(Manager *m) {
         struct epoll_event ev = {
                 .events = EPOLLIN,
@@ -406,27 +456,37 @@ static int manager_setup_signals(Manager *m) {
         return 0;
 }
 
-static void manager_strip_environment(Manager *m) {
+static int manager_default_environment(Manager *m) {
         assert(m);
 
-        /* Remove variables from the inherited set that are part of
-         * the container interface:
-         * http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface */
-        strv_remove_prefix(m->environment, "container=");
-        strv_remove_prefix(m->environment, "container_");
+        if (m->running_as == SYSTEMD_SYSTEM) {
+                /* The system manager always starts with a clean
+                 * environment for its children. It does not import
+                 * the kernel or the parents exported variables.
+                 *
+                 * The initial passed environ is untouched to keep
+                 * /proc/self/environ valid; it is used for tagging
+                 * the init process inside containers. */
+                m->environment = strv_new("PATH=" DEFAULT_PATH,
+                                          NULL);
+
+                /* Import locale variables LC_*= from configuration */
+                locale_setup(&m->environment);
+        } else
+                /* The user manager passes its own environment
+                 * along to its children. */
+                m->environment = strv_copy(environ);
 
-        /* Remove variables from the inherited set that are part of
-         * the initrd interface:
-         * http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */
-        strv_remove_prefix(m->environment, "RD_");
+        if (!m->environment)
+                return -ENOMEM;
 
-        /* Drop invalid entries */
-        strv_env_clean(m->environment);
+        return 0;
 }
 
 int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) {
         Manager *m;
         int r = -ENOMEM;
+        bool try_bus_connect = false;
 
         assert(_m);
         assert(running_as >= 0);
@@ -438,14 +498,14 @@ int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) {
 
 #ifdef ENABLE_EFI
         if (detect_container(NULL) <= 0)
-                efi_get_boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
+                boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
 #endif
 
         m->running_as = running_as;
         m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
         m->exit_code = _MANAGER_EXIT_CODE_INVALID;
         m->pin_cgroupfs_fd = -1;
-        m->idle_pipe[0] = m->idle_pipe[1] = -1;
+        m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
 
         watch_init(&m->signal_watch);
         watch_init(&m->mount_watch);
@@ -457,12 +517,10 @@ int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) {
         m->epoll_fd = m->dev_autofs_fd = -1;
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
 
-        m->environment = strv_copy(environ);
-        if (!m->environment)
+        r = manager_default_environment(m);
+        if (r < 0)
                 goto fail;
 
-        manager_strip_environment(m);
-
         if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
                 goto fail;
 
@@ -500,15 +558,18 @@ int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) {
         if (r < 0)
                 goto fail;
 
-        /* Try to connect to the busses, if possible. */
-        if ((running_as == SYSTEMD_USER && getenv("DBUS_SESSION_BUS_ADDRESS")) ||
-            running_as == SYSTEMD_SYSTEM) {
-                r = bus_init(m, reexecuting || running_as != SYSTEMD_SYSTEM);
-                if (r < 0)
-                        goto fail;
-        } else
+        if (running_as == SYSTEMD_SYSTEM)
+                try_bus_connect = reexecuting;
+        else if (getenv("DBUS_SESSION_BUS_ADDRESS"))
+                try_bus_connect = true;
+        else
                 log_debug("Skipping DBus session bus connection attempt - no DBUS_SESSION_BUS_ADDRESS set...");
 
+        /* Try to connect to the busses, if possible. */
+        r = bus_init(m, try_bus_connect);
+        if (r < 0)
+                goto fail;
+
         m->taint_usr = dir_is_empty("/usr") > 0;
 
         *_m = m;
@@ -615,7 +676,7 @@ static unsigned manager_dispatch_gc_queue(Manager *m) {
 
                 unit_gc_sweep(u, gc_marker);
 
-                LIST_REMOVE(Unit, gc_queue, m->gc_queue, u);
+                LIST_REMOVE(gc_queue, m->gc_queue, u);
                 u->in_gc_queue = false;
 
                 n++;
@@ -657,6 +718,11 @@ static void manager_clear_jobs_and_units(Manager *m) {
         m->n_running_jobs = 0;
 }
 
+static void close_idle_pipe(Manager *m) {
+        close_pipe(m->idle_pipe);
+        close_pipe(m->idle_pipe + 2);
+}
+
 void manager_free(Manager *m) {
         UnitType c;
         int i;
@@ -701,7 +767,7 @@ void manager_free(Manager *m) {
         hashmap_free(m->cgroup_unit);
         set_free_free(m->unit_path_cache);
 
-        close_pipe(m->idle_pipe);
+        close_idle_pipe(m);
 
         free(m->switch_root);
         free(m->switch_root_init);
@@ -709,6 +775,9 @@ void manager_free(Manager *m) {
         for (i = 0; i < RLIMIT_NLIMITS; i++)
                 free(m->rlimit[i]);
 
+        assert(hashmap_isempty(m->units_requiring_mounts_for));
+        hashmap_free(m->units_requiring_mounts_for);
+
         free(m);
 }
 
@@ -721,15 +790,17 @@ int manager_enumerate(Manager *m) {
         /* 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_vtable[c]->enumerate)
-                        if ((q = unit_vtable[c]->enumerate(m)) < 0)
+                if (unit_vtable[c]->enumerate) {
+                        q = unit_vtable[c]->enumerate(m);
+                        if (q < 0)
                                 r = q;
+                }
 
         manager_dispatch_load_queue(m);
         return r;
 }
 
-int manager_coldplug(Manager *m) {
+static int manager_coldplug(Manager *m) {
         int r = 0, q;
         Iterator i;
         Unit *u;
@@ -809,6 +880,29 @@ fail:
         m->unit_path_cache = NULL;
 }
 
+
+static int manager_distribute_fds(Manager *m, FDSet *fds) {
+        Unit *u;
+        Iterator i;
+        int r;
+
+        assert(m);
+
+        HASHMAP_FOREACH(u, m->units, i) {
+
+                if (fdset_size(fds) <= 0)
+                        break;
+
+                if (UNIT_VTABLE(u)->distribute_fds) {
+                        r = UNIT_VTABLE(u)->distribute_fds(u, fds);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
 int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         int r, q;
 
@@ -864,6 +958,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         if (serialization) {
                 assert(m->n_reloading > 0);
                 m->n_reloading --;
+
+                /* Let's wait for the UnitNew/JobNew messages being
+                 * sent, before we notify that the reload is
+                 * finished */
+                m->send_reloading_done = true;
         }
 
         return r;
@@ -1111,7 +1210,7 @@ void manager_clear_jobs(Manager *m) {
                 job_finish_and_invalidate(j, JOB_CANCELED, false);
 }
 
-unsigned manager_dispatch_run_queue(Manager *m) {
+static unsigned manager_dispatch_run_queue(Manager *m) {
         Job *j;
         unsigned n = 0;
 
@@ -1133,10 +1232,13 @@ unsigned manager_dispatch_run_queue(Manager *m) {
         if (m->n_running_jobs > 0)
                 manager_watch_jobs_in_progress(m);
 
+        if (m->n_on_console > 0)
+                manager_watch_idle_pipe(m);
+
         return n;
 }
 
-unsigned manager_dispatch_dbus_queue(Manager *m) {
+static unsigned manager_dispatch_dbus_queue(Manager *m) {
         Job *j;
         Unit *u;
         unsigned n = 0;
@@ -1163,6 +1265,13 @@ unsigned manager_dispatch_dbus_queue(Manager *m) {
         }
 
         m->dispatching_dbus_queue = false;
+
+        if (m->send_reloading_done) {
+                m->send_reloading_done = false;
+
+                bus_broadcast_reloading(m, false);
+        }
+
         return n;
 }
 
@@ -1679,6 +1788,14 @@ static int process_event(Manager *m, struct epoll_event *ev) {
                 break;
         }
 
+        case WATCH_IDLE_PIPE: {
+                m->no_console_output = m->n_on_console > 0;
+
+                manager_unwatch_idle_pipe(m);
+                close_idle_pipe(m);
+                break;
+        }
+
         default:
                 log_error("event type=%i", w->type);
                 assert_not_reached("Unknown epoll event type.");
@@ -1702,7 +1819,7 @@ int manager_loop(Manager *m) {
         manager_check_finished(m);
 
         /* There might still be some zombies hanging around from
-         * before we were exec()'ed. Leat's reap them */
+         * before we were exec()'ed. Let's reap them. */
         r = manager_dispatch_sigchld(m);
         if (r < 0)
                 return r;
@@ -1968,7 +2085,7 @@ void manager_dispatch_bus_query_pid_done(
 
 int manager_open_serialization(Manager *m, FILE **_f) {
         char *path = NULL;
-        int fd;
+        int fd = -1;
         FILE *f;
 
         assert(_f);
@@ -2204,32 +2321,10 @@ finish:
         return r;
 }
 
-int manager_distribute_fds(Manager *m, FDSet *fds) {
-        Unit *u;
-        Iterator i;
-        int r;
-
-        assert(m);
-
-        HASHMAP_FOREACH(u, m->units, i) {
-
-                if (fdset_size(fds) <= 0)
-                        break;
-
-                if (UNIT_VTABLE(u)->distribute_fds) {
-                        r = UNIT_VTABLE(u)->distribute_fds(u, fds);
-                        if (r < 0)
-                                return r;
-                }
-        }
-
-        return 0;
-}
-
 int manager_reload(Manager *m) {
         int r, q;
-        FILE *f;
-        FDSet *fds;
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_fdset_free_ FDSet *fds = NULL;
 
         assert(m);
 
@@ -2238,24 +2333,23 @@ int manager_reload(Manager *m) {
                 return r;
 
         m->n_reloading ++;
+        bus_broadcast_reloading(m, true);
 
         fds = fdset_new();
         if (!fds) {
                 m->n_reloading --;
-                r = -ENOMEM;
-                goto finish;
+                return -ENOMEM;
         }
 
         r = manager_serialize(m, f, fds, false);
         if (r < 0) {
                 m->n_reloading --;
-                goto finish;
+                return r;
         }
 
         if (fseeko(f, 0, SEEK_SET) < 0) {
                 m->n_reloading --;
-                r = -errno;
-                goto finish;
+                return -errno;
         }
 
         /* From here on there is no way back. */
@@ -2297,12 +2391,7 @@ int manager_reload(Manager *m) {
         assert(m->n_reloading > 0);
         m->n_reloading--;
 
-finish:
-        if (f)
-                fclose(f);
-
-        if (fds)
-                fdset_free(fds);
+        m->send_reloading_done = true;
 
         return r;
 }
@@ -2369,7 +2458,8 @@ void manager_check_finished(Manager *m) {
         }
 
         /* Notify Type=idle units that we are done now */
-        close_pipe(m->idle_pipe);
+        manager_unwatch_idle_pipe(m);
+        close_idle_pipe(m);
 
         /* Turn off confirm spawn now */
         m->confirm_spawn = false;
@@ -2571,15 +2661,17 @@ void manager_undo_generators(Manager *m) {
         remove_generator_dir(m, &m->generator_unit_path_late);
 }
 
-int manager_set_default_environment(Manager *m, char **environment) {
-
+int manager_environment_add(Manager *m, char **environment) {
         char **e = NULL;
         assert(m);
+
         e = strv_env_merge(2, m->environment, environment);
         if (!e)
                 return -ENOMEM;
+
         strv_free(m->environment);
         m->environment = e;
+
         return 0;
 }
 
@@ -2645,6 +2737,9 @@ static bool manager_get_show_status(Manager *m) {
         if (m->running_as != SYSTEMD_SYSTEM)
                 return false;
 
+        if (m->no_console_output)
+                return false;
+
         if (m->show_status)
                 return true;
 
@@ -2673,6 +2768,41 @@ void manager_status_printf(Manager *m, bool ephemeral, const char *status, const
         va_end(ap);
 }
 
+int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found) {
+        _cleanup_free_ char *p = NULL;
+        Unit *found;
+
+        assert(m);
+        assert(path);
+        assert(suffix);
+        assert(_found);
+
+        p = unit_name_from_path(path, suffix);
+        if (!p)
+                return -ENOMEM;
+
+        found = manager_get_unit(m, p);
+        if (!found) {
+                *_found = NULL;
+                return 0;
+        }
+
+        *_found = found;
+        return 1;
+}
+
+Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
+        char p[strlen(path)+1];
+
+        assert(m);
+        assert(path);
+
+        strcpy(p, path);
+        path_kill_slashes(p);
+
+        return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p);
+}
+
 void watch_init(Watch *w) {
         assert(w);