]>
Commit | Line | Data |
---|---|---|
724e753c MN |
1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2009 | |
5 | * | |
6 | * Authors: | |
9afe19d6 | 7 | * Daniel Lezcano <daniel.lezcano at free.fr> |
724e753c MN |
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 | |
250b1eec | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
724e753c MN |
22 | */ |
23 | ||
24 | #include <stdio.h> | |
25 | #include <errno.h> | |
26 | #include <unistd.h> | |
27 | #include <signal.h> | |
91480a0f | 28 | #include <fcntl.h> |
37515ebd | 29 | #include <poll.h> |
724e753c MN |
30 | #include <sys/socket.h> |
31 | #include <sys/un.h> | |
724e753c | 32 | #include <sys/param.h> |
2a59a681 SH |
33 | #include <malloc.h> |
34 | #include <stdlib.h> | |
724e753c | 35 | |
f2363e38 ÇO |
36 | #include "log.h" |
37 | #include "lxc.h" | |
38 | #include "conf.h" | |
39 | #include "start.h" /* for struct lxc_handler */ | |
40 | #include "utils.h" | |
41 | #include "cgroup.h" | |
724e753c | 42 | #include "commands.h" |
bbf5cf35 | 43 | #include "commands_utils.h" |
b5159817 | 44 | #include "console.h" |
ef6e34ee | 45 | #include "confile.h" |
dbc9832d | 46 | #include "lxclock.h" |
724e753c | 47 | #include "mainloop.h" |
dbc9832d | 48 | #include "monitor.h" |
724e753c | 49 | #include "af_unix.h" |
adaeaa99 | 50 | #include "config.h" |
724e753c | 51 | |
ded1d23f | 52 | /* |
ef6e34ee DE |
53 | * This file provides the different functions for clients to |
54 | * query/command the server. The client is typically some lxc | |
55 | * tool and the server is typically the container (ie. lxc-start). | |
ded1d23f | 56 | * |
ef6e34ee DE |
57 | * Each command is transactional, the clients send a request to |
58 | * the server and the server answers the request with a message | |
ded1d23f | 59 | * giving the request's status (zero or a negative errno value). |
ec64264d | 60 | * Both the request and response may contain additional data. |
ded1d23f DL |
61 | * |
62 | * Each command is wrapped in a ancillary message in order to pass | |
63 | * a credential making possible to the server to check if the client | |
64 | * is allowed to ask for this command or not. | |
80bcb053 SH |
65 | * |
66 | * IMPORTANTLY: Note that semantics for current commands are fixed. If you | |
67 | * wish to make any changes to how, say, LXC_CMD_GET_CONFIG_ITEM works by | |
68 | * adding information to the end of cmd.data, then you must introduce a new | |
69 | * LXC_CMD_GET_CONFIG_ITEM_V2 define with a new number. You may wish to | |
70 | * also mark LXC_CMD_GET_CONFIG_ITEM deprecated in commands.h. | |
71 | * | |
72 | * This is necessary in order to avoid having a newly compiled lxc command | |
73 | * communicating with a running (old) monitor from crashing the running | |
74 | * container. | |
ded1d23f DL |
75 | */ |
76 | ||
724e753c MN |
77 | lxc_log_define(lxc_commands, lxc); |
78 | ||
ef6e34ee | 79 | static const char *lxc_cmd_str(lxc_cmd_t cmd) |
724e753c | 80 | { |
74a3920a | 81 | static const char * const cmdname[LXC_CMD_MAX] = { |
54446942 | 82 | [LXC_CMD_CONSOLE] = "console", |
86a78f17 | 83 | [LXC_CMD_CONSOLE_WINCH] = "console_winch", |
54446942 CB |
84 | [LXC_CMD_STOP] = "stop", |
85 | [LXC_CMD_GET_STATE] = "get_state", | |
86 | [LXC_CMD_GET_INIT_PID] = "get_init_pid", | |
87 | [LXC_CMD_GET_CLONE_FLAGS] = "get_clone_flags", | |
88 | [LXC_CMD_GET_CGROUP] = "get_cgroup", | |
89 | [LXC_CMD_GET_CONFIG_ITEM] = "get_config_item", | |
90 | [LXC_CMD_GET_NAME] = "get_name", | |
91 | [LXC_CMD_GET_LXCPATH] = "get_lxcpath", | |
92 | [LXC_CMD_ADD_STATE_CLIENT] = "add_state_client", | |
ef6e34ee | 93 | }; |
724e753c | 94 | |
f371aca9 | 95 | if (cmd >= LXC_CMD_MAX) |
ef6e34ee DE |
96 | return "Unknown cmd"; |
97 | return cmdname[cmd]; | |
98 | } | |
99 | ||
100 | /* | |
101 | * lxc_cmd_rsp_recv: Receive a response to a command | |
102 | * | |
103 | * @sock : the socket connected to the container | |
104 | * @cmd : command to put response in | |
105 | * | |
106 | * Returns the size of the response message or < 0 on failure | |
107 | * | |
108 | * Note that if the command response datalen > 0, then data is | |
109 | * a malloc()ed buffer and should be free()ed by the caller. If | |
110 | * the response data is <= a void * worth of data, it will be | |
111 | * stored directly in data and datalen will be 0. | |
112 | * | |
113 | * As a special case, the response for LXC_CMD_CONSOLE is created | |
0115f8fd DE |
114 | * here as it contains an fd for the master pty passed through the |
115 | * unix socket. | |
ef6e34ee DE |
116 | */ |
117 | static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd) | |
118 | { | |
2945ea97 | 119 | int ret, rspfd; |
ef6e34ee DE |
120 | struct lxc_cmd_rsp *rsp = &cmd->rsp; |
121 | ||
ae467c54 | 122 | ret = lxc_abstract_unix_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp)); |
ef6e34ee | 123 | if (ret < 0) { |
08aa08fe CB |
124 | WARN("Command %s failed to receive response: %s.", |
125 | lxc_cmd_str(cmd->req.cmd), strerror(errno)); | |
ef6e34ee DE |
126 | return -1; |
127 | } | |
86a78f17 | 128 | TRACE("Command \"%s received response", lxc_cmd_str(cmd->req.cmd)); |
ef6e34ee DE |
129 | |
130 | if (cmd->req.cmd == LXC_CMD_CONSOLE) { | |
131 | struct lxc_cmd_console_rsp_data *rspdata; | |
132 | ||
0115f8fd DE |
133 | /* recv() returns 0 bytes when a tty cannot be allocated, |
134 | * rsp->ret is < 0 when the peer permission check failed | |
135 | */ | |
136 | if (ret == 0 || rsp->ret < 0) | |
137 | return 0; | |
138 | ||
ef6e34ee DE |
139 | rspdata = malloc(sizeof(*rspdata)); |
140 | if (!rspdata) { | |
08aa08fe | 141 | ERROR("Command %s couldn't allocate response buffer.", |
ef6e34ee DE |
142 | lxc_cmd_str(cmd->req.cmd)); |
143 | return -1; | |
144 | } | |
0115f8fd | 145 | rspdata->masterfd = rspfd; |
ef6e34ee DE |
146 | rspdata->ttynum = PTR_TO_INT(rsp->data); |
147 | rsp->data = rspdata; | |
148 | } | |
149 | ||
860e7c43 CB |
150 | if (rsp->datalen == 0) { |
151 | DEBUG("command %s response data length is 0", | |
152 | lxc_cmd_str(cmd->req.cmd)); | |
ae5c8b8e | 153 | return ret; |
860e7c43 | 154 | } |
ef6e34ee | 155 | if (rsp->datalen > LXC_CMD_DATA_MAX) { |
08aa08fe | 156 | ERROR("Command %s response data %d too long.", |
ef6e34ee DE |
157 | lxc_cmd_str(cmd->req.cmd), rsp->datalen); |
158 | errno = EFBIG; | |
2ac9aafc SH |
159 | return -1; |
160 | } | |
ef6e34ee DE |
161 | |
162 | rsp->data = malloc(rsp->datalen); | |
163 | if (!rsp->data) { | |
08aa08fe | 164 | ERROR("Command %s was unable to allocate response buffer.", |
ef6e34ee DE |
165 | lxc_cmd_str(cmd->req.cmd)); |
166 | return -1; | |
167 | } | |
168 | ret = recv(sock, rsp->data, rsp->datalen, 0); | |
169 | if (ret != rsp->datalen) { | |
08aa08fe CB |
170 | ERROR("Command %s failed to receive response data: %s.", |
171 | lxc_cmd_str(cmd->req.cmd), strerror(errno)); | |
ef6e34ee DE |
172 | if (ret >= 0) |
173 | ret = -1; | |
174 | } | |
724e753c MN |
175 | |
176 | return ret; | |
177 | } | |
178 | ||
ef6e34ee DE |
179 | /* |
180 | * lxc_cmd_rsp_send: Send a command response | |
181 | * | |
182 | * @fd : file descriptor of socket to send response on | |
183 | * @rsp : response to send | |
184 | * | |
185 | * Returns 0 on success, < 0 on failure | |
186 | */ | |
187 | static int lxc_cmd_rsp_send(int fd, struct lxc_cmd_rsp *rsp) | |
188 | { | |
189 | int ret; | |
190 | ||
191 | ret = send(fd, rsp, sizeof(*rsp), 0); | |
192 | if (ret != sizeof(*rsp)) { | |
08aa08fe | 193 | ERROR("Failed to send command response %d: %s.", ret, |
ef6e34ee DE |
194 | strerror(errno)); |
195 | return -1; | |
196 | } | |
197 | ||
198 | if (rsp->datalen > 0) { | |
199 | ret = send(fd, rsp->data, rsp->datalen, 0); | |
200 | if (ret != rsp->datalen) { | |
08aa08fe CB |
201 | WARN("Failed to send command response data %d: %s.", |
202 | ret, strerror(errno)); | |
ef6e34ee DE |
203 | return -1; |
204 | } | |
205 | } | |
206 | return 0; | |
207 | } | |
208 | ||
c01c2be6 CB |
209 | static int lxc_cmd_send(const char *name, struct lxc_cmd_rr *cmd, |
210 | const char *lxcpath, const char *hashed_sock_name) | |
211 | { | |
212 | int client_fd; | |
213 | int ret = -1; | |
214 | ||
c01c2be6 CB |
215 | client_fd = lxc_cmd_connect(name, lxcpath, hashed_sock_name); |
216 | if (client_fd < 0 && client_fd == -ECONNREFUSED) | |
217 | return -ECONNREFUSED; | |
218 | else if (client_fd < 0) | |
219 | return -1; | |
220 | ||
221 | TRACE("Command \"%s\" connected to command socket", | |
222 | lxc_cmd_str(cmd->req.cmd)); | |
223 | ||
224 | ret = lxc_abstract_unix_send_credential(client_fd, &cmd->req, sizeof(cmd->req)); | |
225 | if (ret != sizeof(cmd->req)) { | |
226 | close(client_fd); | |
227 | ||
228 | if (errno == EPIPE) | |
229 | return -EPIPE; | |
230 | ||
231 | if (ret >= 0) | |
232 | return -EMSGSIZE; | |
233 | ||
234 | return -1; | |
235 | } | |
236 | ||
237 | TRACE("Command \"%s\" requested data of length %d", | |
238 | lxc_cmd_str(cmd->req.cmd), cmd->req.datalen); | |
239 | ||
240 | if (cmd->req.datalen > 0) { | |
241 | ret = send(client_fd, cmd->req.data, cmd->req.datalen, MSG_NOSIGNAL); | |
242 | if (ret != cmd->req.datalen) { | |
243 | close(client_fd); | |
244 | ||
245 | if (errno == EPIPE) | |
246 | return -EPIPE; | |
247 | ||
248 | if (ret >= 0) | |
249 | return -EMSGSIZE; | |
250 | ||
251 | return -1; | |
252 | } | |
253 | } | |
254 | ||
255 | return client_fd; | |
256 | } | |
257 | ||
ef6e34ee DE |
258 | /* |
259 | * lxc_cmd: Connect to the specified running container, send it a command | |
260 | * request and collect the response | |
261 | * | |
262 | * @name : name of container to connect to | |
1e8cfdf6 | 263 | * @cmd : command with initialized request to send |
ef6e34ee DE |
264 | * @stopped : output indicator if the container was not running |
265 | * @lxcpath : the lxcpath in which the container is running | |
266 | * | |
267 | * Returns the size of the response message on success, < 0 on failure | |
268 | * | |
269 | * Note that there is a special case for LXC_CMD_CONSOLE. For this command | |
270 | * the fd cannot be closed because it is used as a placeholder to indicate | |
271 | * that a particular tty slot is in use. The fd is also used as a signal to | |
272 | * the container that when the caller dies or closes the fd, the container | |
0115f8fd DE |
273 | * will notice the fd on its side of the socket in its mainloop select and |
274 | * then free the slot with lxc_cmd_fd_cleanup(). The socket fd will be | |
275 | * returned in the cmd response structure. | |
ef6e34ee DE |
276 | */ |
277 | static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped, | |
88556fd7 | 278 | const char *lxcpath, const char *hashed_sock_name) |
724e753c | 279 | { |
c01c2be6 | 280 | int client_fd, ret = -1; |
dbc9832d CB |
281 | bool stay_connected = false; |
282 | ||
283 | if (cmd->req.cmd == LXC_CMD_CONSOLE || | |
54446942 | 284 | cmd->req.cmd == LXC_CMD_ADD_STATE_CLIENT) |
dbc9832d | 285 | stay_connected = true; |
724e753c | 286 | |
0ecf64b5 SH |
287 | *stopped = 0; |
288 | ||
c01c2be6 CB |
289 | TRACE("command %s tries to connect command socket", |
290 | lxc_cmd_str(cmd->req.cmd)); | |
724e753c | 291 | |
c01c2be6 CB |
292 | client_fd = lxc_cmd_send(name, cmd, lxcpath, hashed_sock_name); |
293 | if (client_fd < 0) { | |
294 | TRACE("command %s failed to connect command socket: %s", | |
295 | lxc_cmd_str(cmd->req.cmd), strerror(errno)); | |
296 | if (client_fd == -ECONNREFUSED) { | |
ef6e34ee | 297 | *stopped = 1; |
c01c2be6 | 298 | return -1; |
3f903c04 CB |
299 | } |
300 | ||
c01c2be6 | 301 | if (client_fd == -EPIPE) |
6168ff15 | 302 | goto epipe; |
724e753c | 303 | |
c01c2be6 | 304 | goto out; |
724e753c MN |
305 | } |
306 | ||
c01c2be6 | 307 | ret = lxc_cmd_rsp_recv(client_fd, cmd); |
724e753c | 308 | out: |
4575a9f9 | 309 | if (!stay_connected || ret <= 0) |
c01c2be6 | 310 | close(client_fd); |
0115f8fd | 311 | if (stay_connected && ret > 0) |
c01c2be6 | 312 | cmd->rsp.ret = client_fd; |
43eb6f29 | 313 | |
1362f2eb | 314 | return ret; |
6168ff15 SH |
315 | |
316 | epipe: | |
6168ff15 SH |
317 | *stopped = 1; |
318 | return 0; | |
724e753c MN |
319 | } |
320 | ||
b494d2dd SH |
321 | int lxc_try_cmd(const char *name, const char *lxcpath) |
322 | { | |
0ecf64b5 | 323 | int stopped, ret; |
b494d2dd SH |
324 | struct lxc_cmd_rr cmd = { |
325 | .req = { .cmd = LXC_CMD_GET_INIT_PID }, | |
326 | }; | |
327 | ||
88556fd7 | 328 | ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); |
b494d2dd SH |
329 | |
330 | if (stopped) | |
331 | return 0; | |
332 | if (ret > 0 && cmd.rsp.ret < 0) { | |
333 | errno = cmd.rsp.ret; | |
334 | return -1; | |
335 | } | |
336 | if (ret > 0) | |
337 | return 0; | |
338 | ||
339 | /* | |
340 | * At this point we weren't denied access, and the | |
341 | * container *was* started. There was some inexplicable | |
342 | * error in the protocol. | |
343 | * I'm not clear on whether we should return -1 here, but | |
344 | * we didn't receive a -EACCES, so technically it's not that | |
345 | * we're not allowed to control the container - it's just not | |
346 | * behaving. | |
347 | */ | |
348 | return 0; | |
349 | } | |
350 | ||
ef6e34ee DE |
351 | /* Implentations of the commands and their callbacks */ |
352 | ||
353 | /* | |
354 | * lxc_cmd_get_init_pid: Get pid of the container's init process | |
355 | * | |
356 | * @name : name of container to connect to | |
357 | * @lxcpath : the lxcpath in which the container is running | |
358 | * | |
359 | * Returns the pid on success, < 0 on failure | |
360 | */ | |
361 | pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath) | |
43eb6f29 | 362 | { |
0ecf64b5 | 363 | int ret, stopped; |
ef6e34ee DE |
364 | struct lxc_cmd_rr cmd = { |
365 | .req = { .cmd = LXC_CMD_GET_INIT_PID }, | |
366 | }; | |
367 | ||
88556fd7 | 368 | ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); |
ef6e34ee DE |
369 | if (ret < 0) |
370 | return ret; | |
371 | ||
372 | return PTR_TO_INT(cmd.rsp.data); | |
43eb6f29 DL |
373 | } |
374 | ||
ef6e34ee DE |
375 | static int lxc_cmd_get_init_pid_callback(int fd, struct lxc_cmd_req *req, |
376 | struct lxc_handler *handler) | |
43eb6f29 | 377 | { |
ef6e34ee DE |
378 | struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->pid) }; |
379 | ||
380 | return lxc_cmd_rsp_send(fd, &rsp); | |
43eb6f29 DL |
381 | } |
382 | ||
ef6e34ee DE |
383 | /* |
384 | * lxc_cmd_get_clone_flags: Get clone flags container was spawned with | |
385 | * | |
386 | * @name : name of container to connect to | |
387 | * @lxcpath : the lxcpath in which the container is running | |
388 | * | |
389 | * Returns the clone flags on success, < 0 on failure | |
390 | */ | |
391 | int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath) | |
392 | { | |
0ecf64b5 | 393 | int ret, stopped; |
ef6e34ee DE |
394 | struct lxc_cmd_rr cmd = { |
395 | .req = { .cmd = LXC_CMD_GET_CLONE_FLAGS }, | |
396 | }; | |
397 | ||
88556fd7 | 398 | ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); |
ef6e34ee DE |
399 | if (ret < 0) |
400 | return ret; | |
43eb6f29 | 401 | |
ef6e34ee DE |
402 | return PTR_TO_INT(cmd.rsp.data); |
403 | } | |
404 | ||
405 | static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req, | |
406 | struct lxc_handler *handler) | |
26b2d152 | 407 | { |
ef6e34ee DE |
408 | struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->clone_flags) }; |
409 | ||
410 | return lxc_cmd_rsp_send(fd, &rsp); | |
411 | } | |
412 | ||
413 | /* | |
414 | * lxc_cmd_get_cgroup_path: Calculate a container's cgroup path for a | |
415 | * particular subsystem. This is the cgroup path relative to the root | |
416 | * of the cgroup filesystem. | |
417 | * | |
ef6e34ee DE |
418 | * @name : name of container to connect to |
419 | * @lxcpath : the lxcpath in which the container is running | |
b98f7d6e | 420 | * @subsystem : the subsystem being asked about |
ef6e34ee DE |
421 | * |
422 | * Returns the path on success, NULL on failure. The caller must free() the | |
423 | * returned path. | |
424 | */ | |
b98f7d6e SH |
425 | char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath, |
426 | const char *subsystem) | |
ef6e34ee | 427 | { |
0ecf64b5 | 428 | int ret, stopped; |
ef6e34ee | 429 | struct lxc_cmd_rr cmd = { |
b98f7d6e SH |
430 | .req = { |
431 | .cmd = LXC_CMD_GET_CGROUP, | |
432 | .datalen = strlen(subsystem)+1, | |
433 | .data = subsystem, | |
434 | }, | |
26b2d152 MN |
435 | }; |
436 | ||
88556fd7 | 437 | ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); |
3f903c04 CB |
438 | if (ret < 0) { |
439 | TRACE("command %s failed for container \"%s\": %s.", | |
440 | lxc_cmd_str(cmd.req.cmd), name, strerror(errno)); | |
ef6e34ee | 441 | return NULL; |
3f903c04 | 442 | } |
ef6e34ee DE |
443 | |
444 | if (!ret) { | |
3f903c04 | 445 | WARN("container \"%s\" has stopped before sending its state", name); |
ef6e34ee DE |
446 | return NULL; |
447 | } | |
448 | ||
449 | if (cmd.rsp.ret < 0 || cmd.rsp.datalen < 0) { | |
3f903c04 | 450 | ERROR("command %s failed for container \"%s\": %s", |
08aa08fe | 451 | lxc_cmd_str(cmd.req.cmd), name, strerror(-cmd.rsp.ret)); |
ef6e34ee DE |
452 | return NULL; |
453 | } | |
454 | ||
3f903c04 CB |
455 | TRACE("command %s successful for container \"%s\"", |
456 | lxc_cmd_str(cmd.req.cmd), name); | |
457 | ||
ef6e34ee DE |
458 | return cmd.rsp.data; |
459 | } | |
460 | ||
461 | static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, | |
462 | struct lxc_handler *handler) | |
463 | { | |
b98f7d6e | 464 | struct lxc_cmd_rsp rsp; |
4fb3cba5 | 465 | const char *path; |
b98f7d6e SH |
466 | |
467 | if (req->datalen < 1) | |
468 | return -1; | |
8900b9eb | 469 | |
d4ef7c50 | 470 | path = cgroup_get_cgroup(handler, req->data); |
b98f7d6e SH |
471 | if (!path) |
472 | return -1; | |
473 | rsp.datalen = strlen(path) + 1, | |
4fb3cba5 | 474 | rsp.data = (char *)path; |
4f17323e | 475 | rsp.ret = 0; |
ef6e34ee DE |
476 | |
477 | return lxc_cmd_rsp_send(fd, &rsp); | |
478 | } | |
479 | ||
480 | /* | |
481 | * lxc_cmd_get_config_item: Get config item the running container | |
482 | * | |
483 | * @name : name of container to connect to | |
7fa3f2e9 | 484 | * @item : the configuration item to retrieve (ex: lxc.net.0.veth.pair) |
ef6e34ee DE |
485 | * @lxcpath : the lxcpath in which the container is running |
486 | * | |
487 | * Returns the item on success, NULL on failure. The caller must free() the | |
488 | * returned item. | |
489 | */ | |
490 | char *lxc_cmd_get_config_item(const char *name, const char *item, | |
491 | const char *lxcpath) | |
492 | { | |
0ecf64b5 | 493 | int ret, stopped; |
ef6e34ee DE |
494 | struct lxc_cmd_rr cmd = { |
495 | .req = { .cmd = LXC_CMD_GET_CONFIG_ITEM, | |
496 | .data = item, | |
497 | .datalen = strlen(item)+1, | |
498 | }, | |
499 | }; | |
500 | ||
88556fd7 | 501 | ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); |
ef6e34ee DE |
502 | if (ret < 0) |
503 | return NULL; | |
504 | ||
505 | if (cmd.rsp.ret == 0) | |
506 | return cmd.rsp.data; | |
507 | return NULL; | |
508 | } | |
509 | ||
510 | static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req, | |
511 | struct lxc_handler *handler) | |
512 | { | |
513 | int cilen; | |
514 | struct lxc_cmd_rsp rsp; | |
515 | char *cidata; | |
30aec088 | 516 | struct lxc_config_t *item; |
ef6e34ee DE |
517 | |
518 | memset(&rsp, 0, sizeof(rsp)); | |
300df83e | 519 | item = lxc_get_config(req->data); |
30aec088 CB |
520 | if (!item) |
521 | goto err1; | |
cccd2219 | 522 | cilen = item->get(req->data, NULL, 0, handler->conf, NULL); |
ef6e34ee DE |
523 | if (cilen <= 0) |
524 | goto err1; | |
525 | ||
526 | cidata = alloca(cilen + 1); | |
cccd2219 | 527 | if (item->get(req->data, cidata, cilen + 1, handler->conf, NULL) != cilen) |
ef6e34ee DE |
528 | goto err1; |
529 | cidata[cilen] = '\0'; | |
530 | rsp.data = cidata; | |
531 | rsp.datalen = cilen + 1; | |
532 | rsp.ret = 0; | |
533 | goto out; | |
534 | ||
535 | err1: | |
536 | rsp.ret = -1; | |
537 | out: | |
538 | return lxc_cmd_rsp_send(fd, &rsp); | |
539 | } | |
540 | ||
541 | /* | |
542 | * lxc_cmd_get_state: Get current state of the container | |
543 | * | |
544 | * @name : name of container to connect to | |
545 | * @lxcpath : the lxcpath in which the container is running | |
546 | * | |
547 | * Returns the state on success, < 0 on failure | |
548 | */ | |
dbc9832d | 549 | int lxc_cmd_get_state(const char *name, const char *lxcpath) |
ef6e34ee | 550 | { |
0ecf64b5 | 551 | int ret, stopped; |
ef6e34ee DE |
552 | struct lxc_cmd_rr cmd = { |
553 | .req = { .cmd = LXC_CMD_GET_STATE } | |
554 | }; | |
26b2d152 | 555 | |
88556fd7 | 556 | ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); |
ebdd307d | 557 | if (ret < 0 && stopped) |
ef6e34ee DE |
558 | return STOPPED; |
559 | ||
560 | if (ret < 0) | |
26b2d152 | 561 | return -1; |
26b2d152 | 562 | |
ef6e34ee | 563 | if (!ret) { |
08aa08fe | 564 | WARN("Container \"%s\" has stopped before sending its state.", name); |
ef6e34ee DE |
565 | return -1; |
566 | } | |
567 | ||
08aa08fe | 568 | DEBUG("Container \"%s\" is in \"%s\" state.", name, |
ef6e34ee DE |
569 | lxc_state2str(PTR_TO_INT(cmd.rsp.data))); |
570 | return PTR_TO_INT(cmd.rsp.data); | |
571 | } | |
572 | ||
573 | static int lxc_cmd_get_state_callback(int fd, struct lxc_cmd_req *req, | |
574 | struct lxc_handler *handler) | |
575 | { | |
576 | struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->state) }; | |
577 | ||
578 | return lxc_cmd_rsp_send(fd, &rsp); | |
579 | } | |
580 | ||
581 | /* | |
582 | * lxc_cmd_stop: Stop the container previously started with lxc_start. All | |
583 | * the processes running inside this container will be killed. | |
584 | * | |
585 | * @name : name of container to connect to | |
586 | * @lxcpath : the lxcpath in which the container is running | |
587 | * | |
588 | * Returns 0 on success, < 0 on failure | |
589 | */ | |
590 | int lxc_cmd_stop(const char *name, const char *lxcpath) | |
591 | { | |
0ecf64b5 | 592 | int ret, stopped; |
ef6e34ee DE |
593 | struct lxc_cmd_rr cmd = { |
594 | .req = { .cmd = LXC_CMD_STOP }, | |
595 | }; | |
596 | ||
88556fd7 | 597 | ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); |
26b2d152 | 598 | if (ret < 0) { |
ef6e34ee | 599 | if (stopped) { |
08aa08fe | 600 | INFO("Container \"%s\" is already stopped.", name); |
ef6e34ee DE |
601 | return 0; |
602 | } | |
26b2d152 MN |
603 | return -1; |
604 | } | |
605 | ||
95d5b147 SH |
606 | /* we do not expect any answer, because we wait for the connection to be |
607 | * closed | |
608 | */ | |
609 | if (ret > 0) { | |
08aa08fe CB |
610 | ERROR("Failed to stop container \"%s\": %s.", name, |
611 | strerror(-cmd.rsp.ret)); | |
26b2d152 MN |
612 | return -1; |
613 | } | |
614 | ||
08aa08fe | 615 | INFO("Container \"%s\" has stopped.", name); |
ef6e34ee | 616 | return 0; |
26b2d152 MN |
617 | } |
618 | ||
ef6e34ee DE |
619 | static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, |
620 | struct lxc_handler *handler) | |
d5088cf2 | 621 | { |
ef6e34ee | 622 | struct lxc_cmd_rsp rsp; |
ef6e34ee DE |
623 | int stopsignal = SIGKILL; |
624 | ||
625 | if (handler->conf->stopsignal) | |
626 | stopsignal = handler->conf->stopsignal; | |
627 | memset(&rsp, 0, sizeof(rsp)); | |
628 | rsp.ret = kill(handler->pid, stopsignal); | |
629 | if (!rsp.ret) { | |
4fb3cba5 DE |
630 | /* we can't just use lxc_unfreeze() since we are already in the |
631 | * context of handling the STOP cmd in lxc-start, and calling | |
632 | * lxc_unfreeze() would do another cmd (GET_CGROUP) which would | |
633 | * deadlock us | |
634 | */ | |
635 | if (cgroup_unfreeze(handler)) | |
95d5b147 | 636 | return 0; |
08aa08fe | 637 | ERROR("Failed to unfreeze container \"%s\".", handler->name); |
ecfcb3f0 | 638 | rsp.ret = -1; |
ef6e34ee DE |
639 | } |
640 | ||
641 | return lxc_cmd_rsp_send(fd, &rsp); | |
642 | } | |
d5088cf2 | 643 | |
b5159817 DE |
644 | /* |
645 | * lxc_cmd_console_winch: To process as if a SIGWINCH were received | |
646 | * | |
647 | * @name : name of container to connect to | |
648 | * @lxcpath : the lxcpath in which the container is running | |
649 | * | |
650 | * Returns 0 on success, < 0 on failure | |
651 | */ | |
652 | int lxc_cmd_console_winch(const char *name, const char *lxcpath) | |
653 | { | |
0ecf64b5 | 654 | int ret, stopped; |
b5159817 DE |
655 | struct lxc_cmd_rr cmd = { |
656 | .req = { .cmd = LXC_CMD_CONSOLE_WINCH }, | |
657 | }; | |
658 | ||
88556fd7 | 659 | ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); |
b5159817 DE |
660 | if (ret < 0) |
661 | return ret; | |
662 | ||
663 | return 0; | |
664 | } | |
665 | ||
666 | static int lxc_cmd_console_winch_callback(int fd, struct lxc_cmd_req *req, | |
667 | struct lxc_handler *handler) | |
668 | { | |
669 | struct lxc_cmd_rsp rsp = { .data = 0 }; | |
670 | ||
671 | lxc_console_sigwinch(SIGWINCH); | |
672 | return lxc_cmd_rsp_send(fd, &rsp); | |
673 | } | |
674 | ||
ef6e34ee DE |
675 | /* |
676 | * lxc_cmd_console: Open an fd to a tty in the container | |
677 | * | |
678 | * @name : name of container to connect to | |
679 | * @ttynum : in: the tty to open or -1 for next available | |
680 | * : out: the tty allocated | |
681 | * @fd : out: file descriptor for master side of pty | |
682 | * @lxcpath : the lxcpath in which the container is running | |
683 | * | |
0115f8fd | 684 | * Returns fd holding tty allocated on success, < 0 on failure |
ef6e34ee DE |
685 | */ |
686 | int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath) | |
687 | { | |
0ecf64b5 | 688 | int ret, stopped; |
ef6e34ee DE |
689 | struct lxc_cmd_console_rsp_data *rspdata; |
690 | struct lxc_cmd_rr cmd = { | |
691 | .req = { .cmd = LXC_CMD_CONSOLE, .data = INT_TO_PTR(*ttynum) }, | |
692 | }; | |
d5088cf2 | 693 | |
88556fd7 | 694 | ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); |
ef6e34ee DE |
695 | if (ret < 0) |
696 | return ret; | |
0115f8fd DE |
697 | |
698 | if (cmd.rsp.ret < 0) { | |
08aa08fe | 699 | ERROR("Console access denied: %s.", strerror(-cmd.rsp.ret)); |
ef6e34ee DE |
700 | ret = -1; |
701 | goto out; | |
702 | } | |
d5088cf2 | 703 | |
0115f8fd | 704 | if (ret == 0) { |
08aa08fe | 705 | ERROR("Console %d invalid, busy or all consoles busy.", *ttynum); |
0115f8fd | 706 | ret = -1; |
ef6e34ee DE |
707 | goto out; |
708 | } | |
ef6e34ee DE |
709 | |
710 | rspdata = cmd.rsp.data; | |
0115f8fd | 711 | if (rspdata->masterfd < 0) { |
08aa08fe | 712 | ERROR("Unable to allocate fd for tty %d.", rspdata->ttynum); |
ef6e34ee DE |
713 | goto out; |
714 | } | |
715 | ||
0115f8fd DE |
716 | ret = cmd.rsp.ret; /* sock fd */ |
717 | *fd = rspdata->masterfd; | |
ef6e34ee | 718 | *ttynum = rspdata->ttynum; |
08aa08fe | 719 | INFO("tty %d allocated fd %d sock %d.", rspdata->ttynum, *fd, ret); |
ef6e34ee DE |
720 | out: |
721 | free(cmd.rsp.data); | |
722 | return ret; | |
723 | } | |
724 | ||
725 | static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req, | |
726 | struct lxc_handler *handler) | |
727 | { | |
728 | int ttynum = PTR_TO_INT(req->data); | |
b5159817 | 729 | int masterfd; |
ef6e34ee DE |
730 | struct lxc_cmd_rsp rsp; |
731 | ||
b5159817 DE |
732 | masterfd = lxc_console_allocate(handler->conf, fd, &ttynum); |
733 | if (masterfd < 0) | |
ef6e34ee DE |
734 | goto out_close; |
735 | ||
ef6e34ee DE |
736 | memset(&rsp, 0, sizeof(rsp)); |
737 | rsp.data = INT_TO_PTR(ttynum); | |
ae467c54 | 738 | if (lxc_abstract_unix_send_fds(fd, &masterfd, 1, &rsp, sizeof(rsp)) < 0) { |
08aa08fe | 739 | ERROR("Failed to send tty to client."); |
b5159817 | 740 | lxc_console_free(handler->conf, fd); |
ef6e34ee DE |
741 | goto out_close; |
742 | } | |
743 | ||
ef6e34ee DE |
744 | return 0; |
745 | ||
746 | out_close: | |
747 | /* special indicator to lxc_cmd_handler() to close the fd and do | |
748 | * related cleanup | |
749 | */ | |
750 | return 1; | |
d5088cf2 CS |
751 | } |
752 | ||
88556fd7 ÇO |
753 | /* |
754 | * lxc_cmd_get_name: Returns the name of the container | |
755 | * | |
756 | * @hashed_sock_name: hashed socket name | |
757 | * | |
758 | * Returns the name on success, NULL on failure. | |
759 | */ | |
760 | char *lxc_cmd_get_name(const char *hashed_sock_name) | |
761 | { | |
762 | int ret, stopped; | |
763 | struct lxc_cmd_rr cmd = { | |
764 | .req = { .cmd = LXC_CMD_GET_NAME}, | |
765 | }; | |
766 | ||
767 | ret = lxc_cmd(NULL, &cmd, &stopped, NULL, hashed_sock_name); | |
768 | if (ret < 0) { | |
769 | return NULL; | |
770 | } | |
771 | ||
772 | if (cmd.rsp.ret == 0) | |
773 | return cmd.rsp.data; | |
774 | return NULL; | |
775 | } | |
776 | ||
777 | static int lxc_cmd_get_name_callback(int fd, struct lxc_cmd_req *req, | |
778 | struct lxc_handler *handler) | |
779 | { | |
780 | struct lxc_cmd_rsp rsp; | |
781 | ||
782 | memset(&rsp, 0, sizeof(rsp)); | |
783 | ||
784 | rsp.data = handler->name; | |
785 | rsp.datalen = strlen(handler->name) + 1; | |
786 | rsp.ret = 0; | |
787 | ||
788 | return lxc_cmd_rsp_send(fd, &rsp); | |
789 | } | |
790 | ||
791 | /* | |
792 | * lxc_cmd_get_lxcpath: Returns the lxcpath of the container | |
793 | * | |
794 | * @hashed_sock_name: hashed socket name | |
795 | * | |
796 | * Returns the lxcpath on success, NULL on failure. | |
797 | */ | |
798 | char *lxc_cmd_get_lxcpath(const char *hashed_sock_name) | |
799 | { | |
800 | int ret, stopped; | |
801 | struct lxc_cmd_rr cmd = { | |
802 | .req = { .cmd = LXC_CMD_GET_LXCPATH}, | |
803 | }; | |
724e753c | 804 | |
88556fd7 ÇO |
805 | ret = lxc_cmd(NULL, &cmd, &stopped, NULL, hashed_sock_name); |
806 | if (ret < 0) { | |
807 | return NULL; | |
808 | } | |
809 | ||
810 | if (cmd.rsp.ret == 0) | |
811 | return cmd.rsp.data; | |
812 | return NULL; | |
813 | } | |
814 | ||
815 | static int lxc_cmd_get_lxcpath_callback(int fd, struct lxc_cmd_req *req, | |
816 | struct lxc_handler *handler) | |
817 | { | |
818 | struct lxc_cmd_rsp rsp; | |
819 | ||
820 | memset(&rsp, 0, sizeof(rsp)); | |
821 | ||
822 | rsp.data = (char *)handler->lxcpath; | |
823 | rsp.datalen = strlen(handler->lxcpath) + 1; | |
824 | rsp.ret = 0; | |
825 | ||
826 | return lxc_cmd_rsp_send(fd, &rsp); | |
827 | } | |
ef6e34ee | 828 | |
54446942 | 829 | int lxc_cmd_add_state_client(const char *name, const char *lxcpath, |
92e35018 CB |
830 | lxc_state_t states[MAX_STATE], |
831 | int *state_client_fd) | |
dbc9832d CB |
832 | { |
833 | int stopped; | |
834 | ssize_t ret; | |
835 | int state = -1; | |
dbc9832d CB |
836 | struct lxc_cmd_rr cmd = { |
837 | .req = { | |
54446942 | 838 | .cmd = LXC_CMD_ADD_STATE_CLIENT, |
dbc9832d CB |
839 | .data = states, |
840 | .datalen = (sizeof(lxc_state_t) * MAX_STATE) | |
841 | }, | |
842 | }; | |
843 | ||
54446942 CB |
844 | /* Lock the whole lxc_cmd_add_state_client_callback() call to ensure |
845 | * that lxc_set_state() doesn't cause us to miss a state. | |
dbc9832d CB |
846 | */ |
847 | process_lock(); | |
848 | /* Check if already in requested state. */ | |
849 | state = lxc_getstate(name, lxcpath); | |
850 | if (state < 0) { | |
851 | process_unlock(); | |
852 | TRACE("failed to retrieve state of container: %s", | |
853 | strerror(errno)); | |
854 | return -1; | |
855 | } else if (states[state]) { | |
856 | process_unlock(); | |
857 | TRACE("container is %s state", lxc_state2str(state)); | |
858 | return state; | |
859 | } | |
860 | ||
861 | if ((state == STARTING) && !states[RUNNING] && !states[STOPPING] && !states[STOPPED]) { | |
862 | process_unlock(); | |
863 | TRACE("container is in %s state and caller requested to be " | |
864 | "informed about a previous state", | |
865 | lxc_state2str(state)); | |
866 | return state; | |
867 | } else if ((state == RUNNING) && !states[STOPPING] && !states[STOPPED]) { | |
868 | process_unlock(); | |
869 | TRACE("container is in %s state and caller requested to be " | |
870 | "informed about a previous state", | |
871 | lxc_state2str(state)); | |
872 | return state; | |
873 | } else if ((state == STOPPING) && !states[STOPPED]) { | |
874 | process_unlock(); | |
875 | TRACE("container is in %s state and caller requested to be " | |
876 | "informed about a previous state", | |
877 | lxc_state2str(state)); | |
878 | return state; | |
879 | } else if ((state == STOPPED) || (state == ABORTING)) { | |
880 | process_unlock(); | |
881 | TRACE("container is in %s state and caller requested to be " | |
882 | "informed about a previous state", | |
883 | lxc_state2str(state)); | |
884 | return state; | |
885 | } | |
886 | ||
887 | ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL); | |
888 | process_unlock(); | |
889 | if (ret < 0) { | |
890 | ERROR("failed to execute command: %s", strerror(errno)); | |
891 | return -1; | |
892 | } | |
893 | /* We should now be guaranteed to get an answer from the state sending | |
894 | * function. | |
895 | */ | |
896 | ||
897 | if (cmd.rsp.ret < 0) { | |
898 | ERROR("failed to receive socket fd"); | |
899 | return -1; | |
900 | } | |
901 | ||
92e35018 CB |
902 | *state_client_fd = cmd.rsp.ret; |
903 | return MAX_STATE; | |
dbc9832d CB |
904 | } |
905 | ||
54446942 CB |
906 | static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req, |
907 | struct lxc_handler *handler) | |
dbc9832d CB |
908 | { |
909 | struct lxc_cmd_rsp rsp = {0}; | |
dbc9832d CB |
910 | |
911 | if (req->datalen < 0) { | |
c01c2be6 | 912 | TRACE("Requested datalen was < 0"); |
dbc9832d CB |
913 | return -1; |
914 | } | |
915 | ||
c01c2be6 CB |
916 | if (req->datalen > (sizeof(lxc_state_t) * MAX_STATE)) { |
917 | TRACE("Requested datalen was too large"); | |
dbc9832d CB |
918 | return -1; |
919 | } | |
920 | ||
c01c2be6 CB |
921 | if (!req->data) { |
922 | TRACE("No states requested"); | |
dbc9832d CB |
923 | return -1; |
924 | } | |
925 | ||
c01c2be6 CB |
926 | rsp.ret = lxc_add_state_client(fd, handler, (lxc_state_t *)req->data); |
927 | if (rsp.ret < 0) | |
928 | ERROR("Failed to add state client %d to state client list", fd); | |
929 | else | |
930 | TRACE("Added state client %d to state client list", fd); | |
dbc9832d CB |
931 | |
932 | return lxc_cmd_rsp_send(fd, &rsp); | |
933 | } | |
934 | ||
ef6e34ee | 935 | static int lxc_cmd_process(int fd, struct lxc_cmd_req *req, |
ded1d23f | 936 | struct lxc_handler *handler) |
724e753c | 937 | { |
ef6e34ee DE |
938 | typedef int (*callback)(int, struct lxc_cmd_req *, struct lxc_handler *); |
939 | ||
940 | callback cb[LXC_CMD_MAX] = { | |
54446942 CB |
941 | [LXC_CMD_CONSOLE] = lxc_cmd_console_callback, |
942 | [LXC_CMD_CONSOLE_WINCH] = lxc_cmd_console_winch_callback, | |
943 | [LXC_CMD_STOP] = lxc_cmd_stop_callback, | |
944 | [LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback, | |
945 | [LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_callback, | |
946 | [LXC_CMD_GET_CLONE_FLAGS] = lxc_cmd_get_clone_flags_callback, | |
947 | [LXC_CMD_GET_CGROUP] = lxc_cmd_get_cgroup_callback, | |
948 | [LXC_CMD_GET_CONFIG_ITEM] = lxc_cmd_get_config_item_callback, | |
949 | [LXC_CMD_GET_NAME] = lxc_cmd_get_name_callback, | |
950 | [LXC_CMD_GET_LXCPATH] = lxc_cmd_get_lxcpath_callback, | |
951 | [LXC_CMD_ADD_STATE_CLIENT] = lxc_cmd_add_state_client_callback, | |
724e753c MN |
952 | }; |
953 | ||
f371aca9 | 954 | if (req->cmd >= LXC_CMD_MAX) { |
08aa08fe | 955 | ERROR("Undefined command id %d received.", req->cmd); |
724e753c | 956 | return -1; |
ef6e34ee DE |
957 | } |
958 | return cb[req->cmd](fd, req, handler); | |
724e753c MN |
959 | } |
960 | ||
ef6e34ee | 961 | static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler, |
ded1d23f | 962 | struct lxc_epoll_descr *descr) |
724e753c | 963 | { |
b5159817 | 964 | lxc_console_free(handler->conf, fd); |
724e753c MN |
965 | lxc_mainloop_del_handler(descr, fd); |
966 | close(fd); | |
967 | } | |
968 | ||
84c92abd DE |
969 | static int lxc_cmd_handler(int fd, uint32_t events, void *data, |
970 | struct lxc_epoll_descr *descr) | |
724e753c MN |
971 | { |
972 | int ret; | |
ef6e34ee | 973 | struct lxc_cmd_req req; |
724e753c MN |
974 | struct lxc_handler *handler = data; |
975 | ||
aae93dd3 | 976 | ret = lxc_abstract_unix_rcv_credential(fd, &req, sizeof(req)); |
0a3ec350 | 977 | if (ret == -EACCES) { |
3cc5de36 | 978 | /* we don't care for the peer, just send and close */ |
ef6e34ee DE |
979 | struct lxc_cmd_rsp rsp = { .ret = ret }; |
980 | ||
981 | lxc_cmd_rsp_send(fd, &rsp); | |
3cc5de36 | 982 | goto out_close; |
ded1d23f DL |
983 | } |
984 | ||
c01c2be6 | 985 | TRACE("Processing \"%s\" command", lxc_cmd_str(req.cmd)); |
ded1d23f | 986 | if (ret < 0) { |
c01c2be6 CB |
987 | SYSERROR("Failed to receive data on command socket for \"%s\"", |
988 | lxc_cmd_str(req.cmd)); | |
724e753c MN |
989 | goto out_close; |
990 | } | |
991 | ||
992 | if (!ret) { | |
c01c2be6 | 993 | DEBUG("Peer has disconnected for \"%s\"", lxc_cmd_str(req.cmd)); |
724e753c MN |
994 | goto out_close; |
995 | } | |
996 | ||
ef6e34ee | 997 | if (ret != sizeof(req)) { |
c01c2be6 CB |
998 | WARN("Failed to receive full command request. Ignoring request " |
999 | "for \"%s\"", | |
1000 | lxc_cmd_str(req.cmd)); | |
ef6e34ee DE |
1001 | ret = -1; |
1002 | goto out_close; | |
1003 | } | |
1004 | ||
1005 | if (req.datalen > LXC_CMD_DATA_MAX) { | |
c01c2be6 CB |
1006 | ERROR("Received command data length %d is too large for " |
1007 | "command \"%s\"", | |
1008 | req.datalen, lxc_cmd_str(req.cmd)); | |
ef6e34ee | 1009 | ret = -1; |
724e753c MN |
1010 | goto out_close; |
1011 | } | |
1012 | ||
ef6e34ee DE |
1013 | if (req.datalen > 0) { |
1014 | void *reqdata; | |
1015 | ||
1016 | reqdata = alloca(req.datalen); | |
1017 | ret = recv(fd, reqdata, req.datalen, 0); | |
1018 | if (ret != req.datalen) { | |
c01c2be6 CB |
1019 | WARN("Failed to receive full command request. Ignoring " |
1020 | "request for \"%s\"", | |
1021 | lxc_cmd_str(req.cmd)); | |
ef6e34ee DE |
1022 | ret = -1; |
1023 | goto out_close; | |
1024 | } | |
1025 | req.data = reqdata; | |
1026 | } | |
1027 | ||
1028 | ret = lxc_cmd_process(fd, &req, handler); | |
724e753c MN |
1029 | if (ret) { |
1030 | /* this is not an error, but only a request to close fd */ | |
1031 | ret = 0; | |
1032 | goto out_close; | |
1033 | } | |
1034 | ||
1035 | out: | |
1036 | return ret; | |
1037 | out_close: | |
ef6e34ee | 1038 | lxc_cmd_fd_cleanup(fd, handler, descr); |
724e753c MN |
1039 | goto out; |
1040 | } | |
1041 | ||
84c92abd DE |
1042 | static int lxc_cmd_accept(int fd, uint32_t events, void *data, |
1043 | struct lxc_epoll_descr *descr) | |
724e753c | 1044 | { |
ded1d23f | 1045 | int opt = 1, ret = -1, connection; |
724e753c MN |
1046 | |
1047 | connection = accept(fd, NULL, 0); | |
1048 | if (connection < 0) { | |
08aa08fe | 1049 | SYSERROR("Failed to accept connection to run command."); |
724e753c MN |
1050 | return -1; |
1051 | } | |
1052 | ||
9ccb2dbc | 1053 | if (fcntl(connection, F_SETFD, FD_CLOEXEC)) { |
08aa08fe | 1054 | SYSERROR("Failed to set close-on-exec on incoming command connection."); |
9ccb2dbc DL |
1055 | goto out_close; |
1056 | } | |
1057 | ||
0a3ec350 DL |
1058 | if (setsockopt(connection, SOL_SOCKET, |
1059 | SO_PASSCRED, &opt, sizeof(opt))) { | |
08aa08fe | 1060 | SYSERROR("Failed to enable necessary credentials on command socket."); |
724e753c MN |
1061 | goto out_close; |
1062 | } | |
1063 | ||
ef6e34ee | 1064 | ret = lxc_mainloop_add_handler(descr, connection, lxc_cmd_handler, data); |
724e753c | 1065 | if (ret) { |
08aa08fe | 1066 | ERROR("Failed to add command handler."); |
724e753c MN |
1067 | goto out_close; |
1068 | } | |
1069 | ||
1070 | out: | |
1071 | return ret; | |
1072 | ||
1073 | out_close: | |
1074 | close(connection); | |
1075 | goto out; | |
1076 | } | |
1077 | ||
ef6e34ee DE |
1078 | int lxc_cmd_init(const char *name, struct lxc_handler *handler, |
1079 | const char *lxcpath) | |
724e753c | 1080 | { |
d2e30e99 | 1081 | int fd; |
46968ea3 DL |
1082 | char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 }; |
1083 | char *offset = &path[1]; | |
2a59a681 | 1084 | int len; |
724e753c | 1085 | |
6f2944c1 TA |
1086 | /* -2 here because this is an abstract unix socket so it needs a |
1087 | * leading \0, and we null terminate, so it needs a trailing \0. | |
1088 | * Although null termination isn't required by the API, we do it anyway | |
1089 | * because we print the sockname out sometimes. | |
1090 | */ | |
860e7c43 | 1091 | len = sizeof(path) - 2; |
7935833c CB |
1092 | if (lxc_make_abstract_socket_name(offset, len, name, lxcpath, NULL, |
1093 | "command")) | |
9ba8130c | 1094 | return -1; |
724e753c | 1095 | |
aae93dd3 | 1096 | fd = lxc_abstract_unix_open(path, SOCK_STREAM, 0); |
724e753c | 1097 | if (fd < 0) { |
08aa08fe CB |
1098 | ERROR("Failed to create the command service point %s: %s.", |
1099 | offset, strerror(errno)); | |
1100 | if (errno == EADDRINUSE) | |
1101 | ERROR("Container \"%s\" appears to be already running!", name); | |
724e753c MN |
1102 | return -1; |
1103 | } | |
1104 | ||
91480a0f | 1105 | if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { |
08aa08fe | 1106 | SYSERROR("Failed to set FD_CLOEXEC on signal file descriptor."); |
91480a0f DL |
1107 | close(fd); |
1108 | return -1; | |
1109 | } | |
1110 | ||
d2e30e99 DE |
1111 | handler->conf->maincmd_fd = fd; |
1112 | return 0; | |
1113 | } | |
1114 | ||
ef6e34ee DE |
1115 | int lxc_cmd_mainloop_add(const char *name, |
1116 | struct lxc_epoll_descr *descr, | |
1117 | struct lxc_handler *handler) | |
d2e30e99 DE |
1118 | { |
1119 | int ret, fd = handler->conf->maincmd_fd; | |
1120 | ||
ef6e34ee | 1121 | ret = lxc_mainloop_add_handler(descr, fd, lxc_cmd_accept, handler); |
724e753c | 1122 | if (ret) { |
08aa08fe | 1123 | ERROR("Failed to add handler for command socket."); |
724e753c MN |
1124 | close(fd); |
1125 | } | |
1126 | ||
1127 | return ret; | |
1128 | } |