]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/manager.c
tree-wide: make sure our control buffers are properly aligned
[thirdparty/systemd.git] / src / core / manager.c
index 148df8d523b1cebaacd08d19c7ab0232bb19bf12..09dc92d599a4c2ae1900623a8be36bf651f0ddc8 100644 (file)
@@ -313,9 +313,9 @@ static int manager_check_ask_password(Manager *m) {
                                     m->ask_password_inotify_fd, EPOLLIN,
                                     manager_dispatch_ask_password_fd, m);
                 if (r < 0) {
-                        log_error_errno(errno, "Failed to add event source for /run/systemd/ask-password: %m");
+                        log_error_errno(r, "Failed to add event source for /run/systemd/ask-password: %m");
                         manager_close_ask_password(m);
-                        return -errno;
+                        return r;
                 }
 
                 (void) sd_event_source_set_description(m->ask_password_event_source, "manager-ask-password");
@@ -1968,7 +1968,6 @@ int manager_load_unit_prepare(
         int r;
 
         assert(m);
-        assert(name || path);
         assert(_ret);
 
         /* This will prepare the unit for loading, but not actually
@@ -1977,8 +1976,13 @@ int manager_load_unit_prepare(
         if (path && !is_path(path))
                 return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
 
-        if (!name)
+        if (!name) {
+                /* 'name' and 'path' must not both be null. Check here 'path' using assert_se() to
+                 * workaround a bug in gcc that generates a -Wnonnull warning when calling basename(),
+                 * but this cannot be possible in any code path (See #6119). */
+                assert_se(path);
                 name = basename(path);
+        }
 
         t = unit_name_to_type(name);
 
@@ -2284,6 +2288,20 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui
         return 0;
 }
 
+static bool manager_process_barrier_fd(const char *buf, FDSet *fds) {
+        assert(buf);
+
+        /* nothing else must be sent when using BARRIER=1 */
+        if (STR_IN_SET(buf, "BARRIER=1", "BARRIER=1\n")) {
+                if (fdset_size(fds) != 1)
+                        log_warning("Got incorrect number of fds with BARRIER=1, closing them.");
+                return true;
+        } else if (startswith(buf, "BARRIER=1\n") || strstr(buf, "\nBARRIER=1\n") || endswith(buf, "\nBARRIER=1"))
+                log_warning("Extra notification messages sent with BARRIER=1, ignoring everything.");
+
+        return false;
+}
+
 static void manager_invoke_notify_message(
                 Manager *m,
                 Unit *u,
@@ -2331,11 +2349,8 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
                 .iov_base = buf,
                 .iov_len = sizeof(buf)-1,
         };
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
-                            CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
-        } control = {};
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) +
+                         CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control;
         struct msghdr msghdr = {
                 .msg_iov = &iovec,
                 .msg_iovlen = 1,
@@ -2360,20 +2375,20 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
                 return 0;
         }
 
-        n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC|MSG_TRUNC);
-        if (n < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
-                        return 0; /* Spurious wakeup, try again */
-
-                /* If this is any other, real error, then let's stop processing this socket. This of course means we
-                 * won't take notification messages anymore, but that's still better than busy looping around this:
-                 * being woken up over and over again but being unable to actually read the message off the socket. */
-                return log_error_errno(errno, "Failed to receive notification message: %m");
-        }
+        n = recvmsg_safe(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC|MSG_TRUNC);
+        if (IN_SET(n, -EAGAIN, -EINTR))
+                return 0; /* Spurious wakeup, try again */
+        if (n < 0)
+                /* If this is any other, real error, then let's stop processing this socket. This of course
+                 * means we won't take notification messages anymore, but that's still better than busy
+                 * looping around this: being woken up over and over again but being unable to actually read
+                 * the message off the socket. */
+                return log_error_errno(n, "Failed to receive notification message: %m");
 
         CMSG_FOREACH(cmsg, &msghdr) {
                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
 
+                        assert(!fd_array);
                         fd_array = (int*) CMSG_DATA(cmsg);
                         n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
 
@@ -2381,6 +2396,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
                            cmsg->cmsg_type == SCM_CREDENTIALS &&
                            cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
 
+                        assert(!ucred);
                         ucred = (struct ucred*) CMSG_DATA(cmsg);
                 }
         }
@@ -2416,6 +2432,10 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
         /* Make sure it's NUL-terminated. */
         buf[n] = 0;
 
+        /* possibly a barrier fd, let's see */
+        if (manager_process_barrier_fd(buf, fds))
+                return 0;
+
         /* Increase the generation counter used for filtering out duplicate unit invocations. */
         m->notifygen++;
 
@@ -2931,12 +2951,10 @@ int manager_loop(Manager *m) {
                 if (manager_dispatch_dbus_queue(m) > 0)
                         continue;
 
-                /* Sleep for half the watchdog time */
-                if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m)) {
-                        wait_usec = m->runtime_watchdog / 2;
-                        if (wait_usec <= 0)
-                                wait_usec = 1;
-                } else
+                /* Sleep for watchdog runtime wait time */
+                if (MANAGER_IS_SYSTEM(m))
+                        wait_usec = watchdog_runtime_wait();
+                else
                         wait_usec = USEC_INFINITY;
 
                 r = sd_event_run(m->event, wait_usec);
@@ -3107,7 +3125,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
 }
 
 int manager_open_serialization(Manager *m, FILE **_f) {
-        int fd;
+        _cleanup_close_ int fd = -1;
         FILE *f;
 
         assert(_f);
@@ -3116,11 +3134,9 @@ int manager_open_serialization(Manager *m, FILE **_f) {
         if (fd < 0)
                 return fd;
 
-        f = fdopen(fd, "w+");
-        if (!f) {
-                safe_close(fd);
+        f = take_fdopen(&fd, "w+");
+        if (!f)
                 return -errno;
-        }
 
         *_f = f;
         return 0;
@@ -4249,6 +4265,11 @@ ManagerState manager_state(Manager *m) {
 
         assert(m);
 
+        /* Is the special shutdown target active or queued? If so, we are in shutdown state */
+        u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
+        if (u && unit_active_or_pending(u))
+                return MANAGER_STOPPING;
+
         /* Did we ever finish booting? If not then we are still starting up */
         if (!MANAGER_IS_FINISHED(m)) {
 
@@ -4259,11 +4280,6 @@ ManagerState manager_state(Manager *m) {
                 return MANAGER_STARTING;
         }
 
-        /* Is the special shutdown target active or queued? If so, we are in shutdown state */
-        u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
-        if (u && unit_active_or_pending(u))
-                return MANAGER_STOPPING;
-
         if (MANAGER_IS_SYSTEM(m)) {
                 /* Are the rescue or emergency targets active or queued? If so we are in maintenance state */
                 u = manager_get_unit(m, SPECIAL_RESCUE_TARGET);