]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
lxccontainer: add console_log() API extension
authorChristian Brauner <christian.brauner@ubuntu.com>
Sun, 22 Oct 2017 20:14:49 +0000 (22:14 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Mon, 6 Nov 2017 23:54:53 +0000 (00:54 +0100)
commands: add LXC_CMD_CONSOLE_LOG

Closes #1870.

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/commands.c
src/lxc/commands.h
src/lxc/lxccontainer.c
src/lxc/lxccontainer.h

index 8bc8d696ffe436989c2eac362fa930f7ca98d976..36570714f4219064338d2f445017a013bcfbd976 100644 (file)
@@ -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:
index cc5eec3c55267b253ed914071740e7de4e4284f9..416cfded62a941842e8cfee056fe97ca1e95f22b 100644 (file)
@@ -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 */
index efb1bf7ae8122ba5dccb29b0d86fa31af367072c..8c0487853c7a65a213ded52107f0b704a1654b8a 100644 (file)
@@ -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;
 
index 84bdab81c64b860a025a4416a657ce5f27e84143..22ab852781ed04272dfcbbe1af1e8c0a257f436e 100644 (file)
@@ -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.
  *