]>
Commit | Line | Data |
---|---|---|
724e753c MN |
1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2009 | |
5 | * | |
6 | * Authors: | |
7 | * Daniel Lezcano <dlezcano at fr.ibm.com> | |
8 | * | |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 | */ | |
23 | ||
24 | #include <stdio.h> | |
25 | #include <errno.h> | |
26 | #include <unistd.h> | |
27 | #include <signal.h> | |
91480a0f | 28 | #include <fcntl.h> |
724e753c MN |
29 | #include <sys/socket.h> |
30 | #include <sys/un.h> | |
31 | #include <sys/poll.h> | |
32 | #include <sys/param.h> | |
2a59a681 SH |
33 | #include <malloc.h> |
34 | #include <stdlib.h> | |
724e753c | 35 | |
00b3c2e2 CLG |
36 | #include <lxc/log.h> |
37 | #include <lxc/conf.h> | |
38 | #include <lxc/start.h> /* for struct lxc_handler */ | |
2a59a681 | 39 | #include <lxc/utils.h> |
724e753c MN |
40 | |
41 | #include "commands.h" | |
42 | #include "mainloop.h" | |
43 | #include "af_unix.h" | |
adaeaa99 | 44 | #include "config.h" |
724e753c | 45 | |
ded1d23f DL |
46 | /* |
47 | * This file provides the different functions to have the client | |
48 | * and the server to communicate | |
49 | * | |
50 | * Each command is transactional, the client send a request to | |
51 | * the server and the server answer the request with a message | |
52 | * giving the request's status (zero or a negative errno value). | |
53 | * | |
54 | * Each command is wrapped in a ancillary message in order to pass | |
55 | * a credential making possible to the server to check if the client | |
56 | * is allowed to ask for this command or not. | |
57 | * | |
58 | */ | |
59 | ||
724e753c MN |
60 | lxc_log_define(lxc_commands, lxc); |
61 | ||
13f5be62 SH |
62 | static int fill_sock_name(char *path, int len, const char *name, |
63 | const char *inpath) | |
64 | { | |
65 | char *lxcpath = NULL; | |
2a59a681 | 66 | int ret; |
13f5be62 SH |
67 | |
68 | if (!inpath) { | |
69 | lxcpath = default_lxc_path(); | |
70 | if (!lxcpath) { | |
71 | ERROR("Out of memory getting lxcpath"); | |
72 | return -1; | |
73 | } | |
2a59a681 | 74 | } |
13f5be62 SH |
75 | ret = snprintf(path, len, "%s/%s/command", lxcpath ? lxcpath : inpath, name); |
76 | if (lxcpath) | |
77 | free(lxcpath); | |
78 | ||
2a59a681 SH |
79 | if (ret < 0 || ret >= len) { |
80 | ERROR("Name too long"); | |
81 | return -1; | |
82 | } | |
83 | return 0; | |
84 | } | |
ded1d23f | 85 | |
724e753c MN |
86 | static int receive_answer(int sock, struct lxc_answer *answer) |
87 | { | |
88 | int ret; | |
89 | ||
90 | ret = lxc_af_unix_recv_fd(sock, &answer->fd, answer, sizeof(*answer)); | |
91 | if (ret < 0) | |
92 | ERROR("failed to receive answer for the command"); | |
93 | ||
94 | return ret; | |
95 | } | |
96 | ||
43eb6f29 | 97 | static int __lxc_command(const char *name, struct lxc_command *command, |
13f5be62 | 98 | int *stopped, int stay_connected, const char *lxcpath) |
724e753c | 99 | { |
724e753c | 100 | int sock, ret = -1; |
46968ea3 DL |
101 | char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 }; |
102 | char *offset = &path[1]; | |
2a59a681 | 103 | int len; |
724e753c | 104 | |
9ba8130c | 105 | len = sizeof(path)-1; |
13f5be62 | 106 | if (fill_sock_name(offset, len, name, lxcpath)) |
9ba8130c | 107 | return -1; |
724e753c | 108 | |
46968ea3 | 109 | sock = lxc_af_unix_connect(path); |
d97b36f8 DL |
110 | if (sock < 0 && errno == ECONNREFUSED) { |
111 | *stopped = 1; | |
112 | return -1; | |
113 | } | |
114 | ||
724e753c | 115 | if (sock < 0) { |
d97b36f8 | 116 | SYSERROR("failed to connect to '@%s'", offset); |
724e753c MN |
117 | return -1; |
118 | } | |
119 | ||
120 | ret = lxc_af_unix_send_credential(sock, &command->request, | |
121 | sizeof(command->request)); | |
122 | if (ret < 0) { | |
d97b36f8 | 123 | SYSERROR("failed to send request to '@%s'", offset); |
1362f2eb | 124 | goto out; |
724e753c MN |
125 | } |
126 | ||
127 | if (ret != sizeof(command->request)) { | |
d97b36f8 | 128 | SYSERROR("message partially sent to '@%s'", offset); |
1362f2eb | 129 | goto out; |
724e753c MN |
130 | } |
131 | ||
132 | ret = receive_answer(sock, &command->answer); | |
724e753c | 133 | out: |
43eb6f29 DL |
134 | if (!stay_connected || ret < 0) |
135 | close(sock); | |
136 | ||
1362f2eb | 137 | return ret; |
724e753c MN |
138 | } |
139 | ||
43eb6f29 | 140 | extern int lxc_command(const char *name, |
13f5be62 SH |
141 | struct lxc_command *command, int *stopped, |
142 | const char *lxcpath) | |
43eb6f29 | 143 | { |
13f5be62 | 144 | return __lxc_command(name, command, stopped, 0, lxcpath); |
43eb6f29 DL |
145 | } |
146 | ||
147 | extern int lxc_command_connected(const char *name, | |
13f5be62 SH |
148 | struct lxc_command *command, int *stopped, |
149 | const char *lxcpath) | |
43eb6f29 | 150 | { |
13f5be62 | 151 | return __lxc_command(name, command, stopped, 1, lxcpath); |
43eb6f29 DL |
152 | } |
153 | ||
154 | ||
13f5be62 | 155 | pid_t get_init_pid(const char *name, const char *lxcpath) |
26b2d152 MN |
156 | { |
157 | struct lxc_command command = { | |
158 | .request = { .type = LXC_COMMAND_PID }, | |
159 | }; | |
160 | ||
161 | int ret, stopped = 0; | |
162 | ||
13f5be62 | 163 | ret = lxc_command(name, &command, &stopped, lxcpath); |
ebdd307d | 164 | if (ret < 0 && stopped) |
26b2d152 | 165 | return -1; |
26b2d152 MN |
166 | |
167 | if (ret < 0) { | |
168 | ERROR("failed to send command"); | |
169 | return -1; | |
170 | } | |
171 | ||
172 | if (command.answer.ret) { | |
173 | ERROR("failed to retrieve the init pid: %s", | |
174 | strerror(-command.answer.ret)); | |
175 | return -1; | |
176 | } | |
177 | ||
178 | return command.answer.pid; | |
179 | } | |
180 | ||
13f5be62 | 181 | int lxc_get_clone_flags(const char *name, const char *lxcpath) |
d5088cf2 CS |
182 | { |
183 | struct lxc_command command = { | |
184 | .request = { .type = LXC_COMMAND_CLONE_FLAGS }, | |
185 | }; | |
186 | ||
187 | int ret, stopped = 0; | |
188 | ||
13f5be62 | 189 | ret = lxc_command(name, &command, &stopped, lxcpath); |
d5088cf2 CS |
190 | if (ret < 0 && stopped) |
191 | return -1; | |
192 | ||
193 | if (ret < 0) { | |
194 | ERROR("failed to send command"); | |
195 | return -1; | |
196 | } | |
197 | ||
198 | return command.answer.ret; | |
199 | } | |
200 | ||
ded1d23f DL |
201 | extern void lxc_console_remove_fd(int, struct lxc_tty_info *); |
202 | extern int lxc_console_callback(int, struct lxc_request *, struct lxc_handler *); | |
203 | extern int lxc_stop_callback(int, struct lxc_request *, struct lxc_handler *); | |
204 | extern int lxc_state_callback(int, struct lxc_request *, struct lxc_handler *); | |
81c75799 | 205 | extern int lxc_pid_callback(int, struct lxc_request *, struct lxc_handler *); |
d5088cf2 | 206 | extern int lxc_clone_flags_callback(int, struct lxc_request *, struct lxc_handler *); |
724e753c MN |
207 | |
208 | static int trigger_command(int fd, struct lxc_request *request, | |
ded1d23f | 209 | struct lxc_handler *handler) |
724e753c | 210 | { |
ded1d23f | 211 | typedef int (*callback)(int, struct lxc_request *, struct lxc_handler *); |
724e753c MN |
212 | |
213 | callback cb[LXC_COMMAND_MAX] = { | |
d5088cf2 CS |
214 | [LXC_COMMAND_TTY] = lxc_console_callback, |
215 | [LXC_COMMAND_STOP] = lxc_stop_callback, | |
216 | [LXC_COMMAND_STATE] = lxc_state_callback, | |
217 | [LXC_COMMAND_PID] = lxc_pid_callback, | |
218 | [LXC_COMMAND_CLONE_FLAGS] = lxc_clone_flags_callback, | |
724e753c MN |
219 | }; |
220 | ||
221 | if (request->type < 0 || request->type >= LXC_COMMAND_MAX) | |
222 | return -1; | |
223 | ||
224 | return cb[request->type](fd, request, handler); | |
225 | } | |
226 | ||
227 | static void command_fd_cleanup(int fd, struct lxc_handler *handler, | |
ded1d23f | 228 | struct lxc_epoll_descr *descr) |
724e753c | 229 | { |
fae349da | 230 | lxc_console_remove_fd(fd, &handler->conf->tty_info); |
724e753c MN |
231 | lxc_mainloop_del_handler(descr, fd); |
232 | close(fd); | |
233 | } | |
234 | ||
ded1d23f | 235 | static int command_handler(int fd, void *data, struct lxc_epoll_descr *descr) |
724e753c MN |
236 | { |
237 | int ret; | |
238 | struct lxc_request request; | |
239 | struct lxc_handler *handler = data; | |
240 | ||
241 | ret = lxc_af_unix_rcv_credential(fd, &request, sizeof(request)); | |
0a3ec350 | 242 | if (ret == -EACCES) { |
3cc5de36 MN |
243 | /* we don't care for the peer, just send and close */ |
244 | struct lxc_answer answer = { .ret = ret }; | |
245 | send(fd, &answer, sizeof(answer), 0); | |
246 | goto out_close; | |
ded1d23f DL |
247 | } |
248 | ||
249 | if (ret < 0) { | |
724e753c MN |
250 | SYSERROR("failed to receive data on command socket"); |
251 | goto out_close; | |
252 | } | |
253 | ||
254 | if (!ret) { | |
255 | DEBUG("peer has disconnected"); | |
256 | goto out_close; | |
257 | } | |
258 | ||
259 | if (ret != sizeof(request)) { | |
260 | WARN("partial request, ignored"); | |
261 | goto out_close; | |
262 | } | |
263 | ||
264 | ret = trigger_command(fd, &request, handler); | |
265 | if (ret) { | |
266 | /* this is not an error, but only a request to close fd */ | |
267 | ret = 0; | |
268 | goto out_close; | |
269 | } | |
270 | ||
271 | out: | |
272 | return ret; | |
273 | out_close: | |
274 | command_fd_cleanup(fd, handler, descr); | |
275 | goto out; | |
276 | } | |
277 | ||
278 | static int incoming_command_handler(int fd, void *data, | |
279 | struct lxc_epoll_descr *descr) | |
280 | { | |
ded1d23f | 281 | int opt = 1, ret = -1, connection; |
724e753c MN |
282 | |
283 | connection = accept(fd, NULL, 0); | |
284 | if (connection < 0) { | |
285 | SYSERROR("failed to accept connection"); | |
286 | return -1; | |
287 | } | |
288 | ||
9ccb2dbc DL |
289 | if (fcntl(connection, F_SETFD, FD_CLOEXEC)) { |
290 | SYSERROR("failed to set close-on-exec on incoming connection"); | |
291 | goto out_close; | |
292 | } | |
293 | ||
0a3ec350 DL |
294 | if (setsockopt(connection, SOL_SOCKET, |
295 | SO_PASSCRED, &opt, sizeof(opt))) { | |
724e753c MN |
296 | SYSERROR("failed to enable credential on socket"); |
297 | goto out_close; | |
298 | } | |
299 | ||
300 | ret = lxc_mainloop_add_handler(descr, connection, command_handler, data); | |
301 | if (ret) { | |
302 | ERROR("failed to add handler"); | |
303 | goto out_close; | |
304 | } | |
305 | ||
306 | out: | |
307 | return ret; | |
308 | ||
309 | out_close: | |
310 | close(connection); | |
311 | goto out; | |
312 | } | |
313 | ||
13f5be62 SH |
314 | extern int lxc_command_init(const char *name, struct lxc_handler *handler, |
315 | const char *lxcpath) | |
724e753c | 316 | { |
d2e30e99 | 317 | int fd; |
46968ea3 DL |
318 | char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 }; |
319 | char *offset = &path[1]; | |
2a59a681 | 320 | int len; |
724e753c | 321 | |
9ba8130c | 322 | len = sizeof(path)-1; |
13f5be62 | 323 | if (fill_sock_name(offset, len, name, lxcpath)) |
9ba8130c | 324 | return -1; |
724e753c | 325 | |
46968ea3 | 326 | fd = lxc_af_unix_open(path, SOCK_STREAM, 0); |
724e753c | 327 | if (fd < 0) { |
97d3756c SH |
328 | ERROR("failed (%d) to create the command service point %s", errno, offset); |
329 | if (errno == EADDRINUSE) { | |
330 | ERROR("##"); | |
331 | ERROR("# The container appears to be already running!"); | |
332 | ERROR("##"); | |
333 | } | |
724e753c MN |
334 | return -1; |
335 | } | |
336 | ||
91480a0f DL |
337 | if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { |
338 | SYSERROR("failed to set sigfd to close-on-exec"); | |
339 | close(fd); | |
340 | return -1; | |
341 | } | |
342 | ||
d2e30e99 DE |
343 | handler->conf->maincmd_fd = fd; |
344 | return 0; | |
345 | } | |
346 | ||
347 | extern int lxc_command_mainloop_add(const char *name, | |
348 | struct lxc_epoll_descr *descr, | |
349 | struct lxc_handler *handler) | |
350 | { | |
351 | int ret, fd = handler->conf->maincmd_fd; | |
352 | ||
0a3ec350 DL |
353 | ret = lxc_mainloop_add_handler(descr, fd, incoming_command_handler, |
354 | handler); | |
724e753c MN |
355 | if (ret) { |
356 | ERROR("failed to add handler for command socket"); | |
357 | close(fd); | |
358 | } | |
359 | ||
360 | return ret; | |
361 | } |