* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "config.h"
-
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
#include <caps.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <unistd.h>
#include "af_unix.h"
#include "cgroup.h"
#include "commands.h"
#include "commands_utils.h"
#include "conf.h"
+#include "config.h"
#include "confile.h"
#include "log.h"
#include "lxc.h"
#include "lxclock.h"
#include "mainloop.h"
+#include "memory_utils.h"
#include "monitor.h"
#include "start.h"
#include "terminal.h"
* container.
*/
-lxc_log_define(lxc_commands, lxc);
+lxc_log_define(commands, lxc);
static const char *lxc_cmd_str(lxc_cmd_t cmd)
{
};
if (cmd >= LXC_CMD_MAX)
- return "Unknown cmd";
+ return "Invalid request";
return cmdname[cmd];
}
lxc_cmd_str(cmd->req.cmd));
if (errno == ECONNRESET)
- return -ECONNRESET;
+ return -1;
return -1;
}
rspdata = malloc(sizeof(*rspdata));
if (!rspdata) {
+ errno = ENOMEM;
ERROR("Failed to allocate response buffer for command \"%s\"",
lxc_cmd_str(cmd->req.cmd));
- return -ENOMEM;
+ return -1;
}
+
rspdata->masterfd = rspfd;
rspdata->ttynum = PTR_TO_INT(rsp->data);
rsp->data = rspdata;
if ((rsp->datalen > LXC_CMD_DATA_MAX) &&
(cmd->req.cmd != LXC_CMD_CONSOLE_LOG)) {
- errno = EFBIG;
- ERROR("%s - Response data for command \"%s\" is too long: %d "
- "bytes > %d", strerror(errno), lxc_cmd_str(cmd->req.cmd),
- rsp->datalen, LXC_CMD_DATA_MAX);
- return -EFBIG;
+ ERROR("Response data for command \"%s\" is too long: %d bytes > %d",
+ lxc_cmd_str(cmd->req.cmd), rsp->datalen, LXC_CMD_DATA_MAX);
+ return -1;
}
if (cmd->req.cmd == LXC_CMD_CONSOLE_LOG) {
}
if (!rsp->data) {
errno = ENOMEM;
- ERROR("%s - Failed to allocate response buffer for command "
- "\"%s\"", strerror(errno), lxc_cmd_str(cmd->req.cmd));
- return -ENOMEM;
+ ERROR("Failed to allocate response buffer for command \"%s\"",
+ lxc_cmd_str(cmd->req.cmd));
+ return -1;
}
- ret = recv(sock, rsp->data, rsp->datalen, 0);
+ ret = lxc_recv_nointr(sock, rsp->data, rsp->datalen, 0);
if (ret != rsp->datalen) {
- ERROR("%s - Failed to receive response data for command \"%s\"",
- lxc_cmd_str(cmd->req.cmd), strerror(errno));
- if (ret >= 0)
- ret = -1;
+ SYSERROR("Failed to receive response data for command \"%s\"",
+ lxc_cmd_str(cmd->req.cmd));
+ return -1;
}
return ret;
{
ssize_t ret;
- ret = send(fd, rsp, sizeof(*rsp), 0);
+ errno = EMSGSIZE;
+ ret = lxc_send_nointr(fd, rsp, sizeof(*rsp), MSG_NOSIGNAL);
if (ret < 0 || (size_t)ret != sizeof(*rsp)) {
- ERROR("%s - Failed to send command response %zd",
- strerror(errno), ret);
+ SYSERROR("Failed to send command response %zd", ret);
return -1;
}
if (!rsp->data || rsp->datalen <= 0)
return 0;
- ret = send(fd, rsp->data, rsp->datalen, 0);
+ errno = EMSGSIZE;
+ ret = lxc_send_nointr(fd, rsp->data, rsp->datalen, MSG_NOSIGNAL);
if (ret < 0 || ret != (ssize_t)rsp->datalen) {
SYSWARN("Failed to send command response data %zd", ret);
return -1;
static int lxc_cmd_send(const char *name, struct lxc_cmd_rr *cmd,
const char *lxcpath, const char *hashed_sock_name)
{
- int client_fd;
+ __do_close_prot_errno int client_fd = -EBADF;
ssize_t ret = -1;
client_fd = lxc_cmd_connect(name, lxcpath, hashed_sock_name, "command");
- if (client_fd < 0) {
- if (client_fd == -ECONNREFUSED)
- return -ECONNREFUSED;
-
+ if (client_fd < 0)
return -1;
- }
ret = lxc_abstract_unix_send_credential(client_fd, &cmd->req,
sizeof(cmd->req));
- if (ret < 0 || (size_t)ret != sizeof(cmd->req)) {
- close(client_fd);
-
- if (errno == EPIPE)
- return -EPIPE;
-
- if (ret >= 0)
- return -EMSGSIZE;
-
+ if (ret < 0 || (size_t)ret != sizeof(cmd->req))
return -1;
- }
if (cmd->req.datalen <= 0)
- return client_fd;
-
- ret = send(client_fd, cmd->req.data, cmd->req.datalen, MSG_NOSIGNAL);
- if (ret < 0 || ret != (ssize_t)cmd->req.datalen) {
- close(client_fd);
-
- if (errno == EPIPE)
- return -EPIPE;
-
- if (ret >= 0)
- return -EMSGSIZE;
+ return steal_fd(client_fd);
+ errno = EMSGSIZE;
+ ret = lxc_send_nointr(client_fd, (void *)cmd->req.data,
+ cmd->req.datalen, MSG_NOSIGNAL);
+ if (ret < 0 || ret != (ssize_t)cmd->req.datalen)
return -1;
- }
- return client_fd;
+ return steal_fd(client_fd);
}
/*
static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
const char *lxcpath, const char *hashed_sock_name)
{
- int client_fd;
+ __do_close_prot_errno int client_fd = -EBADF;
int ret = -1;
bool stay_connected = false;
SYSTRACE("Command \"%s\" failed to connect command socket",
lxc_cmd_str(cmd->req.cmd));
- if (client_fd == -ECONNREFUSED)
+ if (errno == ECONNREFUSED || errno == EPIPE)
*stopped = 1;
- if (client_fd == -EPIPE) {
- *stopped = 1;
- client_fd = 0;
- }
-
- return client_fd;
+ return -1;
}
ret = lxc_cmd_rsp_recv(client_fd, cmd);
- if (ret == -ECONNRESET)
+ if (ret < 0 && errno == ECONNRESET)
*stopped = 1;
- if (!stay_connected || ret <= 0)
- if (client_fd >= 0)
- close(client_fd);
-
if (stay_connected && ret > 0)
- cmd->rsp.ret = client_fd;
+ cmd->rsp.ret = steal_fd(client_fd);
return ret;
}
return 0;
}
-/* Implentations of the commands and their callbacks */
+/* Implementations of the commands and their callbacks */
/*
* lxc_cmd_get_init_pid: Get pid of the container's init process
pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath)
{
int ret, stopped;
+ intmax_t pid;
struct lxc_cmd_rr cmd = {
- .req = { .cmd = LXC_CMD_GET_INIT_PID },
+ .req = {
+ .cmd = LXC_CMD_GET_INIT_PID
+ },
+ .rsp = {
+ .data = INTMAX_TO_PTR((intmax_t){-1})
+ }
};
ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
if (ret < 0)
- return ret;
+ return -1;
- return PTR_TO_INT(cmd.rsp.data);
+ pid = PTR_TO_INTMAX(cmd.rsp.data);
+ if (pid < 0)
+ return -1;
+
+ /* We need to assume that pid_t can actually hold any pid given to us
+ * by the kernel. If it can't it's a libc bug.
+ */
+ return (pid_t)pid;
}
static int lxc_cmd_get_init_pid_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler)
{
- struct lxc_cmd_rsp rsp = { .data = INT_TO_PTR(handler->pid) };
+ intmax_t pid = handler->pid;
+
+ struct lxc_cmd_rsp rsp = {
+ .data = INTMAX_TO_PTR(pid)
+ };
return lxc_cmd_rsp_send(fd, &rsp);
}
static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler)
{
+ __do_free char *cidata = NULL;
int cilen;
- char *cidata;
struct lxc_config_t *item;
struct lxc_cmd_rsp rsp;
if (cilen <= 0)
goto err1;
- cidata = alloca(cilen + 1);
+ cidata = must_realloc(NULL, cilen + 1);
if (item->get(req->data, cidata, cilen + 1, handler->conf, NULL) != cilen)
goto err1;
* closed.
*/
if (ret > 0) {
- ERROR("%s - Failed to stop container \"%s\"",
- strerror(-cmd.rsp.ret), name);
+ errno = -cmd.rsp.ret;
+ SYSERROR("Failed to stop container \"%s\"", name);
return -1;
}
* lxc_unfreeze() would do another cmd (GET_CGROUP) which would
* deadlock us.
*/
+ if (!cgroup_ops->get_cgroup(cgroup_ops, "freezer"))
+ return 0;
+
if (cgroup_ops->unfreeze(cgroup_ops))
return 0;
}
/*
- * lxc_cmd_terminal_winch: To process as if a SIGWINCH were received
+ * lxc_cmd_terminal_winch: noop
*
* @name : name of container to connect to
* @lxcpath : the lxcpath in which the container is running
*/
int lxc_cmd_terminal_winch(const char *name, const char *lxcpath)
{
- int ret, stopped;
- struct lxc_cmd_rr cmd = {
- .req = { .cmd = LXC_CMD_TERMINAL_WINCH },
- };
-
- ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
- if (ret < 0)
- return ret;
-
return 0;
}
static int lxc_cmd_terminal_winch_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler)
{
- struct lxc_cmd_rsp rsp = { .data = 0 };
-
- lxc_terminal_sigwinch(SIGWINCH);
-
- return lxc_cmd_rsp_send(fd, &rsp);
+ /* should never be called */
+ return -1;
}
/*
*/
int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath)
{
+ __do_free struct lxc_cmd_console_rsp_data *rspdata = NULL;
int ret, stopped;
- struct lxc_cmd_console_rsp_data *rspdata;
struct lxc_cmd_rr cmd = {
.req = { .cmd = LXC_CMD_CONSOLE, .data = INT_TO_PTR(*ttynum) },
};
if (ret < 0)
return ret;
+ rspdata = cmd.rsp.data;
if (cmd.rsp.ret < 0) {
- ERROR("%s - Denied access to tty", strerror(-cmd.rsp.ret));
- ret = -1;
- goto out;
+ errno = -cmd.rsp.ret;
+ SYSERROR("Denied access to tty");
+ return -1;
}
if (ret == 0) {
ERROR("tty number %d invalid, busy or all ttys busy", *ttynum);
- ret = -1;
- goto out;
+ return -1;
}
- rspdata = cmd.rsp.data;
if (rspdata->masterfd < 0) {
ERROR("Unable to allocate fd for tty %d", rspdata->ttynum);
- goto out;
+ return -1;
}
ret = cmd.rsp.ret; /* socket fd */
*ttynum = rspdata->ttynum;
INFO("Alloced fd %d for tty %d via socket %d", *fd, rspdata->ttynum, ret);
-out:
- free(cmd.rsp.data);
return ret;
}
rsp.data = INT_TO_PTR(ttynum);
ret = lxc_abstract_unix_send_fds(fd, &masterfd, 1, &rsp, sizeof(rsp));
if (ret < 0) {
- ERROR("Failed to send tty to client");
+ SYSERROR("Failed to send tty to client");
lxc_terminal_free(handler->conf, fd);
goto out_close;
}
lxc_state_t states[MAX_STATE],
int *state_client_fd)
{
+ __do_close_prot_errno int clientfd = -EBADF;
int state, stopped;
ssize_t ret;
struct lxc_cmd_rr cmd = {
if (ret < 0) {
if (errno != ECONNREFUSED)
- ERROR("%s - Failed to execute command", strerror(errno));
+ SYSERROR("Failed to execute command");
+
return -1;
}
/* We should now be guaranteed to get an answer from the state sending
* function.
*/
- if (cmd.rsp.ret < 0) {
- ERROR("%s - Failed to receive socket fd", strerror(-cmd.rsp.ret));
+ clientfd = cmd.rsp.ret;
+ if (clientfd < 0) {
+ errno = -clientfd;
+ SYSERROR("Failed to receive socket fd");
return -1;
}
state = PTR_TO_INT(cmd.rsp.data);
if (state < MAX_STATE) {
TRACE("Container is already in requested state %s", lxc_state2str(state));
- close(cmd.rsp.ret);
return state;
}
- *state_client_fd = cmd.rsp.ret;
- TRACE("Added state client %d to state client list", cmd.rsp.ret);
+ *state_client_fd = steal_fd(clientfd);
+ TRACE("Added state client %d to state client list", *state_client_fd);
return MAX_STATE;
}
if (req->datalen < 0)
goto reap_client_fd;
- if (req->datalen > (sizeof(lxc_state_t) * MAX_STATE))
+ if (req->datalen != (sizeof(lxc_state_t) * MAX_STATE))
goto reap_client_fd;
if (!req->data)
ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
if (ret < 0) {
- ERROR("%s - Failed to execute command", strerror(errno));
+ SYSERROR("Failed to execute command");
return -1;
}
struct lxc_epoll_descr *descr,
const lxc_cmd_t cmd)
{
- struct lxc_state_client *client;
struct lxc_list *cur, *next;
lxc_terminal_free(handler->conf, fd);
}
lxc_list_for_each_safe(cur, &handler->conf->state_clients, next) {
- client = cur->elem;
+ struct lxc_state_client *client = cur->elem;
+
if (client->clientfd != fd)
continue;
static int lxc_cmd_handler(int fd, uint32_t events, void *data,
struct lxc_epoll_descr *descr)
{
+ __do_free void *reqdata = NULL;
int ret;
struct lxc_cmd_req req;
- void *reqdata = NULL;
struct lxc_handler *handler = data;
ret = lxc_abstract_unix_rcv_credential(fd, &req, sizeof(req));
- if (ret == -EACCES) {
- /* We don't care for the peer, just send and close. */
- struct lxc_cmd_rsp rsp = {.ret = ret};
-
- lxc_cmd_rsp_send(fd, &rsp);
- goto out_close;
- }
-
if (ret < 0) {
SYSERROR("Failed to receive data on command socket for command "
- "\"%s\"", lxc_cmd_str(req.cmd));
+ "\"%s\"", lxc_cmd_str(req.cmd));
+
+ if (errno == EACCES) {
+ /* We don't care for the peer, just send and close. */
+ struct lxc_cmd_rsp rsp = {.ret = ret};
+
+ lxc_cmd_rsp_send(fd, &rsp);
+ }
+
goto out_close;
}
}
if (req.datalen > 0) {
- /* LXC_CMD_CONSOLE_LOG needs to be able to allocate data
- * that exceeds LXC_CMD_DATA_MAX: use malloc() for that.
- */
- if (req.cmd == LXC_CMD_CONSOLE_LOG)
- reqdata = malloc(req.datalen);
- else
- reqdata = alloca(req.datalen);
- if (!reqdata) {
- ERROR("Failed to allocate memory for \"%s\" command",
- lxc_cmd_str(req.cmd));
- errno = ENOMEM;
- ret = -ENOMEM;
- goto out_close;
- }
-
- ret = recv(fd, reqdata, req.datalen, 0);
+ reqdata = must_realloc(NULL, req.datalen);
+ ret = lxc_recv_nointr(fd, reqdata, req.datalen, 0);
if (ret != req.datalen) {
WARN("Failed to receive full command request. Ignoring "
"request for \"%s\"", lxc_cmd_str(req.cmd));
}
out:
- if (req.cmd == LXC_CMD_CONSOLE_LOG && reqdata)
- free(reqdata);
-
return ret;
out_close:
static int lxc_cmd_accept(int fd, uint32_t events, void *data,
struct lxc_epoll_descr *descr)
{
- int connection;
+ __do_close_prot_errno int connection = -EBADF;
int opt = 1, ret = -1;
connection = accept(fd, NULL, 0);
if (connection < 0) {
- SYSERROR("Failed to accept connection to run command.");
+ SYSERROR("Failed to accept connection to run command");
return LXC_MAINLOOP_ERROR;
}
ret = fcntl(connection, F_SETFD, FD_CLOEXEC);
if (ret < 0) {
SYSERROR("Failed to set close-on-exec on incoming command connection");
- goto out_close;
+ return ret;
}
ret = setsockopt(connection, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt));
if (ret < 0) {
SYSERROR("Failed to enable necessary credentials on command socket");
- goto out_close;
+ return ret;
}
ret = lxc_mainloop_add_handler(descr, connection, lxc_cmd_handler, data);
if (ret) {
ERROR("Failed to add command handler");
- goto out_close;
+ return ret;
}
-out:
+ steal_fd(connection);
return ret;
-
-out_close:
- close(connection);
- goto out;
}
int lxc_cmd_init(const char *name, const char *lxcpath, const char *suffix)
{
- int fd, len, ret;
- char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = {0};
- char *offset = &path[1];
-
- /* -2 here because this is an abstract unix socket so it needs a
- * leading \0, and we null terminate, so it needs a trailing \0.
- * Although null termination isn't required by the API, we do it anyway
- * because we print the sockname out sometimes.
- */
- len = sizeof(path) - 2;
- ret = lxc_make_abstract_socket_name(offset, len, name, lxcpath, NULL, suffix);
+ int fd, ret;
+ char path[LXC_AUDS_ADDR_LEN] = {0};
+
+ ret = lxc_make_abstract_socket_name(path, sizeof(path), name, lxcpath, NULL, suffix);
if (ret < 0)
return -1;
- TRACE("Creating abstract unix socket \"%s\"", offset);
+ TRACE("Creating abstract unix socket \"%s\"", &path[1]);
fd = lxc_abstract_unix_open(path, SOCK_STREAM, 0);
if (fd < 0) {
- ERROR("%s - Failed to create command socket %s",
- strerror(errno), offset);
+ SYSERROR("Failed to create command socket %s", &path[1]);
if (errno == EADDRINUSE)
ERROR("Container \"%s\" appears to be already running", name);
+
return -1;
}