chiark / gitweb /
core: when enabling a generated unit file, return a clean error
[elogind.git] / src / libelogind / sd-bus / bus-container.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2013 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <fcntl.h>
21 #include <unistd.h>
22
23 #include "bus-container.h"
24 #include "bus-internal.h"
25 #include "bus-socket.h"
26 #include "fd-util.h"
27 #include "process-util.h"
28 #include "util.h"
29
30 int bus_container_connect_socket(sd_bus *b) {
31         _cleanup_close_pair_ int pair[2] = { -1, -1 };
32         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
33         pid_t child;
34         siginfo_t si;
35         int r, error_buf = 0;
36         ssize_t n;
37
38         assert(b);
39         assert(b->input_fd < 0);
40         assert(b->output_fd < 0);
41         assert(b->nspid > 0 || b->machine);
42
43         if (b->nspid <= 0) {
44                 r = container_get_leader(b->machine, &b->nspid);
45                 if (r < 0)
46                         return r;
47         }
48
49         r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
50         if (r < 0)
51                 return r;
52
53         b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
54         if (b->input_fd < 0)
55                 return -errno;
56
57         b->output_fd = b->input_fd;
58
59         bus_socket_setup(b);
60
61         if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
62                 return -errno;
63
64         child = fork();
65         if (child < 0)
66                 return -errno;
67
68         if (child == 0) {
69                 pid_t grandchild;
70
71                 pair[0] = safe_close(pair[0]);
72
73                 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
74                 if (r < 0)
75                         _exit(EXIT_FAILURE);
76
77                 /* We just changed PID namespace, however it will only
78                  * take effect on the children we now fork. Hence,
79                  * let's fork another time, and connect from this
80                  * grandchild, so that SO_PEERCRED of our connection
81                  * comes from a process from within the container, and
82                  * not outside of it */
83
84                 grandchild = fork();
85                 if (grandchild < 0)
86                         _exit(EXIT_FAILURE);
87
88                 if (grandchild == 0) {
89
90                         r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
91                         if (r < 0) {
92                                 /* Try to send error up */
93                                 error_buf = errno;
94                                 (void) write(pair[1], &error_buf, sizeof(error_buf));
95                                 _exit(EXIT_FAILURE);
96                         }
97
98                         _exit(EXIT_SUCCESS);
99                 }
100
101                 r = wait_for_terminate(grandchild, &si);
102                 if (r < 0)
103                         _exit(EXIT_FAILURE);
104
105                 if (si.si_code != CLD_EXITED)
106                         _exit(EXIT_FAILURE);
107
108                 _exit(si.si_status);
109         }
110
111         pair[1] = safe_close(pair[1]);
112
113         r = wait_for_terminate(child, &si);
114         if (r < 0)
115                 return r;
116
117         n = read(pair[0], &error_buf, sizeof(error_buf));
118         if (n < 0)
119                 return -errno;
120
121         if (n > 0) {
122                 if (n != sizeof(error_buf))
123                         return -EIO;
124
125                 if (error_buf < 0)
126                         return -EIO;
127
128                 if (error_buf == EINPROGRESS)
129                         return 1;
130
131                 if (error_buf > 0)
132                         return -error_buf;
133         }
134
135         if (si.si_code != CLD_EXITED)
136                 return -EIO;
137
138         if (si.si_status != EXIT_SUCCESS)
139                 return -EIO;
140
141         return bus_socket_start_auth(b);
142 }
143
144 int bus_container_connect_kernel(sd_bus *b) {
145         _cleanup_close_pair_ int pair[2] = { -1, -1 };
146         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
147         union {
148                 struct cmsghdr cmsghdr;
149                 uint8_t buf[CMSG_SPACE(sizeof(int))];
150         } control = {};
151         int error_buf = 0;
152         struct iovec iov = {
153                 .iov_base = &error_buf,
154                 .iov_len = sizeof(error_buf),
155         };
156         struct msghdr mh = {
157                 .msg_control = &control,
158                 .msg_controllen = sizeof(control),
159                 .msg_iov = &iov,
160                 .msg_iovlen = 1,
161         };
162         struct cmsghdr *cmsg;
163         pid_t child;
164         siginfo_t si;
165         int r, fd = -1;
166         ssize_t n;
167
168         assert(b);
169         assert(b->input_fd < 0);
170         assert(b->output_fd < 0);
171         assert(b->nspid > 0 || b->machine);
172
173         if (b->nspid <= 0) {
174                 r = container_get_leader(b->machine, &b->nspid);
175                 if (r < 0)
176                         return r;
177         }
178
179         r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
180         if (r < 0)
181                 return r;
182
183         if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
184                 return -errno;
185
186         child = fork();
187         if (child < 0)
188                 return -errno;
189
190         if (child == 0) {
191                 pid_t grandchild;
192
193                 pair[0] = safe_close(pair[0]);
194
195                 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
196                 if (r < 0)
197                         _exit(EXIT_FAILURE);
198
199                 /* We just changed PID namespace, however it will only
200                  * take effect on the children we now fork. Hence,
201                  * let's fork another time, and connect from this
202                  * grandchild, so that kdbus only sees the credentials
203                  * of this process which comes from within the
204                  * container, and not outside of it */
205
206                 grandchild = fork();
207                 if (grandchild < 0)
208                         _exit(EXIT_FAILURE);
209
210                 if (grandchild == 0) {
211                         fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
212                         if (fd < 0) {
213                                 /* Try to send error up */
214                                 error_buf = errno;
215                                 (void) write(pair[1], &error_buf, sizeof(error_buf));
216                                 _exit(EXIT_FAILURE);
217                         }
218
219                         r = send_one_fd(pair[1], fd, 0);
220                         if (r < 0)
221                                 _exit(EXIT_FAILURE);
222
223                         _exit(EXIT_SUCCESS);
224                 }
225
226                 r = wait_for_terminate(grandchild, &si);
227                 if (r < 0)
228                         _exit(EXIT_FAILURE);
229
230                 if (si.si_code != CLD_EXITED)
231                         _exit(EXIT_FAILURE);
232
233                 _exit(si.si_status);
234         }
235
236         pair[1] = safe_close(pair[1]);
237
238         r = wait_for_terminate(child, &si);
239         if (r < 0)
240                 return r;
241
242         n = recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
243         if (n < 0)
244                 return -errno;
245
246         CMSG_FOREACH(cmsg, &mh) {
247                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
248                         int *fds;
249                         unsigned n_fds;
250
251                         assert(fd < 0);
252
253                         fds = (int*) CMSG_DATA(cmsg);
254                         n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
255
256                         if (n_fds != 1) {
257                                 close_many(fds, n_fds);
258                                 return -EIO;
259                         }
260
261                         fd = fds[0];
262                 }
263         }
264
265         /* If there's an fd passed, we are good. */
266         if (fd >= 0) {
267                 b->input_fd = b->output_fd = fd;
268                 return bus_kernel_take_fd(b);
269         }
270
271         /* If there's an error passed, use it */
272         if (n == sizeof(error_buf) && error_buf > 0)
273                 return -error_buf;
274
275         /* Otherwise, we have no clue */
276         return -EIO;
277 }