chiark / gitweb /
[3/5] Apply missing fixes from upstream
[elogind.git] / src / libelogind / sd-bus / bus-container.c
index 101e4af18d6b78546728f29b841c724aa1989a60..435ec92d6fab3c7915fbc1eff63501bb5d899d4a 100644 (file)
 #include "bus-container.h"
 
 int bus_container_connect_socket(sd_bus *b) {
+        _cleanup_close_pair_ int pair[2] = { -1, -1 };
         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
         pid_t child;
         siginfo_t si;
-        int r;
+        int r, error_buf = 0;
+        ssize_t n;
 
         assert(b);
         assert(b->input_fd < 0);
@@ -57,6 +59,9 @@ int bus_container_connect_socket(sd_bus *b) {
 
         bus_socket_setup(b);
 
+        if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
+                return -errno;
+
         child = fork();
         if (child < 0)
                 return -errno;
@@ -64,9 +69,11 @@ int bus_container_connect_socket(sd_bus *b) {
         if (child == 0) {
                 pid_t grandchild;
 
+                pair[0] = safe_close(pair[0]);
+
                 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
                 if (r < 0)
-                        _exit(255);
+                        _exit(EXIT_FAILURE);
 
                 /* We just changed PID namespace, however it will only
                  * take effect on the children we now fork. Hence,
@@ -77,16 +84,16 @@ int bus_container_connect_socket(sd_bus *b) {
 
                 grandchild = fork();
                 if (grandchild < 0)
-                        _exit(255);
+                        _exit(EXIT_FAILURE);
 
                 if (grandchild == 0) {
 
                         r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
                         if (r < 0) {
-                                if (errno == EINPROGRESS)
-                                        _exit(1);
-
-                                _exit(255);
+                                /* Try to send error up */
+                                error_buf = errno;
+                                (void) write(pair[1], &error_buf, sizeof(error_buf));
+                                _exit(EXIT_FAILURE);
                         }
 
                         _exit(EXIT_SUCCESS);
@@ -94,24 +101,41 @@ int bus_container_connect_socket(sd_bus *b) {
 
                 r = wait_for_terminate(grandchild, &si);
                 if (r < 0)
-                        _exit(255);
+                        _exit(EXIT_FAILURE);
 
                 if (si.si_code != CLD_EXITED)
-                        _exit(255);
+                        _exit(EXIT_FAILURE);
 
                 _exit(si.si_status);
         }
 
+        pair[1] = safe_close(pair[1]);
+
         r = wait_for_terminate(child, &si);
         if (r < 0)
                 return r;
 
+        n = read(pair[0], &error_buf, sizeof(error_buf));
+        if (n < 0)
+                return -errno;
+
+        if (n > 0) {
+                if (n != sizeof(error_buf))
+                        return -EIO;
+
+                if (error_buf < 0)
+                        return -EIO;
+
+                if (error_buf == EINPROGRESS)
+                        return 1;
+
+                if (error_buf > 0)
+                        return -error_buf;
+        }
+
         if (si.si_code != CLD_EXITED)
                 return -EIO;
 
-        if (si.si_status == 1)
-                return 1;
-
         if (si.si_status != EXIT_SUCCESS)
                 return -EIO;
 
@@ -125,15 +149,22 @@ int bus_container_connect_kernel(sd_bus *b) {
                 struct cmsghdr cmsghdr;
                 uint8_t buf[CMSG_SPACE(sizeof(int))];
         } control = {};
+        int error_buf = 0;
+        struct iovec iov = {
+                .iov_base = &error_buf,
+                .iov_len = sizeof(error_buf),
+        };
         struct msghdr mh = {
                 .msg_control = &control,
                 .msg_controllen = sizeof(control),
+                .msg_iov = &iov,
+                .msg_iovlen = 1,
         };
         struct cmsghdr *cmsg;
         pid_t child;
         siginfo_t si;
-        int r;
-        _cleanup_close_ int fd = -1;
+        int r, fd = -1;
+        ssize_t n;
 
         assert(b);
         assert(b->input_fd < 0);
@@ -150,7 +181,7 @@ int bus_container_connect_kernel(sd_bus *b) {
         if (r < 0)
                 return r;
 
-        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+        if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
                 return -errno;
 
         child = fork();
@@ -178,20 +209,16 @@ int bus_container_connect_kernel(sd_bus *b) {
                         _exit(EXIT_FAILURE);
 
                 if (grandchild == 0) {
-
                         fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
-                        if (fd < 0)
+                        if (fd < 0) {
+                                /* Try to send error up */
+                                error_buf = errno;
+                                (void) write(pair[1], &error_buf, sizeof(error_buf));
                                 _exit(EXIT_FAILURE);
+                        }
 
-                        cmsg = CMSG_FIRSTHDR(&mh);
-                        cmsg->cmsg_level = SOL_SOCKET;
-                        cmsg->cmsg_type = SCM_RIGHTS;
-                        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-                        memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
-
-                        mh.msg_controllen = cmsg->cmsg_len;
-
-                        if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
+                        r = send_one_fd(pair[1], fd, 0);
+                        if (r < 0)
                                 _exit(EXIT_FAILURE);
 
                         _exit(EXIT_SUCCESS);
@@ -213,20 +240,17 @@ int bus_container_connect_kernel(sd_bus *b) {
         if (r < 0)
                 return r;
 
-        if (si.si_code != CLD_EXITED)
-                return -EIO;
-
-        if (si.si_status != EXIT_SUCCESS)
-                return -EIO;
-
-        if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
+        n = recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
+        if (n < 0)
                 return -errno;
 
-        CMSG_FOREACH(cmsg, &mh)
+        CMSG_FOREACH(cmsg, &mh) {
                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
                         int *fds;
                         unsigned n_fds;
 
+                        assert(fd < 0);
+
                         fds = (int*) CMSG_DATA(cmsg);
                         n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
 
@@ -237,9 +261,18 @@ int bus_container_connect_kernel(sd_bus *b) {
 
                         fd = fds[0];
                 }
+        }
+
+        /* If there's an fd passed, we are good. */
+        if (fd >= 0) {
+                b->input_fd = b->output_fd = fd;
+                return bus_kernel_take_fd(b);
+        }
 
-        b->input_fd = b->output_fd = fd;
-        fd = -1;
+        /* If there's an error passed, use it */
+        if (n == sizeof(error_buf) && error_buf > 0)
+                return -error_buf;
 
-        return bus_kernel_take_fd(b);
+        /* Otherwise, we have no clue */
+        return -EIO;
 }