lxccontainer: add console_log() API extension

commands: add LXC_CMD_CONSOLE_LOG Closes #1870. Signed-off-by: 's avatarChristian Brauner <christian.brauner@ubuntu.com>
parent fe84a562
......@@ -92,6 +92,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
[LXC_CMD_GET_LXCPATH] = "get_lxcpath",
[LXC_CMD_ADD_STATE_CLIENT] = "add_state_client",
[LXC_CMD_SET_CONFIG_ITEM] = "set_config_item",
[LXC_CMD_CONSOLE_LOG] = "console_log",
};
if (cmd >= LXC_CMD_MAX)
......@@ -156,7 +157,8 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
return ret;
}
if (rsp->datalen > LXC_CMD_DATA_MAX) {
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),
......@@ -164,7 +166,12 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
return -EFBIG;
}
rsp->data = malloc(rsp->datalen);
if (cmd->req.cmd == LXC_CMD_CONSOLE_LOG) {
rsp->data = malloc(rsp->datalen + 1);
((char *)rsp->data)[rsp->datalen] = '\0';
} else {
rsp->data = malloc(rsp->datalen);
}
if (!rsp->data) {
errno = ENOMEM;
ERROR("%s - Failed to allocate response buffer for command "
......@@ -977,6 +984,79 @@ static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req,
return lxc_cmd_rsp_send(fd, &rsp);
}
int lxc_cmd_console_log(const char *name, const char *lxcpath,
struct lxc_console_log *log)
{
int ret, stopped;
struct lxc_cmd_console_log data;
struct lxc_cmd_rr cmd;
data.clear = log->clear;
data.read = log->read;
data.read_max = *log->read_max;
cmd.req.cmd = LXC_CMD_CONSOLE_LOG;
cmd.req.data = &data;
cmd.req.datalen = sizeof(struct lxc_cmd_console_log);
ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
if (ret < 0)
return ret;
/* There is nothing to be read from the buffer. So clear any values we
* where passed to clearly indicate to the user that nothing went wrong.
*/
if (cmd.rsp.ret == -ENODATA || cmd.rsp.ret == -EFAULT) {
*log->read_max = 0;
log->data = NULL;
}
/* This is a proper error so don't touch any values we were passed. */
if (cmd.rsp.ret < 0)
return cmd.rsp.ret;
*log->read_max = cmd.rsp.datalen;
log->data = cmd.rsp.data;
return 0;
}
static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler)
{
struct lxc_cmd_rsp rsp;
uint64_t logsize = handler->conf->console.log_size;
const struct lxc_cmd_console_log *log = req->data;
struct lxc_ringbuf *buf = &handler->conf->console.ringbuf;
rsp.ret = -EFAULT;
rsp.datalen = 0;
rsp.data = NULL;
if (logsize <= 0)
goto out;
rsp.datalen = lxc_ringbuf_used(buf);
if (log->read)
rsp.data = lxc_ringbuf_get_read_addr(buf);
if (log->read_max > 0 && (log->read_max <= rsp.datalen))
rsp.datalen = log->read_max;
/* there's nothing to read */
if (log->read && (buf->r_off == buf->w_off))
rsp.ret = -ENODATA;
else
rsp.ret = 0;
if (log->clear)
lxc_ringbuf_clear(buf);
else if (rsp.datalen > 0)
lxc_ringbuf_move_read_addr(buf, rsp.datalen);
out:
return lxc_cmd_rsp_send(fd, &rsp);
}
static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
struct lxc_handler *handler)
{
......@@ -995,6 +1075,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
[LXC_CMD_GET_LXCPATH] = lxc_cmd_get_lxcpath_callback,
[LXC_CMD_ADD_STATE_CLIENT] = lxc_cmd_add_state_client_callback,
[LXC_CMD_SET_CONFIG_ITEM] = lxc_cmd_set_config_item_callback,
[LXC_CMD_CONSOLE_LOG] = lxc_cmd_console_log_callback,
};
if (req->cmd >= LXC_CMD_MAX) {
......@@ -1017,6 +1098,7 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data,
{
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));
......@@ -1044,7 +1126,8 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data,
goto out_close;
}
if (req.datalen > LXC_CMD_DATA_MAX) {
if ((req.datalen > LXC_CMD_DATA_MAX) &&
(req.cmd != LXC_CMD_CONSOLE_LOG)) {
ERROR("Received command data length %d is too large for "
"command \"%s\"", req.datalen, lxc_cmd_str(req.cmd));
errno = EFBIG;
......@@ -1053,9 +1136,21 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data,
}
if (req.datalen > 0) {
void *reqdata;
/* 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;
}
reqdata = alloca(req.datalen);
ret = recv(fd, reqdata, req.datalen, 0);
if (ret != req.datalen) {
WARN("Failed to receive full command request. Ignoring "
......@@ -1075,6 +1170,9 @@ static int lxc_cmd_handler(int fd, uint32_t events, void *data,
}
out:
if (req.cmd == LXC_CMD_CONSOLE_LOG && reqdata)
free(reqdata);
return ret;
out_close:
......
......@@ -29,6 +29,7 @@
#include <sys/types.h>
#include "state.h"
#include "lxccontainer.h"
#define LXC_CMD_DATA_MAX (MAXPATHLEN * 2)
......@@ -49,6 +50,7 @@ typedef enum {
LXC_CMD_GET_LXCPATH,
LXC_CMD_ADD_STATE_CLIENT,
LXC_CMD_SET_CONFIG_ITEM,
LXC_CMD_CONSOLE_LOG,
LXC_CMD_MAX,
} lxc_cmd_t;
......@@ -79,6 +81,13 @@ struct lxc_cmd_set_config_item_req_data {
void *value;
};
struct lxc_cmd_console_log {
bool clear;
bool read;
uint64_t read_max;
};
extern int lxc_cmd_console_winch(const char *name, const char *lxcpath);
extern int lxc_cmd_console(const char *name, int *ttynum, int *fd,
const char *lxcpath);
......@@ -124,5 +133,7 @@ extern int lxc_try_cmd(const char *name, const char *lxcpath);
extern int lxc_cmd_set_config_item(const char *name, const char *item,
const char *value, const char *lxcpath);
extern int lxc_cmd_console_log(const char *name, const char *lxcpath,
struct lxc_console_log *log);
#endif /* __commands_h */
......@@ -516,6 +516,25 @@ static int lxcapi_console(struct lxc_container *c, int ttynum, int stdinfd,
return ret;
}
static int do_lxcapi_console_log(struct lxc_container *c, struct lxc_console_log *log)
{
int ret;
ret = lxc_cmd_console_log(c->name, do_lxcapi_get_config_path(c), log);
if (ret < 0) {
if (ret == -ENODATA)
NOTICE("%s - The console log is empty", strerror(-ret));
else if (ret == -EFAULT)
NOTICE("%s - The container does not have a console log configured", strerror(-ret));
else
ERROR("%s - Failed to retrieve console log", strerror(-ret));
}
return ret;
}
WRAP_API_1(int, lxcapi_console_log, struct lxc_console_log *)
static pid_t do_lxcapi_init_pid(struct lxc_container *c)
{
if (!c)
......@@ -4607,6 +4626,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
c->checkpoint = lxcapi_checkpoint;
c->restore = lxcapi_restore;
c->migrate = lxcapi_migrate;
c->console_log = lxcapi_console_log;
return c;
......
......@@ -51,6 +51,8 @@ struct lxc_lock;
struct migrate_opts;
struct lxc_console_log;
/*!
* An LXC container.
*
......@@ -834,6 +836,16 @@ struct lxc_container {
* \return \c true on success, else \c false.
*/
bool (*set_running_config_item)(struct lxc_container *c, const char *key, const char *value);
/*!
* \brief Query the console log of a container.
*
* \param c Container.
* \param opts A lxc_console_log struct filled with relevant options.
*
* \return \c 0 on success, nonzero on failure.
*/
int (*console_log)(struct lxc_container *c, struct lxc_console_log *log);
};
/*!
......@@ -921,6 +933,28 @@ struct migrate_opts {
uint64_t ghost_limit;
};
struct lxc_console_log {
/* Clear the console log. */
bool clear;
/* Retrieve the console log. */
bool read;
/* This specifies the maximum size to read from the ringbuffer. Setting
* it to 0 means that the a read can be as big as the whole ringbuffer.
* On return callers can check how many bytes were actually read.
* If "read" and "clear" are set to false and a non-zero value is
* specified then up to "read_max" bytes of data will be discarded from
* the ringbuffer.
*/
uint64_t *read_max;
/* Data that was read from the ringbuffer. If "read_max" is 0 on return
* "data" is invalid.
*/
char *data;
};
/*!
* \brief Create a new container.
*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment