chiark / gitweb /
core: add support for a configurable system-wide start-up timeout
[elogind.git] / src / core / manager.c
index e488aba5f8be8a5cbc5cc8755a8df447bf23921d..1bb0c9025f8c9833baf2c3f1b1fa9947ec6e92c3 100644 (file)
@@ -435,6 +435,8 @@ int manager_new(SystemdRunningAs running_as, bool test_run, Manager **_m) {
         m->running_as = running_as;
         m->exit_code = _MANAGER_EXIT_CODE_INVALID;
         m->default_timer_accuracy_usec = USEC_PER_MINUTE;
+        m->start_timeout_usec = DEFAULT_MANAGER_START_TIMEOUT_USEC;
+        m->start_timeout_action = FAILURE_ACTION_REBOOT_FORCE;
 
         m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
 
@@ -562,7 +564,7 @@ static int manager_setup_notify(Manager *m) {
                 strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1);
                 r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
                 if (r < 0) {
-                        log_error("bind(@%s) failed: %m", sa.un.sun_path+1);
+                        log_error("bind(%s) failed: %m", sa.un.sun_path);
                         return -errno;
                 }
 
@@ -823,6 +825,9 @@ void manager_free(Manager *m) {
 
         manager_close_idle_pipe(m);
 
+        sd_event_source_unref(m->start_timeout_event_source);
+        free(m->start_timeout_reboot_arg);
+
         udev_unref(m->udev);
         sd_event_unref(m->event);
 
@@ -970,6 +975,20 @@ static int manager_distribute_fds(Manager *m, FDSet *fds) {
         return 0;
 }
 
+static int on_start_timeout(sd_event_source *s, usec_t usec, void *userdata) {
+        Manager *m = userdata;
+
+        assert(s);
+        assert(m);
+
+        m->start_timeout_event_source = sd_event_source_unref(m->start_timeout_event_source);
+
+        log_error("Startup timed out.");
+
+        failure_action(m, m->start_timeout_action, m->start_timeout_reboot_arg);
+        return 0;
+}
+
 int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         int r, q;
 
@@ -1042,6 +1061,22 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
                 m->send_reloading_done = true;
         }
 
+        /* Possibly set up a start timeout */
+        if (!dual_timestamp_is_set(&m->finish_timestamp)) {
+                m->start_timeout_event_source = sd_event_source_unref(m->start_timeout_event_source);
+
+                if (m->start_timeout_usec) {
+                        r = sd_event_add_time(
+                                        m->event,
+                                        &m->start_timeout_event_source,
+                                        CLOCK_MONOTONIC,
+                                        now(CLOCK_MONOTONIC) + m->start_timeout_usec, 0,
+                                        on_start_timeout, m);
+                        if (r < 0)
+                                log_error("Failed to add start timeout event: %s", strerror(-r));
+                }
+        }
+
         return r;
 }
 
@@ -1398,7 +1433,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
                         .msg_controllen = sizeof(control),
                 };
                 struct ucred *ucred;
-                Unit *u;
+                Unit *u1, *u2, *u3;
 
                 n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT);
                 if (n <= 0) {
@@ -1424,21 +1459,23 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
                 assert((size_t) n < sizeof(buf));
                 buf[n] = 0;
 
-                u = manager_get_unit_by_pid(m, ucred->pid);
-                if (u) {
-                        manager_invoke_notify_message(m, u, ucred->pid, buf, n);
+                /* Notify every unit that might be interested, but try
+                 * to avoid notifying the same one multiple times. */
+                u1 = manager_get_unit_by_pid(m, ucred->pid);
+                if (u1) {
+                        manager_invoke_notify_message(m, u1, ucred->pid, buf, n);
                         found = true;
                 }
 
-                u = hashmap_get(m->watch_pids1, LONG_TO_PTR(ucred->pid));
-                if (u) {
-                        manager_invoke_notify_message(m, u, ucred->pid, buf, n);
+                u2 = hashmap_get(m->watch_pids1, LONG_TO_PTR(ucred->pid));
+                if (u2 && u2 != u1) {
+                        manager_invoke_notify_message(m, u2, ucred->pid, buf, n);
                         found = true;
                 }
 
-                u = hashmap_get(m->watch_pids2, LONG_TO_PTR(ucred->pid));
-                if (u) {
-                        manager_invoke_notify_message(m, u, ucred->pid, buf, n);
+                u3 = hashmap_get(m->watch_pids2, LONG_TO_PTR(ucred->pid));
+                if (u3 && u3 != u2 && u3 != u1) {
+                        manager_invoke_notify_message(m, u3, ucred->pid, buf, n);
                         found = true;
                 }
 
@@ -1485,7 +1522,7 @@ static int manager_dispatch_sigchld(Manager *m) {
 
                 if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
                         _cleanup_free_ char *name = NULL;
-                        Unit *u;
+                        Unit *u1, *u2, *u3;
 
                         get_process_comm(si.si_pid, &name);
 
@@ -1499,15 +1536,15 @@ static int manager_dispatch_sigchld(Manager *m) {
 
                         /* And now figure out the unit this belongs
                          * to, it might be multiple... */
-                        u = manager_get_unit_by_pid(m, si.si_pid);
-                        if (u)
-                                invoke_sigchld_event(m, u, &si);
-                        u = hashmap_get(m->watch_pids1, LONG_TO_PTR(si.si_pid));
-                        if (u)
-                                invoke_sigchld_event(m, u, &si);
-                        u = hashmap_get(m->watch_pids2, LONG_TO_PTR(si.si_pid));
-                        if (u)
-                                invoke_sigchld_event(m, u, &si);
+                        u1 = manager_get_unit_by_pid(m, si.si_pid);
+                        if (u1)
+                                invoke_sigchld_event(m, u1, &si);
+                        u2 = hashmap_get(m->watch_pids1, LONG_TO_PTR(si.si_pid));
+                        if (u2 && u2 != u1)
+                                invoke_sigchld_event(m, u2, &si);
+                        u3 = hashmap_get(m->watch_pids2, LONG_TO_PTR(si.si_pid));
+                        if (u3 && u3 != u2 && u3 != u1)
+                                invoke_sigchld_event(m, u3, &si);
                 }
 
                 /* And now, we actually reap the zombie. */
@@ -2460,10 +2497,8 @@ void manager_check_finished(Manager *m) {
 
         if (hashmap_size(m->jobs) > 0) {
 
-                if (m->jobs_in_progress_event_source) {
-                        sd_event_source_set_time(m->jobs_in_progress_event_source,
-                                                 now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
-                }
+                if (m->jobs_in_progress_event_source)
+                        sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
 
                 return;
         }
@@ -2485,6 +2520,8 @@ void manager_check_finished(Manager *m) {
 
         dual_timestamp_get(&m->finish_timestamp);
 
+        m->start_timeout_event_source = sd_event_source_unref(m->start_timeout_event_source);
+
         if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0) {
 
                 /* Note that m->kernel_usec.monotonic is always at 0,
@@ -2549,7 +2586,8 @@ void manager_check_finished(Manager *m) {
         bus_manager_send_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec);
 
         sd_notifyf(false,
-                   "READY=1\nSTATUS=Startup finished in %s.",
+                   "READY=1\n"
+                   "STATUS=Startup finished in %s.",
                    format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC));
 }