chiark / gitweb /
bus: when connecting to a container's kdbus instance, enter namespace first
[elogind.git] / src / libsystemd-bus / bus-container.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <fcntl.h>
24
25 #include "util.h"
26 #include "fileio.h"
27 #include "bus-internal.h"
28 #include "bus-socket.h"
29 #include "bus-container.h"
30
31 int bus_container_connect_socket(sd_bus *b) {
32         _cleanup_close_ int nsfd = -1, rootfd = -1;
33         pid_t leader, child;
34         siginfo_t si;
35         int r;
36
37         assert(b);
38         assert(b->input_fd < 0);
39         assert(b->output_fd < 0);
40
41         r = container_get_leader(b->machine, &leader);
42         if (r < 0)
43                 return r;
44
45         r = namespace_open(leader, &nsfd, &rootfd);
46         if (r < 0)
47                 return r;
48
49         b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
50         if (b->input_fd < 0)
51                 return -errno;
52
53         b->output_fd = b->input_fd;
54
55         r = bus_socket_setup(b);
56         if (r < 0)
57                 return r;
58
59         child = fork();
60         if (child < 0)
61                 return -errno;
62
63         if (child == 0) {
64
65                 r = namespace_enter(nsfd, rootfd);
66                 if (r < 0)
67                         _exit(255);
68
69                 r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
70                 if (r < 0) {
71                         if (errno == EINPROGRESS)
72                                 _exit(1);
73
74                         _exit(255);
75                 }
76
77                 _exit(EXIT_SUCCESS);
78         }
79
80         r = wait_for_terminate(child, &si);
81         if (r < 0)
82                 return r;
83
84         if (si.si_code != CLD_EXITED)
85                 return -EIO;
86
87         if (si.si_status == 1)
88                 return 1;
89
90         if (si.si_status != EXIT_SUCCESS)
91                 return -EIO;
92
93         return bus_socket_start_auth(b);
94 }
95
96 int bus_container_connect_kernel(sd_bus *b) {
97         _cleanup_close_pipe_ int pair[2] = { -1, -1 };
98         _cleanup_close_ int nsfd = -1, rootfd = -1;
99         union {
100                 struct cmsghdr cmsghdr;
101                 uint8_t buf[CMSG_SPACE(sizeof(int))];
102         } control = {};
103         struct msghdr mh = {
104                 .msg_control = &control,
105                 .msg_controllen = sizeof(control),
106         };
107         struct cmsghdr *cmsg;
108         pid_t leader, child;
109         siginfo_t si;
110         int r;
111         _cleanup_close_ int fd = -1;
112
113         assert(b);
114         assert(b->input_fd < 0);
115         assert(b->output_fd < 0);
116
117         r = container_get_leader(b->machine, &leader);
118         if (r < 0)
119                 return r;
120
121         r = namespace_open(leader, &nsfd, &rootfd);
122         if (r < 0)
123                 return r;
124
125         if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
126                 return -errno;
127
128         child = fork();
129         if (child < 0)
130                 return -errno;
131
132         if (child == 0) {
133                 close_nointr_nofail(pair[0]);
134                 pair[0] = -1;
135
136                 r = namespace_enter(nsfd, rootfd);
137                 if (r < 0)
138                         _exit(EXIT_FAILURE);
139
140                 fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
141                 if (fd < 0)
142                         _exit(EXIT_FAILURE);
143
144                 cmsg = CMSG_FIRSTHDR(&mh);
145                 cmsg->cmsg_level = SOL_SOCKET;
146                 cmsg->cmsg_type = SCM_RIGHTS;
147                 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
148                 memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
149
150                 mh.msg_controllen = cmsg->cmsg_len;
151
152                 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
153                         _exit(EXIT_FAILURE);
154
155                 _exit(EXIT_SUCCESS);
156         }
157
158         close_nointr_nofail(pair[1]);
159         pair[1] = -1;
160
161         if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
162                 return -errno;
163
164         for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
165                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
166                         int *fds;
167                         unsigned n_fds;
168
169                         fds = (int*) CMSG_DATA(cmsg);
170                         n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
171
172                         if (n_fds != 1) {
173                                 close_many(fds, n_fds);
174                                 return -EIO;
175                         }
176
177                         fd = fds[0];
178                 }
179
180         r = wait_for_terminate(child, &si);
181         if (r < 0)
182                 return r;
183
184         if (si.si_code != CLD_EXITED)
185                 return -EIO;
186
187         if (si.si_status != EXIT_SUCCESS)
188                 return -EIO;
189
190         b->input_fd = b->output_fd = fd;
191         fd = -1;
192
193         return bus_kernel_take_fd(b);
194 }