#include <stdlib.h>
#include <lxc/log.h>
+#include <lxc/lxc.h>
#include <lxc/conf.h>
#include <lxc/start.h> /* for struct lxc_handler */
#include <lxc/utils.h>
#include "commands.h"
+#include "confile.h"
#include "mainloop.h"
#include "af_unix.h"
#include "config.h"
/*
- * This file provides the different functions to have the client
- * and the server to communicate
+ * This file provides the different functions for clients to
+ * query/command the server. The client is typically some lxc
+ * tool and the server is typically the container (ie. lxc-start).
*
- * Each command is transactional, the client send a request to
- * the server and the server answer the request with a message
+ * Each command is transactional, the clients send a request to
+ * the server and the server answers the request with a message
* giving the request's status (zero or a negative errno value).
+ * Both the request and response may contain addtional data.
*
* Each command is wrapped in a ancillary message in order to pass
* a credential making possible to the server to check if the client
* is allowed to ask for this command or not.
- *
*/
lxc_log_define(lxc_commands, lxc);
return 0;
}
-static int receive_answer(int sock, struct lxc_answer *answer)
+static const char *lxc_cmd_str(lxc_cmd_t cmd)
{
- int ret;
- static char answerpath[MAXPATHLEN];
+ static const char *cmdname[LXC_CMD_MAX] = {
+ [LXC_CMD_CONSOLE] = "console",
+ [LXC_CMD_STOP] = "stop",
+ [LXC_CMD_GET_STATE] = "get_state",
+ [LXC_CMD_GET_INIT_PID] = "get_init_pid",
+ [LXC_CMD_GET_CLONE_FLAGS] = "get_clone_flags",
+ [LXC_CMD_GET_CGROUP] = "get_cgroup",
+ [LXC_CMD_GET_CONFIG_ITEM] = "get_config_item",
+ };
- ret = lxc_af_unix_recv_fd(sock, &answer->fd, answer, sizeof(*answer));
- if (ret < 0)
- ERROR("failed to receive answer for the command");
- if (answer->pathlen == 0)
+ if (cmd < 0 || cmd >= LXC_CMD_MAX)
+ return "Unknown cmd";
+ return cmdname[cmd];
+}
+
+/*
+ * lxc_cmd_rsp_recv: Receive a response to a command
+ *
+ * @sock : the socket connected to the container
+ * @cmd : command to put response in
+ *
+ * Returns the size of the response message or < 0 on failure
+ *
+ * Note that if the command response datalen > 0, then data is
+ * a malloc()ed buffer and should be free()ed by the caller. If
+ * the response data is <= a void * worth of data, it will be
+ * stored directly in data and datalen will be 0.
+ *
+ * As a special case, the response for LXC_CMD_CONSOLE is created
+ * here as it contains an fd passed through the unix socket.
+ */
+static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
+{
+ int ret,rspfd;
+ struct lxc_cmd_rsp *rsp = &cmd->rsp;
+
+ ret = lxc_af_unix_recv_fd(sock, &rspfd, rsp, sizeof(*rsp));
+ if (ret < 0) {
+ ERROR("command %s failed to receive response",
+ lxc_cmd_str(cmd->req.cmd));
+ return -1;
+ }
+
+ if (cmd->req.cmd == LXC_CMD_CONSOLE) {
+ struct lxc_cmd_console_rsp_data *rspdata;
+
+ rspdata = malloc(sizeof(*rspdata));
+ if (!rspdata) {
+ ERROR("command %s couldn't allocate response buffer",
+ lxc_cmd_str(cmd->req.cmd));
+ return -1;
+ }
+ rspdata->fd = rspfd;
+ rspdata->ttynum = PTR_TO_INT(rsp->data);
+ rsp->data = rspdata;
+ }
+
+ if (rsp->datalen == 0)
return ret;
- if (answer->pathlen >= MAXPATHLEN) {
- ERROR("cgroup path was too long");
+ if (rsp->datalen > LXC_CMD_DATA_MAX) {
+ ERROR("command %s response data %d too long",
+ lxc_cmd_str(cmd->req.cmd), rsp->datalen);
+ errno = EFBIG;
return -1;
}
- ret = recv(sock, answerpath, answer->pathlen, 0);
- if (ret != answer->pathlen) {
- ERROR("failed to receive answer for the command");
- ret = 0;
- } else
- answer->path = answerpath;
+
+ rsp->data = malloc(rsp->datalen);
+ if (!rsp->data) {
+ ERROR("command %s unable to allocate response buffer",
+ lxc_cmd_str(cmd->req.cmd));
+ return -1;
+ }
+ ret = recv(sock, rsp->data, rsp->datalen, 0);
+ if (ret != rsp->datalen) {
+ ERROR("command %s failed to receive response data",
+ lxc_cmd_str(cmd->req.cmd));
+ if (ret >= 0)
+ ret = -1;
+ }
return ret;
}
-static int __lxc_command(const char *name, struct lxc_command *command,
- int *stopped, int stay_connected, const char *lxcpath)
+/*
+ * lxc_cmd_rsp_send: Send a command response
+ *
+ * @fd : file descriptor of socket to send response on
+ * @rsp : response to send
+ *
+ * Returns 0 on success, < 0 on failure
+ */
+static int lxc_cmd_rsp_send(int fd, struct lxc_cmd_rsp *rsp)
+{
+ int ret;
+
+ ret = send(fd, rsp, sizeof(*rsp), 0);
+ if (ret != sizeof(*rsp)) {
+ ERROR("failed to send command response %d %s", ret,
+ strerror(errno));
+ return -1;
+ }
+
+ if (rsp->datalen > 0) {
+ ret = send(fd, rsp->data, rsp->datalen, 0);
+ if (ret != rsp->datalen) {
+ WARN("failed to send command response data %d %s", ret,
+ strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * lxc_cmd: Connect to the specified running container, send it a command
+ * request and collect the response
+ *
+ * @name : name of container to connect to
+ * @cmd : command with initialized reqest to send
+ * @stopped : output indicator if the container was not running
+ * @lxcpath : the lxcpath in which the container is running
+ *
+ * Returns the size of the response message on success, < 0 on failure
+ *
+ * Note that there is a special case for LXC_CMD_CONSOLE. For this command
+ * the fd cannot be closed because it is used as a placeholder to indicate
+ * that a particular tty slot is in use. The fd is also used as a signal to
+ * the container that when the caller dies or closes the fd, the container
+ * will notice the fd in its mainloop select and then free the slot with
+ * lxc_cmd_fd_cleanup().
+ */
+static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
+ const char *lxcpath)
{
int sock, ret = -1;
char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 };
char *offset = &path[1];
int len;
+ int stay_connected = cmd->req.cmd == LXC_CMD_CONSOLE;
len = sizeof(path)-1;
if (fill_sock_name(offset, len, name, lxcpath))
return -1;
sock = lxc_af_unix_connect(path);
- if (sock < 0 && errno == ECONNREFUSED) {
- *stopped = 1;
- return -1;
- }
-
if (sock < 0) {
- SYSERROR("failed to connect to '@%s'", offset);
+ if (errno == ECONNREFUSED)
+ *stopped = 1;
+ else
+ SYSERROR("command %s failed to connect to '@%s'",
+ lxc_cmd_str(cmd->req.cmd), offset);
return -1;
}
- ret = lxc_af_unix_send_credential(sock, &command->request,
- sizeof(command->request));
- if (ret < 0) {
- SYSERROR("failed to send request to '@%s'", offset);
+ ret = lxc_af_unix_send_credential(sock, &cmd->req, sizeof(cmd->req));
+ if (ret != sizeof(cmd->req)) {
+ SYSERROR("command %s failed to send req to '@%s' %d",
+ lxc_cmd_str(cmd->req.cmd), offset, ret);
+ if (ret >=0)
+ ret = -1;
goto out;
}
- if (ret != sizeof(command->request)) {
- SYSERROR("message partially sent to '@%s'", offset);
- goto out;
+ if (cmd->req.datalen > 0) {
+ ret = send(sock, cmd->req.data, cmd->req.datalen, 0);
+ if (ret != cmd->req.datalen) {
+ SYSERROR("command %s failed to send request data to '@%s' %d",
+ lxc_cmd_str(cmd->req.cmd), offset, ret);
+ if (ret >=0)
+ ret = -1;
+ goto out;
+ }
}
- ret = receive_answer(sock, &command->answer);
+ ret = lxc_cmd_rsp_recv(sock, cmd);
out:
if (!stay_connected || ret < 0)
close(sock);
return ret;
}
-extern int lxc_command(const char *name,
- struct lxc_command *command, int *stopped,
- const char *lxcpath)
+/* Implentations of the commands and their callbacks */
+
+/*
+ * lxc_cmd_get_init_pid: Get pid of the container's init process
+ *
+ * @name : name of container to connect to
+ * @lxcpath : the lxcpath in which the container is running
+ *
+ * Returns the pid on success, < 0 on failure
+ */
+pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath)
{
- return __lxc_command(name, command, stopped, 0, lxcpath);
+ int ret, stopped = 0;
+ struct lxc_cmd_rr cmd = {
+ .req = { .cmd = LXC_CMD_GET_INIT_PID },
+ };
+
+ ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
+ if (ret < 0)
+ return ret;
+
+ return PTR_TO_INT(cmd.rsp.data);
}
-extern int lxc_command_connected(const char *name,
- struct lxc_command *command, int *stopped,
- const char *lxcpath)
+static int lxc_cmd_get_init_pid_callback(int fd, struct lxc_cmd_req *req,
+ struct lxc_handler *handler)
{
- return __lxc_command(name, command, stopped, 1, lxcpath);
+ struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->pid) };
+
+ return lxc_cmd_rsp_send(fd, &rsp);
}
+/*
+ * lxc_cmd_get_clone_flags: Get clone flags container was spawned with
+ *
+ * @name : name of container to connect to
+ * @lxcpath : the lxcpath in which the container is running
+ *
+ * Returns the clone flags on success, < 0 on failure
+ */
+int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath)
+{
+ int ret, stopped = 0;
+ struct lxc_cmd_rr cmd = {
+ .req = { .cmd = LXC_CMD_GET_CLONE_FLAGS },
+ };
+
+ ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
+ if (ret < 0)
+ return ret;
-pid_t get_init_pid(const char *name, const char *lxcpath)
+ return PTR_TO_INT(cmd.rsp.data);
+}
+
+static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req,
+ struct lxc_handler *handler)
{
- struct lxc_command command = {
- .request = { .type = LXC_COMMAND_PID },
+ struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->clone_flags) };
+
+ return lxc_cmd_rsp_send(fd, &rsp);
+}
+
+/*
+ * lxc_cmd_get_cgroup_path: Calculate a container's cgroup path for a
+ * particular subsystem. This is the cgroup path relative to the root
+ * of the cgroup filesystem.
+ *
+ * @subsystem : the cgroup subsystem of interest (i.e. freezer)
+ * @name : name of container to connect to
+ * @lxcpath : the lxcpath in which the container is running
+ *
+ * Returns the path on success, NULL on failure. The caller must free() the
+ * returned path.
+ */
+char *lxc_cmd_get_cgroup_path(const char *subsystem, const char *name,
+ const char *lxcpath)
+{
+ int ret, stopped = 0;
+ struct lxc_cmd_rr cmd = {
+ .req = { .cmd = LXC_CMD_GET_CGROUP },
};
+ ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
+ if (ret < 0)
+ return NULL;
+
+ if (!ret) {
+ WARN("'%s' has stopped before sending its state", name);
+ return NULL;
+ }
+
+ if (cmd.rsp.ret < 0 || cmd.rsp.datalen < 0) {
+ ERROR("command %s failed for '%s': %s",
+ lxc_cmd_str(cmd.req.cmd), name,
+ strerror(-cmd.rsp.ret));
+ return NULL;
+ }
+
+ return cmd.rsp.data;
+}
+
+static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req,
+ struct lxc_handler *handler)
+{
+ struct lxc_cmd_rsp rsp = {
+ .datalen = strlen(handler->cgroup) + 1,
+ .data = handler->cgroup,
+ };
+
+ return lxc_cmd_rsp_send(fd, &rsp);
+}
+
+/*
+ * lxc_cmd_get_config_item: Get config item the running container
+ *
+ * @name : name of container to connect to
+ * @item : the configuration item to retrieve (ex: lxc.network.0.veth.pair)
+ * @lxcpath : the lxcpath in which the container is running
+ *
+ * Returns the item on success, NULL on failure. The caller must free() the
+ * returned item.
+ */
+char *lxc_cmd_get_config_item(const char *name, const char *item,
+ const char *lxcpath)
+{
+ int ret, stopped = 0;
+ struct lxc_cmd_rr cmd = {
+ .req = { .cmd = LXC_CMD_GET_CONFIG_ITEM,
+ .data = item,
+ .datalen = strlen(item)+1,
+ },
+ };
+
+ ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
+ if (ret < 0)
+ return NULL;
+
+ if (cmd.rsp.ret == 0)
+ return cmd.rsp.data;
+ return NULL;
+}
+
+static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req,
+ struct lxc_handler *handler)
+{
+ int cilen;
+ struct lxc_cmd_rsp rsp;
+ char *cidata;
+
+ memset(&rsp, 0, sizeof(rsp));
+ cilen = lxc_get_config_item(handler->conf, req->data, NULL, 0);
+ if (cilen <= 0)
+ goto err1;
+
+ cidata = alloca(cilen + 1);
+ if (lxc_get_config_item(handler->conf, req->data, cidata, cilen + 1) != cilen)
+ goto err1;
+ cidata[cilen] = '\0';
+ rsp.data = cidata;
+ rsp.datalen = cilen + 1;
+ rsp.ret = 0;
+ goto out;
+
+err1:
+ rsp.ret = -1;
+out:
+ return lxc_cmd_rsp_send(fd, &rsp);
+}
+
+/*
+ * lxc_cmd_get_state: Get current state of the container
+ *
+ * @name : name of container to connect to
+ * @lxcpath : the lxcpath in which the container is running
+ *
+ * Returns the state on success, < 0 on failure
+ */
+lxc_state_t lxc_cmd_get_state(const char *name, const char *lxcpath)
+{
int ret, stopped = 0;
+ struct lxc_cmd_rr cmd = {
+ .req = { .cmd = LXC_CMD_GET_STATE }
+ };
- ret = lxc_command(name, &command, &stopped, lxcpath);
+ ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
if (ret < 0 && stopped)
+ return STOPPED;
+
+ if (ret < 0)
return -1;
+ if (!ret) {
+ WARN("'%s' has stopped before sending its state", name);
+ return -1;
+ }
+
+ DEBUG("'%s' is in '%s' state", name,
+ lxc_state2str(PTR_TO_INT(cmd.rsp.data)));
+ return PTR_TO_INT(cmd.rsp.data);
+}
+
+static int lxc_cmd_get_state_callback(int fd, struct lxc_cmd_req *req,
+ struct lxc_handler *handler)
+{
+ struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->state) };
+
+ return lxc_cmd_rsp_send(fd, &rsp);
+}
+
+/*
+ * lxc_cmd_stop: Stop the container previously started with lxc_start. All
+ * the processes running inside this container will be killed.
+ *
+ * @name : name of container to connect to
+ * @lxcpath : the lxcpath in which the container is running
+ *
+ * Returns 0 on success, < 0 on failure
+ */
+int lxc_cmd_stop(const char *name, const char *lxcpath)
+{
+ int ret, stopped = 0;
+ struct lxc_cmd_rr cmd = {
+ .req = { .cmd = LXC_CMD_STOP },
+ };
+
+ ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
if (ret < 0) {
- ERROR("failed to send command");
+ if (stopped) {
+ INFO("'%s' is already stopped", name);
+ return 0;
+ }
return -1;
}
- if (command.answer.ret) {
- ERROR("failed to retrieve the init pid: %s",
- strerror(-command.answer.ret));
+ /* we do not expect any answer, because we wait for the connection to be
+ * closed
+ */
+ if (ret > 0) {
+ ERROR("failed to stop '%s': %s", name, strerror(-cmd.rsp.ret));
return -1;
}
- return command.answer.pid;
+ INFO("'%s' has stopped", name);
+ return 0;
}
-int lxc_get_clone_flags(const char *name, const char *lxcpath)
+static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req,
+ struct lxc_handler *handler)
{
- struct lxc_command command = {
- .request = { .type = LXC_COMMAND_CLONE_FLAGS },
- };
+ struct lxc_cmd_rsp rsp;
+ int ret;
+ int stopsignal = SIGKILL;
+
+ if (handler->conf->stopsignal)
+ stopsignal = handler->conf->stopsignal;
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.ret = kill(handler->pid, stopsignal);
+ if (!rsp.ret) {
+ ret = lxc_unfreeze_bypath(handler->cgroup);
+ if (!ret)
+ return 0;
+
+ ERROR("failed to unfreeze container");
+ rsp.ret = ret;
+ }
+
+ return lxc_cmd_rsp_send(fd, &rsp);
+}
+/*
+ * lxc_cmd_console: Open an fd to a tty in the container
+ *
+ * @name : name of container to connect to
+ * @ttynum : in: the tty to open or -1 for next available
+ * : out: the tty allocated
+ * @fd : out: file descriptor for master side of pty
+ * @lxcpath : the lxcpath in which the container is running
+ *
+ * Returns 0 on success, < 0 on failure
+ */
+int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath)
+{
int ret, stopped = 0;
+ struct lxc_cmd_console_rsp_data *rspdata;
+ struct lxc_cmd_rr cmd = {
+ .req = { .cmd = LXC_CMD_CONSOLE, .data = INT_TO_PTR(*ttynum) },
+ };
- ret = lxc_command(name, &command, &stopped, lxcpath);
- if (ret < 0 && stopped)
- return -1;
+ ret = lxc_cmd(name, &cmd, &stopped, lxcpath);
+ if (ret < 0)
+ return ret;
+ if (ret == 0) {
+ ERROR("console %d invalid or all consoles busy", *ttynum);
+ ret = -1;
+ goto out;
+ }
- if (ret < 0) {
- ERROR("failed to send command");
- return -1;
+ ret = -1;
+ #if 1 /* FIXME: how can this happen? */
+ if (cmd.rsp.ret) {
+ ERROR("console access denied: %s",
+ strerror(-cmd.rsp.ret));
+ goto out;
+ }
+ #endif
+
+ rspdata = cmd.rsp.data;
+ if (rspdata->fd < 0) {
+ ERROR("unable to allocate fd for tty %d", rspdata->ttynum);
+ goto out;
+ }
+
+ ret = 0;
+ *fd = rspdata->fd;
+ *ttynum = rspdata->ttynum;
+ INFO("tty %d allocated", rspdata->ttynum);
+out:
+ free(cmd.rsp.data);
+ return ret;
+}
+
+static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req,
+ struct lxc_handler *handler)
+{
+ int ttynum = PTR_TO_INT(req->data);
+ struct lxc_tty_info *tty_info = &handler->conf->tty_info;
+ struct lxc_cmd_rsp rsp;
+
+ if (ttynum > 0) {
+ if (ttynum > tty_info->nbtty)
+ goto out_close;
+
+ if (tty_info->pty_info[ttynum - 1].busy)
+ goto out_close;
+
+ /* the requested tty is available */
+ goto out_send;
}
- return command.answer.ret;
+ /* search for next available tty, fixup index tty1 => [0] */
+ for (ttynum = 1;
+ ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy;
+ ttynum++);
+
+ /* we didn't find any available slot for tty */
+ if (ttynum > tty_info->nbtty)
+ goto out_close;
+
+out_send:
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.data = INT_TO_PTR(ttynum);
+ if (lxc_af_unix_send_fd(fd, tty_info->pty_info[ttynum - 1].master,
+ &rsp, sizeof(rsp)) < 0) {
+ ERROR("failed to send tty to client");
+ goto out_close;
+ }
+
+ tty_info->pty_info[ttynum - 1].busy = fd;
+ return 0;
+
+out_close:
+ /* special indicator to lxc_cmd_handler() to close the fd and do
+ * related cleanup
+ */
+ return 1;
}
-extern void lxc_console_remove_fd(int, struct lxc_tty_info *);
-extern int lxc_console_callback(int, struct lxc_request *, struct lxc_handler *);
-extern int lxc_stop_callback(int, struct lxc_request *, struct lxc_handler *);
-extern int lxc_state_callback(int, struct lxc_request *, struct lxc_handler *);
-extern int lxc_pid_callback(int, struct lxc_request *, struct lxc_handler *);
-extern int lxc_clone_flags_callback(int, struct lxc_request *, struct lxc_handler *);
-extern int lxc_cgroup_callback(int, struct lxc_request *, struct lxc_handler *);
-static int trigger_command(int fd, struct lxc_request *request,
+
+static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler)
{
- typedef int (*callback)(int, struct lxc_request *, struct lxc_handler *);
-
- callback cb[LXC_COMMAND_MAX] = {
- [LXC_COMMAND_TTY] = lxc_console_callback,
- [LXC_COMMAND_STOP] = lxc_stop_callback,
- [LXC_COMMAND_STATE] = lxc_state_callback,
- [LXC_COMMAND_PID] = lxc_pid_callback,
- [LXC_COMMAND_CLONE_FLAGS] = lxc_clone_flags_callback,
- [LXC_COMMAND_CGROUP] = lxc_cgroup_callback,
+ typedef int (*callback)(int, struct lxc_cmd_req *, struct lxc_handler *);
+
+ callback cb[LXC_CMD_MAX] = {
+ [LXC_CMD_CONSOLE] = lxc_cmd_console_callback,
+ [LXC_CMD_STOP] = lxc_cmd_stop_callback,
+ [LXC_CMD_GET_STATE] = lxc_cmd_get_state_callback,
+ [LXC_CMD_GET_INIT_PID] = lxc_cmd_get_init_pid_callback,
+ [LXC_CMD_GET_CLONE_FLAGS] = lxc_cmd_get_clone_flags_callback,
+ [LXC_CMD_GET_CGROUP] = lxc_cmd_get_cgroup_callback,
+ [LXC_CMD_GET_CONFIG_ITEM] = lxc_cmd_get_config_item_callback,
};
- if (request->type < 0 || request->type >= LXC_COMMAND_MAX)
+ if (req->cmd < 0 || req->cmd >= LXC_CMD_MAX) {
+ ERROR("bad cmd %d recieved", req->cmd);
return -1;
-
- return cb[request->type](fd, request, handler);
+ }
+ return cb[req->cmd](fd, req, handler);
}
-static void command_fd_cleanup(int fd, struct lxc_handler *handler,
+static void lxc_cmd_fd_cleanup(int fd, struct lxc_handler *handler,
struct lxc_epoll_descr *descr)
{
+ extern void lxc_console_remove_fd(int, struct lxc_tty_info *);
lxc_console_remove_fd(fd, &handler->conf->tty_info);
lxc_mainloop_del_handler(descr, fd);
close(fd);
}
-static int command_handler(int fd, void *data, struct lxc_epoll_descr *descr)
+static int lxc_cmd_handler(int fd, void *data, struct lxc_epoll_descr *descr)
{
int ret;
- struct lxc_request request;
+ struct lxc_cmd_req req;
struct lxc_handler *handler = data;
- ret = lxc_af_unix_rcv_credential(fd, &request, sizeof(request));
+ ret = lxc_af_unix_rcv_credential(fd, &req, sizeof(req));
if (ret == -EACCES) {
/* we don't care for the peer, just send and close */
- struct lxc_answer answer = { .ret = ret };
- send(fd, &answer, sizeof(answer), 0);
+ struct lxc_cmd_rsp rsp = { .ret = ret };
+
+ lxc_cmd_rsp_send(fd, &rsp);
goto out_close;
}
goto out_close;
}
- if (ret != sizeof(request)) {
+ if (ret != sizeof(req)) {
WARN("partial request, ignored");
+ ret = -1;
+ goto out_close;
+ }
+
+ if (req.datalen > LXC_CMD_DATA_MAX) {
+ ERROR("cmd data length %d too large", req.datalen);
+ ret = -1;
goto out_close;
}
- ret = trigger_command(fd, &request, handler);
+ if (req.datalen > 0) {
+ void *reqdata;
+
+ reqdata = alloca(req.datalen);
+ ret = recv(fd, reqdata, req.datalen, 0);
+ if (ret != req.datalen) {
+ WARN("partial request, ignored");
+ ret = -1;
+ goto out_close;
+ }
+ req.data = reqdata;
+ }
+
+ ret = lxc_cmd_process(fd, &req, handler);
if (ret) {
/* this is not an error, but only a request to close fd */
ret = 0;
out:
return ret;
out_close:
- command_fd_cleanup(fd, handler, descr);
+ lxc_cmd_fd_cleanup(fd, handler, descr);
goto out;
}
-static int incoming_command_handler(int fd, void *data,
- struct lxc_epoll_descr *descr)
+static int lxc_cmd_accept(int fd, void *data, struct lxc_epoll_descr *descr)
{
int opt = 1, ret = -1, connection;
goto out_close;
}
- ret = lxc_mainloop_add_handler(descr, connection, command_handler, data);
+ ret = lxc_mainloop_add_handler(descr, connection, lxc_cmd_handler, data);
if (ret) {
ERROR("failed to add handler");
goto out_close;
goto out;
}
-extern int lxc_command_init(const char *name, struct lxc_handler *handler,
- const char *lxcpath)
+int lxc_cmd_init(const char *name, struct lxc_handler *handler,
+ const char *lxcpath)
{
int fd;
char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 };
return 0;
}
-extern int lxc_command_mainloop_add(const char *name,
- struct lxc_epoll_descr *descr,
- struct lxc_handler *handler)
+int lxc_cmd_mainloop_add(const char *name,
+ struct lxc_epoll_descr *descr,
+ struct lxc_handler *handler)
{
int ret, fd = handler->conf->maincmd_fd;
- ret = lxc_mainloop_add_handler(descr, fd, incoming_command_handler,
- handler);
+ ret = lxc_mainloop_add_handler(descr, fd, lxc_cmd_accept, handler);
if (ret) {
ERROR("failed to add handler for command socket");
close(fd);