]> git.proxmox.com Git - mirror_lxc.git/commitdiff
Merge pull request #1908 from Cypresslin/fix-overlayfs
authorChristian Brauner <christian.brauner@ubuntu.com>
Thu, 9 Nov 2017 18:44:10 +0000 (19:44 +0100)
committerGitHub <noreply@github.com>
Thu, 9 Nov 2017 18:44:10 +0000 (19:44 +0100)
lxc-test-unpriv: fix the overlayfs mount error

12 files changed:
.gitignore
src/lxc/commands.c
src/lxc/commands.h
src/lxc/conf.c
src/lxc/conf.h
src/lxc/console.c
src/lxc/console.h
src/lxc/lxccontainer.c
src/lxc/lxccontainer.h
src/lxc/start.c
src/tests/Makefile.am
src/tests/console_log.c [new file with mode: 0644]

index 3930b11fa62f76f0c821022375deb379881bdbcd..892489cc959de7b0d47b7a8e5c7e0c897d7adcf5 100644 (file)
@@ -78,6 +78,7 @@ src/tests/lxc-test-cgpath
 src/tests/lxc-test-clonetest
 src/tests/lxc-test-concurrent
 src/tests/lxc-test-console
+src/tests/lxc-test-console-log
 src/tests/lxc-test-containertests
 src/tests/lxc-test-createtest
 src/tests/lxc-test-destroytest
index 86d5b0208b8b6238fcd45390146c70d2323694c2..880cb28c2f66a02dfb371d83fbfaacf2c9c71b90 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <stdio.h>
+#include "config.h"
+
 #include <errno.h>
-#include <unistd.h>
-#include <signal.h>
 #include <fcntl.h>
+#include <malloc.h>
 #include <poll.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 <sys/param.h>
-#include <malloc.h>
-#include <stdlib.h>
 
-#include "log.h"
-#include "lxc.h"
-#include "conf.h"
-#include "start.h"     /* for struct lxc_handler */
-#include "utils.h"
+#include "af_unix.h"
 #include "cgroup.h"
 #include "commands.h"
 #include "commands_utils.h"
-#include "console.h"
+#include "conf.h"
 #include "confile.h"
+#include "console.h"
+#include "log.h"
+#include "lxc.h"
 #include "lxclock.h"
 #include "mainloop.h"
 #include "monitor.h"
-#include "af_unix.h"
-#include "config.h"
+#include "start.h"
+#include "utils.h"
 
 /*
- * 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).
+ * 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 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 additional data.
+ * 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
+ * additional 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.
+ * 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.
  *
- * IMPORTANTLY: Note that semantics for current commands are fixed.  If you
- * wish to make any changes to how, say, LXC_CMD_GET_CONFIG_ITEM works by
- * adding information to the end of cmd.data, then you must introduce a new
- * LXC_CMD_GET_CONFIG_ITEM_V2 define with a new number.  You may wish to
- * also mark LXC_CMD_GET_CONFIG_ITEM deprecated in commands.h.
+ * IMPORTANTLY: Note that semantics for current commands are fixed. If you wish
+ * to make any changes to how, say, LXC_CMD_GET_CONFIG_ITEM works by adding
+ * information to the end of cmd.data, then you must introduce a new
+ * LXC_CMD_GET_CONFIG_ITEM_V2 define with a new number. You may wish to also
+ * mark LXC_CMD_GET_CONFIG_ITEM deprecated in commands.h.
  *
  * This is necessary in order to avoid having a newly compiled lxc command
  * communicating with a running (old) monitor from crashing the running
@@ -78,7 +79,7 @@ lxc_log_define(lxc_commands, lxc);
 
 static const char *lxc_cmd_str(lxc_cmd_t cmd)
 {
-       static const char * const cmdname[LXC_CMD_MAX] = {
+       static const char *const cmdname[LXC_CMD_MAX] = {
                [LXC_CMD_CONSOLE]          = "console",
                [LXC_CMD_CONSOLE_WINCH]    = "console_winch",
                [LXC_CMD_STOP]             = "stop",
@@ -91,10 +92,12 @@ 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)
                return "Unknown cmd";
+
        return cmdname[cmd];
 }
 
@@ -122,11 +125,11 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
 
        ret = lxc_abstract_unix_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp));
        if (ret < 0) {
-               WARN("Command %s failed to receive response: %s.",
-                    lxc_cmd_str(cmd->req.cmd), strerror(errno));
+               WARN("%s - Failed to receive response for command \"%s\"",
+                    strerror(errno), lxc_cmd_str(cmd->req.cmd));
                return -1;
        }
-       TRACE("Command \"%s received response", lxc_cmd_str(cmd->req.cmd));
+       TRACE("Command \"%s\" received response", lxc_cmd_str(cmd->req.cmd));
 
        if (cmd->req.cmd == LXC_CMD_CONSOLE) {
                struct lxc_cmd_console_rsp_data *rspdata;
@@ -139,9 +142,9 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
 
                rspdata = malloc(sizeof(*rspdata));
                if (!rspdata) {
-                       ERROR("Command %s couldn't allocate response buffer.",
+                       ERROR("Failed to allocate response buffer for command \"%s\"",
                              lxc_cmd_str(cmd->req.cmd));
-                       return -1;
+                       return -ENOMEM;
                }
                rspdata->masterfd = rspfd;
                rspdata->ttynum = PTR_TO_INT(rsp->data);
@@ -149,26 +152,36 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
        }
 
        if (rsp->datalen == 0) {
-               DEBUG("command %s response data length is 0",
+               DEBUG("Response data length for command \"%s\" is 0",
                      lxc_cmd_str(cmd->req.cmd));
                return ret;
        }
-       if (rsp->datalen > LXC_CMD_DATA_MAX) {
-               ERROR("Command %s response data %d too long.",
-                     lxc_cmd_str(cmd->req.cmd), rsp->datalen);
+
+       if ((rsp->datalen > LXC_CMD_DATA_MAX) &&
+           (cmd->req.cmd != LXC_CMD_CONSOLE_LOG)) {
                errno = EFBIG;
-               return -1;
+               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;
        }
 
-       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) {
-               ERROR("Command %s was unable to allocate response buffer.",
-                     lxc_cmd_str(cmd->req.cmd));
-               return -1;
+               errno = ENOMEM;
+               ERROR("%s - Failed to allocate response buffer for command "
+                     "\"%s\"", strerror(errno), lxc_cmd_str(cmd->req.cmd));
+               return -ENOMEM;
        }
+
        ret = recv(sock, rsp->data, rsp->datalen, 0);
        if (ret != rsp->datalen) {
-               ERROR("Command %s failed to receive response data: %s.",
+               ERROR("%s - Failed to receive response data for command \"%s\"",
                      lxc_cmd_str(cmd->req.cmd), strerror(errno));
                if (ret >= 0)
                        ret = -1;
@@ -187,43 +200,45 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
  */
 static int lxc_cmd_rsp_send(int fd, struct lxc_cmd_rsp *rsp)
 {
-       int ret;
+       ssize_t ret;
 
        ret = send(fd, rsp, sizeof(*rsp), 0);
-       if (ret != sizeof(*rsp)) {
-               ERROR("Failed to send command response %d: %s.", ret,
-                     strerror(errno));
+       if (ret < 0 || (size_t)ret != sizeof(*rsp)) {
+               ERROR("%s - Failed to send command response %zd",
+                     strerror(errno), ret);
                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;
-               }
+       if (rsp->datalen <= 0)
+               return 0;
+
+       ret = send(fd, rsp->data, rsp->datalen, 0);
+       if (ret < 0 || ret != (ssize_t)rsp->datalen) {
+               WARN("%s - Failed to send command response data %zd",
+                    strerror(errno), ret);
+               return -1;
        }
+
        return 0;
 }
 
 static int lxc_cmd_send(const char *name, struct lxc_cmd_rr *cmd,
-                const char *lxcpath, const char *hashed_sock_name)
+                       const char *lxcpath, const char *hashed_sock_name)
 {
        int client_fd;
-       int ret = -1;
+       ssize_t ret = -1;
 
        client_fd = lxc_cmd_connect(name, lxcpath, hashed_sock_name);
-       if (client_fd < 0 && client_fd == -ECONNREFUSED)
-               return -ECONNREFUSED;
-       else if (client_fd < 0)
-               return -1;
+       if (client_fd < 0) {
+               if (client_fd == -ECONNREFUSED)
+                       return -ECONNREFUSED;
 
-       TRACE("Command \"%s\" connected to command socket",
-             lxc_cmd_str(cmd->req.cmd));
+               return -1;
+       }
 
-       ret = lxc_abstract_unix_send_credential(client_fd, &cmd->req, sizeof(cmd->req));
-       if (ret != sizeof(cmd->req)) {
+       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)
@@ -235,22 +250,20 @@ static int lxc_cmd_send(const char *name, struct lxc_cmd_rr *cmd,
                return -1;
        }
 
-       TRACE("Command \"%s\" requested data of length %d",
-             lxc_cmd_str(cmd->req.cmd), cmd->req.datalen);
+       if (cmd->req.datalen <= 0)
+               return client_fd;
 
-       if (cmd->req.datalen > 0) {
-               ret = send(client_fd, cmd->req.data, cmd->req.datalen, MSG_NOSIGNAL);
-               if (ret != cmd->req.datalen) {
-                       close(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 (errno == EPIPE)
+                       return -EPIPE;
 
-                       if (ret >= 0)
-                               return -EMSGSIZE;
+               if (ret >= 0)
+                       return -EMSGSIZE;
 
-                       return -1;
-               }
+               return -1;
        }
 
        return client_fd;
@@ -278,7 +291,8 @@ static int lxc_cmd_send(const char *name, struct lxc_cmd_rr *cmd,
 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, ret = -1;
+       int client_fd;
+       int ret = -1;
        bool stay_connected = false;
 
        if (cmd->req.cmd == LXC_CMD_CONSOLE ||
@@ -287,13 +301,10 @@ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
 
        *stopped = 0;
 
-       TRACE("command %s tries to connect command socket",
-             lxc_cmd_str(cmd->req.cmd));
-
        client_fd = lxc_cmd_send(name, cmd, lxcpath, hashed_sock_name);
        if (client_fd < 0) {
-               TRACE("command %s failed to connect command socket: %s",
-                     lxc_cmd_str(cmd->req.cmd), strerror(errno));
+               TRACE("%s - Command \"%s\" failed to connect command socket",
+                     strerror(errno), lxc_cmd_str(cmd->req.cmd));
                if (client_fd == -ECONNREFUSED) {
                        *stopped = 1;
                        return -1;
@@ -309,6 +320,7 @@ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
 out:
        if (!stay_connected || ret <= 0)
                close(client_fd);
+
        if (stay_connected && ret > 0)
                cmd->rsp.ret = client_fd;
 
@@ -327,7 +339,6 @@ int lxc_try_cmd(const char *name, const char *lxcpath)
        };
 
        ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
-
        if (stopped)
                return 0;
        if (ret > 0 && cmd.rsp.ret < 0) {
@@ -337,14 +348,11 @@ int lxc_try_cmd(const char *name, const char *lxcpath)
        if (ret > 0)
                return 0;
 
-       /*
-        * At this point we weren't denied access, and the
-        * container *was* started.  There was some inexplicable
-        * error in the protocol.
-        * I'm not clear on whether we should return -1 here, but
-        * we didn't receive a -EACCES, so technically it's not that
-        * we're not allowed to control the container - it's just not
-        * behaving.
+       /* At this point we weren't denied access, and the container *was*
+        * started. There was some inexplicable error in the protocol.  I'm not
+        * clear on whether we should return -1 here, but we didn't receive a
+        * -EACCES, so technically it's not that we're not allowed to control
+        * the container - it's just not behaving.
         */
        return 0;
 }
@@ -424,37 +432,26 @@ static int lxc_cmd_get_clone_flags_callback(int fd, struct lxc_cmd_req *req,
  * returned path.
  */
 char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath,
-       const char *subsystem)
+                             const char *subsystem)
 {
        int ret, stopped;
        struct lxc_cmd_rr cmd = {
                .req = {
                        .cmd = LXC_CMD_GET_CGROUP,
-                       .datalen = strlen(subsystem)+1,
+                       .datalen = strlen(subsystem) + 1,
                        .data = subsystem,
                },
        };
 
        ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
-       if (ret < 0) {
-               TRACE("command %s failed for container \"%s\": %s.",
-                     lxc_cmd_str(cmd.req.cmd), name, strerror(errno));
+       if (ret < 0)
                return NULL;
-       }
 
-       if (!ret) {
-               WARN("container \"%s\" has stopped before sending its state", name);
+       if (ret == 0)
                return NULL;
-       }
 
-       if (cmd.rsp.ret < 0 || cmd.rsp.datalen < 0) {
-               ERROR("command %s failed for container \"%s\": %s",
-                     lxc_cmd_str(cmd.req.cmd), name, strerror(-cmd.rsp.ret));
+       if (cmd.rsp.ret < 0 || cmd.rsp.datalen < 0)
                return NULL;
-       }
-
-       TRACE("command %s successful for container \"%s\"",
-             lxc_cmd_str(cmd.req.cmd), name);
 
        return cmd.rsp.data;
 }
@@ -462,8 +459,8 @@ char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath,
 static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req,
                                       struct lxc_handler *handler)
 {
-       struct lxc_cmd_rsp rsp;
        const char *path;
+       struct lxc_cmd_rsp rsp;
 
        if (req->datalen < 1)
                return -1;
@@ -471,9 +468,10 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req,
        path = cgroup_get_cgroup(handler, req->data);
        if (!path)
                return -1;
-       rsp.datalen = strlen(path) + 1,
-       rsp.data = (char *)path;
+
        rsp.ret = 0;
+       rsp.datalen = strlen(path) + 1;
+       rsp.data = (char *)path;
 
        return lxc_cmd_rsp_send(fd, &rsp);
 }
@@ -495,7 +493,7 @@ char *lxc_cmd_get_config_item(const char *name, const char *item,
        struct lxc_cmd_rr cmd = {
                .req = { .cmd = LXC_CMD_GET_CONFIG_ITEM,
                         .data = item,
-                        .datalen = strlen(item)+1,
+                        .datalen = strlen(item) + 1,
                       },
        };
 
@@ -505,6 +503,7 @@ char *lxc_cmd_get_config_item(const char *name, const char *item,
 
        if (cmd.rsp.ret == 0)
                return cmd.rsp.data;
+
        return NULL;
 }
 
@@ -512,14 +511,15 @@ 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;
        struct lxc_config_t *item;
+       struct lxc_cmd_rsp rsp;
 
        memset(&rsp, 0, sizeof(rsp));
        item = lxc_get_config(req->data);
        if (!item)
                goto err1;
+
        cilen = item->get(req->data, NULL, 0, handler->conf, NULL);
        if (cilen <= 0)
                goto err1;
@@ -527,6 +527,7 @@ static int lxc_cmd_get_config_item_callback(int fd, struct lxc_cmd_req *req,
        cidata = alloca(cilen + 1);
        if (item->get(req->data, cidata, cilen + 1, handler->conf, NULL) != cilen)
                goto err1;
+
        cidata[cilen] = '\0';
        rsp.data = cidata;
        rsp.datalen = cilen + 1;
@@ -589,6 +590,7 @@ static int lxc_cmd_set_config_item_callback(int fd, struct lxc_cmd_req *req,
 
        memset(&rsp, 0, sizeof(rsp));
        rsp.ret = lxc_set_config_item_locked(handler->conf, key, value);
+
        return lxc_cmd_rsp_send(fd, &rsp);
 }
 
@@ -615,12 +617,13 @@ int lxc_cmd_get_state(const char *name, const char *lxcpath)
                return -1;
 
        if (!ret) {
-               WARN("Container \"%s\" has stopped before sending its state.", name);
+               WARN("Container \"%s\" has stopped before sending its state", name);
                return -1;
        }
 
-       DEBUG("Container \"%s\" is in \"%s\" state.", name,
+       DEBUG("Container \"%s\" is in \"%s\" state", name,
              lxc_state2str(PTR_TO_INT(cmd.rsp.data)));
+
        return PTR_TO_INT(cmd.rsp.data);
 }
 
@@ -651,22 +654,23 @@ int lxc_cmd_stop(const char *name, const char *lxcpath)
        ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
        if (ret < 0) {
                if (stopped) {
-                       INFO("Container \"%s\" is already stopped.", name);
+                       INFO("Container \"%s\" is already stopped", name);
                        return 0;
                }
+
                return -1;
        }
 
-       /* we do not expect any answer, because we wait for the connection to be
-        * closed
+       /* We do not expect any answer, because we wait for the connection to be
+        * closed.
         */
        if (ret > 0) {
-               ERROR("Failed to stop container \"%s\": %s.", name,
-                     strerror(-cmd.rsp.ret));
+               ERROR("%s - Failed to stop container \"%s\"",
+                     strerror(-cmd.rsp.ret), name);
                return -1;
        }
 
-       INFO("Container \"%s\" has stopped.", name);
+       INFO("Container \"%s\" has stopped", name);
        return 0;
 }
 
@@ -681,14 +685,15 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req,
        memset(&rsp, 0, sizeof(rsp));
        rsp.ret = kill(handler->pid, stopsignal);
        if (!rsp.ret) {
-               /* we can't just use lxc_unfreeze() since we are already in the
+               /* We can't just use lxc_unfreeze() since we are already in the
                 * context of handling the STOP cmd in lxc-start, and calling
                 * lxc_unfreeze() would do another cmd (GET_CGROUP) which would
-                * deadlock us
+                * deadlock us.
                 */
                if (cgroup_unfreeze(handler))
                        return 0;
-               ERROR("Failed to unfreeze container \"%s\".", handler->name);
+
+               ERROR("Failed to unfreeze container \"%s\"", handler->name);
                rsp.ret = -1;
        }
 
@@ -723,6 +728,7 @@ static int lxc_cmd_console_winch_callback(int fd, struct lxc_cmd_req *req,
        struct lxc_cmd_rsp rsp = { .data = 0 };
 
        lxc_console_sigwinch(SIGWINCH);
+
        return lxc_cmd_rsp_send(fd, &rsp);
 }
 
@@ -750,27 +756,28 @@ int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath)
                return ret;
 
        if (cmd.rsp.ret < 0) {
-               ERROR("Console access denied: %s.", strerror(-cmd.rsp.ret));
+               ERROR("%s - Denied access to tty", strerror(-cmd.rsp.ret));
                ret = -1;
                goto out;
        }
 
        if (ret == 0) {
-               ERROR("Console %d invalid, busy or all consoles busy.", *ttynum);
+               ERROR("tty number %d invalid, busy or all ttys busy", *ttynum);
                ret = -1;
                goto out;
        }
 
        rspdata = cmd.rsp.data;
        if (rspdata->masterfd < 0) {
-               ERROR("Unable to allocate fd for tty %d.", rspdata->ttynum);
+               ERROR("Unable to allocate fd for tty %d", rspdata->ttynum);
                goto out;
        }
 
-       ret = cmd.rsp.ret;      /* sock fd */
+       ret = cmd.rsp.ret; /* socket fd */
        *fd = rspdata->masterfd;
        *ttynum = rspdata->ttynum;
-       INFO("tty %d allocated fd %d sock %d.", rspdata->ttynum, *fd, ret);
+       INFO("Alloced fd %d for tty %d via socket %d", *fd, rspdata->ttynum, ret);
+
 out:
        free(cmd.rsp.data);
        return ret;
@@ -779,9 +786,9 @@ out:
 static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req,
                                    struct lxc_handler *handler)
 {
-       int ttynum = PTR_TO_INT(req->data);
-       int masterfd;
+       int masterfd, ret;
        struct lxc_cmd_rsp rsp;
+       int ttynum = PTR_TO_INT(req->data);
 
        masterfd = lxc_console_allocate(handler->conf, fd, &ttynum);
        if (masterfd < 0)
@@ -789,8 +796,9 @@ static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req,
 
        memset(&rsp, 0, sizeof(rsp));
        rsp.data = INT_TO_PTR(ttynum);
-       if (lxc_abstract_unix_send_fds(fd, &masterfd, 1, &rsp, sizeof(rsp)) < 0) {
-               ERROR("Failed to send tty to client.");
+       ret = lxc_abstract_unix_send_fds(fd, &masterfd, 1, &rsp, sizeof(rsp));
+       if (ret < 0) {
+               ERROR("Failed to send tty to client");
                lxc_console_free(handler->conf, fd);
                goto out_close;
        }
@@ -798,8 +806,8 @@ static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req,
        return 0;
 
 out_close:
-       /* special indicator to lxc_cmd_handler() to close the fd and do
-        * related cleanup
+       /* Special indicator to lxc_cmd_handler() to close the fd and do
+        * related cleanup.
         */
        return 1;
 }
@@ -819,17 +827,17 @@ char *lxc_cmd_get_name(const char *hashed_sock_name)
        };
 
        ret = lxc_cmd(NULL, &cmd, &stopped, NULL, hashed_sock_name);
-       if (ret < 0) {
+       if (ret < 0)
                return NULL;
-       }
 
        if (cmd.rsp.ret == 0)
                return cmd.rsp.data;
+
        return NULL;
 }
 
 static int lxc_cmd_get_name_callback(int fd, struct lxc_cmd_req *req,
-                                           struct lxc_handler *handler)
+                                    struct lxc_handler *handler)
 {
        struct lxc_cmd_rsp rsp;
 
@@ -857,12 +865,12 @@ char *lxc_cmd_get_lxcpath(const char *hashed_sock_name)
        };
 
        ret = lxc_cmd(NULL, &cmd, &stopped, NULL, hashed_sock_name);
-       if (ret < 0) {
+       if (ret < 0)
                return NULL;
-       }
 
        if (cmd.rsp.ret == 0)
                return cmd.rsp.data;
+
        return NULL;
 }
 
@@ -873,9 +881,9 @@ static int lxc_cmd_get_lxcpath_callback(int fd, struct lxc_cmd_req *req,
 
        memset(&rsp, 0, sizeof(rsp));
 
+       rsp.ret = 0;
        rsp.data = (char *)handler->lxcpath;
        rsp.datalen = strlen(handler->lxcpath) + 1;
-       rsp.ret = 0;
 
        return lxc_cmd_rsp_send(fd, &rsp);
 }
@@ -903,53 +911,49 @@ int lxc_cmd_add_state_client(const char *name, const char *lxcpath,
        state = lxc_getstate(name, lxcpath);
        if (state < 0) {
                process_unlock();
-               TRACE("failed to retrieve state of container: %s",
-                     strerror(errno));
+               TRACE("%s - Failed to retrieve state of container", strerror(errno));
                return -1;
        } else if (states[state]) {
                process_unlock();
-               TRACE("container is %s state", lxc_state2str(state));
+               TRACE("Container is %s state", lxc_state2str(state));
                return state;
        }
 
        if ((state == STARTING) && !states[RUNNING] && !states[STOPPING] && !states[STOPPED]) {
                process_unlock();
-               TRACE("container is in %s state and caller requested to be "
-                     "informed about a previous state",
-                     lxc_state2str(state));
+               TRACE("Container is in %s state and caller requested to be "
+                     "informed about a previous state", lxc_state2str(state));
                return state;
        } else if ((state == RUNNING) && !states[STOPPING] && !states[STOPPED]) {
                process_unlock();
-               TRACE("container is in %s state and caller requested to be "
-                     "informed about a previous state",
-                     lxc_state2str(state));
+               TRACE("Container is in %s state and caller requested to be "
+                     "informed about a previous state", lxc_state2str(state));
                return state;
        } else if ((state == STOPPING) && !states[STOPPED]) {
                process_unlock();
-               TRACE("container is in %s state and caller requested to be "
-                     "informed about a previous state",
-                     lxc_state2str(state));
+               TRACE("Container is in %s state and caller requested to be "
+                     "informed about a previous state", lxc_state2str(state));
                return state;
        } else if ((state == STOPPED) || (state == ABORTING)) {
                process_unlock();
-               TRACE("container is in %s state and caller requested to be "
-                     "informed about a previous state",
-                     lxc_state2str(state));
+               TRACE("Container is in %s state and caller requested to be "
+                     "informed about a previous state", lxc_state2str(state));
                return state;
        }
 
        ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
        process_unlock();
        if (ret < 0) {
-               ERROR("failed to execute command: %s", strerror(errno));
+               ERROR("%s - Failed to execute command", strerror(errno));
                return -1;
        }
+
        /* We should now be guaranteed to get an answer from the state sending
         * function.
         */
 
        if (cmd.rsp.ret < 0) {
-               ERROR("failed to receive socket fd");
+               ERROR("Failed to receive socket fd");
                return -1;
        }
 
@@ -962,20 +966,14 @@ static int lxc_cmd_add_state_client_callback(int fd, struct lxc_cmd_req *req,
 {
        struct lxc_cmd_rsp rsp = {0};
 
-       if (req->datalen < 0) {
-               TRACE("Requested datalen was < 0");
+       if (req->datalen < 0)
                return -1;
-       }
 
-       if (req->datalen > (sizeof(lxc_state_t) * MAX_STATE)) {
-               TRACE("Requested datalen was too large");
+       if (req->datalen > (sizeof(lxc_state_t) * MAX_STATE))
                return -1;
-       }
 
-       if (!req->data) {
-               TRACE("No states requested");
+       if (!req->data)
                return -1;
-       }
 
        rsp.ret = lxc_add_state_client(fd, handler, (lxc_state_t *)req->data);
        if (rsp.ret < 0)
@@ -986,6 +984,92 @@ 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;
+       data.write_logfile = log->write_logfile;
+
+       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 || cmd.rsp.ret == -ENOENT) {
+               *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_console *console = &handler->conf->console;
+       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 */
+       rsp.ret = -ENODATA;
+       if (log->read && (buf->r_off == buf->w_off))
+               goto out;
+
+       if (log->write_logfile && rsp.datalen > 0) {
+               rsp.ret = -ENOENT;
+               if (!console->log_path)
+                       goto out;
+
+               rsp.ret = lxc_console_write_ringbuffer(console);
+               if (rsp.ret < 0)
+                       goto out;
+       }
+
+       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)
 {
@@ -1004,10 +1088,11 @@ 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) {
-               ERROR("Undefined command id %d received.", req->cmd);
+               ERROR("Undefined command id %d", req->cmd);
                return -1;
        }
        return cb[req->cmd](fd, req, handler);
@@ -1026,69 +1111,83 @@ 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));
        if (ret == -EACCES) {
-               /* we don't care for the peer, just send and close */
-               struct lxc_cmd_rsp rsp = { .ret = ret };
+               /* 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;
        }
 
-       TRACE("Processing \"%s\" command", lxc_cmd_str(req.cmd));
        if (ret < 0) {
-               SYSERROR("Failed to receive data on command socket for \"%s\"",
-                        lxc_cmd_str(req.cmd));
+               SYSERROR("Failed to receive data on command socket for command "
+                        "\"%s\"", lxc_cmd_str(req.cmd));
                goto out_close;
        }
 
-       if (!ret) {
-               DEBUG("Peer has disconnected for \"%s\"", lxc_cmd_str(req.cmd));
+       if (ret == 0)
                goto out_close;
-       }
 
        if (ret != sizeof(req)) {
                WARN("Failed to receive full command request. Ignoring request "
-                    "for \"%s\"",
-                    lxc_cmd_str(req.cmd));
+                    "for \"%s\"", lxc_cmd_str(req.cmd));
                ret = -1;
                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));
-               ret = -1;
+                     "command \"%s\"", req.datalen, lxc_cmd_str(req.cmd));
+               errno = EFBIG;
+               ret = -EFBIG;
                goto out_close;
        }
 
        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 "
-                            "request for \"%s\"",
-                            lxc_cmd_str(req.cmd));
+                            "request for \"%s\"", lxc_cmd_str(req.cmd));
                        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 */
+               /* This is not an error, but only a request to close fd. */
                ret = 0;
                goto out_close;
        }
 
 out:
+       if (req.cmd == LXC_CMD_CONSOLE_LOG && reqdata)
+               free(reqdata);
+
        return ret;
+
 out_close:
        lxc_cmd_fd_cleanup(fd, handler, descr);
        goto out;
@@ -1097,7 +1196,8 @@ out_close:
 static int lxc_cmd_accept(int fd, uint32_t events, void *data,
                          struct lxc_epoll_descr *descr)
 {
-       int opt = 1, ret = -1, connection;
+       int connection;
+       int opt = 1, ret = -1;
 
        connection = accept(fd, NULL, 0);
        if (connection < 0) {
@@ -1105,20 +1205,21 @@ static int lxc_cmd_accept(int fd, uint32_t events, void *data,
                return -1;
        }
 
-       if (fcntl(connection, F_SETFD, FD_CLOEXEC)) {
-               SYSERROR("Failed to set close-on-exec on incoming command connection.");
+       ret = fcntl(connection, F_SETFD, FD_CLOEXEC);
+       if (ret < 0) {
+               SYSERROR("Failed to set close-on-exec on incoming command connection");
                goto out_close;
        }
 
-       if (setsockopt(connection, SOL_SOCKET,
-                      SO_PASSCRED, &opt, sizeof(opt))) {
-               SYSERROR("Failed to enable necessary credentials on command socket.");
+       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;
        }
 
        ret = lxc_mainloop_add_handler(descr, connection, lxc_cmd_handler, data);
        if (ret) {
-               ERROR("Failed to add command handler.");
+               ERROR("Failed to add command handler");
                goto out_close;
        }
 
@@ -1133,10 +1234,9 @@ out_close:
 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 };
+       int fd, len, ret;
+       char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = {0};
        char *offset = &path[1];
-       int len;
 
        /* -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.
@@ -1144,21 +1244,22 @@ int lxc_cmd_init(const char *name, struct lxc_handler *handler,
         * because we print the sockname out sometimes.
         */
        len = sizeof(path) - 2;
-       if (lxc_make_abstract_socket_name(offset, len, name, lxcpath, NULL,
-                                         "command"))
+       ret = lxc_make_abstract_socket_name(offset, len, name, lxcpath, NULL, "command");
+       if (ret < 0)
                return -1;
 
        fd = lxc_abstract_unix_open(path, SOCK_STREAM, 0);
        if (fd < 0) {
-               ERROR("Failed to create the command service point %s: %s.",
-                     offset, strerror(errno));
+               ERROR("%s - Failed to create command socket %s",
+                     strerror(errno), offset);
                if (errno == EADDRINUSE)
-                       ERROR("Container \"%s\" appears to be already running!", name);
+                       ERROR("Container \"%s\" appears to be already running", name);
                return -1;
        }
 
-       if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
-               SYSERROR("Failed to set FD_CLOEXEC on signal file descriptor.");
+       ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
+       if (ret < 0) {
+               SYSERROR("Failed to set FD_CLOEXEC on command socket file descriptor");
                close(fd);
                return -1;
        }
@@ -1167,15 +1268,15 @@ int lxc_cmd_init(const char *name, struct lxc_handler *handler,
        return 0;
 }
 
-int lxc_cmd_mainloop_add(const char *name,
-                        struct lxc_epoll_descr *descr,
+int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
                         struct lxc_handler *handler)
 {
-       int ret, fd = handler->conf->maincmd_fd;
+       int ret;
+       int fd = handler->conf->maincmd_fd;
 
        ret = lxc_mainloop_add_handler(descr, fd, lxc_cmd_accept, handler);
-       if (ret) {
-               ERROR("Failed to add handler for command socket.");
+       if (ret < 0) {
+               ERROR("Failed to add handler for command socket");
                close(fd);
        }
 
index cc5eec3c55267b253ed914071740e7de4e4284f9..7e197b98d9565f0c7bdc4f44b965b0f704724372 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,14 @@ struct lxc_cmd_set_config_item_req_data {
        void *value;
 };
 
+struct lxc_cmd_console_log {
+       bool clear;
+       bool read;
+       uint64_t read_max;
+       bool write_logfile;
+
+};
+
 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 +134,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 88a1cf4e207813b07627385368bb70cc2f5fed13..2e9077c1b1157400a6169667ab201acb234473fc 100644 (file)
@@ -3083,65 +3083,7 @@ static bool verify_start_hooks(struct lxc_conf *conf)
        return true;
 }
 
-/**
- * Note that this function needs to run before the mainloop starts. Since we
- * register a handler for the console's masterfd when we create the mainloop
- * the console handler needs to see an allocated ringbuffer.
- */
-static int lxc_setup_console_ringbuf(struct lxc_console *console)
-{
-       int ret;
-       struct lxc_ringbuf *buf = &console->ringbuf;
-       uint64_t size = console->log_size;
-
-       /* no ringbuffer previously allocated and no ringbuffer requested */
-       if (!buf->addr && size <= 0)
-               return 0;
-
-       /* ringbuffer allocated but no new ringbuffer requested */
-       if (buf->addr && size <= 0) {
-               lxc_ringbuf_release(buf);
-               buf->addr = NULL;
-               buf->r_off = 0;
-               buf->w_off = 0;
-               buf->size = 0;
-               TRACE("Deallocated console ringbuffer");
-               return 0;
-       }
-
-       if (size <= 0)
-               return 0;
-
-       /* check wether the requested size for the ringbuffer has changed */
-       if (buf->addr && buf->size != size) {
-               TRACE("Console ringbuffer size changed from %" PRIu64
-                     " to %" PRIu64 " bytes. Deallocating console ringbuffer",
-                     buf->size, size);
-               lxc_ringbuf_release(buf);
-       }
-
-       ret = lxc_ringbuf_create(buf, size);
-       if (ret < 0) {
-               ERROR("Failed to setup %" PRIu64 " byte console ringbuffer", size);
-               return -1;
-       }
-
-       TRACE("Allocated %" PRIu64 " byte console ringbuffer", size);
-       return 0;
-}
-
-int lxc_setup_parent(struct lxc_handler *handler)
-{
-       int ret;
-
-       ret = lxc_setup_console_ringbuf(&handler->conf->console);
-       if (ret < 0)
-               return -1;
-
-       return 0;
-}
-
-int lxc_setup_child(struct lxc_handler *handler)
+int lxc_setup(struct lxc_handler *handler)
 {
        int ret;
        const char *name = handler->name;
index 063b589808b624ed2012e36a9013973f307804dd..43eeb4dedf26b7815ccd4786755fd8bd78fe1e91 100644 (file)
@@ -379,7 +379,7 @@ extern int lxc_delete_autodev(struct lxc_handler *handler);
 extern void lxc_clear_includes(struct lxc_conf *conf);
 extern int do_rootfs_setup(struct lxc_conf *conf, const char *name,
                           const char *lxcpath);
-extern int lxc_setup_child(struct lxc_handler *handler);
+extern int lxc_setup(struct lxc_handler *handler);
 extern int lxc_setup_parent(struct lxc_handler *handler);
 extern int setup_resource_limits(struct lxc_list *limits, pid_t pid);
 extern int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype);
index cbee9f8e8c0875ae0618366148561a05985d9993..2d08366170b2a46d3182e963af1620693ae59134 100644 (file)
@@ -512,11 +512,50 @@ out:
        return ret;
 }
 
+int lxc_console_write_ringbuffer(struct lxc_console *console)
+{
+       int fd;
+       char *r_addr;
+       ssize_t ret;
+       uint64_t used;
+       struct lxc_ringbuf *buf = &console->ringbuf;
+
+       if (!console->log_path)
+               return 0;
+
+       used = lxc_ringbuf_used(buf);
+       if (used == 0)
+               return 0;
+
+       fd = lxc_unpriv(open(console->log_path, O_CLOEXEC | O_RDWR | O_CREAT | O_TRUNC, 0600));
+       if (fd < 0) {
+               SYSERROR("Failed to open console log file \"%s\"", console->log_path);
+               return -EIO;
+       }
+       DEBUG("Using \"%s\" as console log file", console->log_path);
+
+       r_addr = lxc_ringbuf_get_read_addr(buf);
+       ret = lxc_write_nointr(fd, r_addr, used);
+       close(fd);
+       if (ret < 0)
+               return -EIO;
+
+       return 0;
+}
+
 void lxc_console_delete(struct lxc_console *console)
 {
-       if (console->tios && console->peer >= 0 &&
-           tcsetattr(console->peer, TCSAFLUSH, console->tios))
-               WARN("failed to set old terminal settings");
+       int ret;
+
+       ret = lxc_console_write_ringbuffer(console);
+       if (ret < 0)
+               WARN("Failed to write console log to disk");
+
+       if (console->tios && console->peer >= 0) {
+               ret = tcsetattr(console->peer, TCSAFLUSH, console->tios);
+               if (ret < 0)
+                       WARN("%s - Failed to set old terminal settings", strerror(errno));
+       }
        free(console->tios);
        console->tios = NULL;
 
@@ -525,66 +564,120 @@ void lxc_console_delete(struct lxc_console *console)
        close(console->slave);
        if (console->log_fd >= 0)
                close(console->log_fd);
-
        console->peer = -1;
        console->master = -1;
        console->slave = -1;
        console->log_fd = -1;
 }
 
+/**
+ * Note that this function needs to run before the mainloop starts. Since we
+ * register a handler for the console's masterfd when we create the mainloop
+ * the console handler needs to see an allocated ringbuffer.
+ */
+static int lxc_setup_console_ringbuf(struct lxc_console *console)
+{
+       int ret;
+       struct lxc_ringbuf *buf = &console->ringbuf;
+       uint64_t size = console->log_size;
+
+       /* no ringbuffer previously allocated and no ringbuffer requested */
+       if (!buf->addr && size <= 0)
+               return 0;
+
+       /* ringbuffer allocated but no new ringbuffer requested */
+       if (buf->addr && size <= 0) {
+               lxc_ringbuf_release(buf);
+               buf->addr = NULL;
+               buf->r_off = 0;
+               buf->w_off = 0;
+               buf->size = 0;
+               TRACE("Deallocated console ringbuffer");
+               return 0;
+       }
+
+       if (size <= 0)
+               return 0;
+
+       /* check wether the requested size for the ringbuffer has changed */
+       if (buf->addr && buf->size != size) {
+               TRACE("Console ringbuffer size changed from %" PRIu64
+                     " to %" PRIu64 " bytes. Deallocating console ringbuffer",
+                     buf->size, size);
+               lxc_ringbuf_release(buf);
+       }
+
+       ret = lxc_ringbuf_create(buf, size);
+       if (ret < 0) {
+               ERROR("Failed to setup %" PRIu64 " byte console ringbuffer", size);
+               return -1;
+       }
+
+       TRACE("Allocated %" PRIu64 " byte console ringbuffer", size);
+       return 0;
+}
+
 int lxc_console_create(struct lxc_conf *conf)
 {
+       int ret, saved_errno;
        struct lxc_console *console = &conf->console;
-       int ret;
 
        if (!conf->rootfs.path) {
-               INFO("container does not have a rootfs, console device will be shared with the host");
+               INFO("Container does not have a rootfs. The console will be "
+                    "shared with the host");
                return 0;
        }
 
        if (console->path && !strcmp(console->path, "none")) {
-               INFO("no console requested");
+               INFO("No console was requested");
                return 0;
        }
 
        process_lock();
        ret = openpty(&console->master, &console->slave, console->name, NULL, NULL);
+       saved_errno = errno;
        process_unlock();
        if (ret < 0) {
-               SYSERROR("failed to allocate a pty");
+               ERROR("%s - Failed to allocate a pty", strerror(saved_errno));
                return -1;
        }
 
-       if (fcntl(console->master, F_SETFD, FD_CLOEXEC)) {
-               SYSERROR("failed to set console master to close-on-exec");
+       ret = fcntl(console->master, F_SETFD, FD_CLOEXEC);
+       if (ret < 0) {
+               SYSERROR("Failed to set FD_CLOEXEC flag on console master");
                goto err;
        }
 
-       if (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) {
-               SYSERROR("failed to set console slave to close-on-exec");
+       ret = fcntl(console->slave, F_SETFD, FD_CLOEXEC);
+       if (ret < 0) {
+               SYSERROR("Failed to set FD_CLOEXEC flag on console slave");
                goto err;
        }
 
        ret = lxc_console_peer_default(console);
        if (ret < 0) {
-               ERROR("failed to allocate peer tty device");
+               ERROR("Failed to allocate a peer pty device");
                goto err;
        }
 
-       if (console->log_path) {
+       if (console->log_path && console->log_size <= 0) {
                console->log_fd = lxc_unpriv(open(console->log_path, O_CLOEXEC | O_RDWR | O_CREAT | O_APPEND, 0600));
                if (console->log_fd < 0) {
-                       SYSERROR("failed to open console log file \"%s\"", console->log_path);
+                       SYSERROR("Failed to open console log file \"%s\"", console->log_path);
                        goto err;
                }
-               DEBUG("using \"%s\" as console log file", console->log_path);
+               DEBUG("Using \"%s\" as console log file", console->log_path);
        }
 
+       ret = lxc_setup_console_ringbuf(console);
+       if (ret < 0)
+               goto err;
+
        return 0;
 
 err:
        lxc_console_delete(console);
-       return -1;
+       return -ENODEV;
 }
 
 int lxc_console_set_stdfds(int fd)
@@ -687,18 +780,16 @@ int lxc_console(struct lxc_container *c, int ttynum,
        istty = isatty(stdinfd);
        if (istty) {
                ret = lxc_setup_tios(stdinfd, &oldtios);
-               if (ret) {
-                       ERROR("failed to setup terminal properties");
+               if (ret < 0)
                        return -1;
-               }
        } else {
-               INFO("fd %d does not refer to a tty device", stdinfd);
+               INFO("File descriptor %d does not refer to a tty device", stdinfd);
        }
 
        ttyfd = lxc_cmd_console(c->name, &ttynum, &masterfd, c->config_path);
        if (ttyfd < 0) {
                ret = ttyfd;
-               goto err1;
+               goto restore_tios;
        }
 
        fprintf(stderr, "\n"
@@ -708,13 +799,13 @@ int lxc_console(struct lxc_container *c, int ttynum,
                        ttynum, 'a' + escape - 1);
 
        ret = setsid();
-       if (ret)
-               INFO("already group leader");
+       if (ret < 0)
+               TRACE("Process is already group leader");
 
        ts = lxc_console_sigwinch_init(stdinfd, masterfd);
        if (!ts) {
                ret = -1;
-               goto err2;
+               goto close_fds;
        }
        ts->escape = escape;
        ts->winch_proxy = c->name;
@@ -728,52 +819,57 @@ int lxc_console(struct lxc_container *c, int ttynum,
 
        ret = lxc_mainloop_open(&descr);
        if (ret) {
-               ERROR("failed to create mainloop");
-               goto err3;
+               ERROR("Failed to create mainloop");
+               goto sigwinch_fini;
        }
 
        if (ts->sigfd != -1) {
                ret = lxc_mainloop_add_handler(&descr, ts->sigfd,
-                               lxc_console_cb_sigwinch_fd, ts);
-               if (ret) {
-                       ERROR("failed to add handler for SIGWINCH fd");
-                       goto err4;
+                                              lxc_console_cb_sigwinch_fd, ts);
+               if (ret < 0) {
+                       ERROR("Failed to add SIGWINCH handler");
+                       goto close_mainloop;
                }
        }
 
        ret = lxc_mainloop_add_handler(&descr, ts->stdinfd,
                                       lxc_console_cb_tty_stdin, ts);
-       if (ret) {
-               ERROR("failed to add handler for stdinfd");
-               goto err4;
+       if (ret < 0) {
+               ERROR("Failed to add stdin handler");
+               goto close_mainloop;
        }
 
        ret = lxc_mainloop_add_handler(&descr, ts->masterfd,
                                       lxc_console_cb_tty_master, ts);
-       if (ret) {
-               ERROR("failed to add handler for masterfd");
-               goto err4;
+       if (ret < 0) {
+               ERROR("Failed to add master handler");
+               goto close_mainloop;
        }
 
        ret = lxc_mainloop(&descr, -1);
-       if (ret) {
-               ERROR("mainloop returned an error");
-               goto err4;
+       if (ret < 0) {
+               ERROR("The mainloop returned an error");
+               goto close_mainloop;
        }
 
        ret = 0;
 
-err4:
+close_mainloop:
        lxc_mainloop_close(&descr);
-err3:
+
+sigwinch_fini:
        lxc_console_sigwinch_fini(ts);
-err2:
+
+close_fds:
        close(masterfd);
        close(ttyfd);
-err1:
+
+restore_tios:
        if (istty) {
-               if (tcsetattr(stdinfd, TCSAFLUSH, &oldtios) < 0)
-                       WARN("failed to reset terminal properties: %s.", strerror(errno));
+               istty = tcsetattr(stdinfd, TCSAFLUSH, &oldtios);
+               if (istty < 0)
+                       WARN("%s - Failed to restore terminal properties",
+                            strerror(errno));
        }
 
        return ret;
index 2453d60d446132ac05addfa7604216d1bdbc9487..50dfd2e1cb1ebdfcf3f7ceea5eb370451a08fba6 100644 (file)
@@ -215,4 +215,6 @@ extern int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata,
  */
 extern void lxc_console_sigwinch_fini(struct lxc_tty_state *ts);
 
+extern int lxc_console_write_ringbuffer(struct lxc_console *console);
+
 #endif
index efb1bf7ae8122ba5dccb29b0d86fa31af367072c..7c5b504b60f8faf84944f2b4422060776029b10e 100644 (file)
@@ -516,6 +516,29 @@ 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("The console log is empty");
+               else if (ret == -EFAULT)
+                       NOTICE("The container does not keep a console log");
+               else if (ret == -ENOENT)
+                       NOTICE("The container does not keep a console log file");
+               else if (ret == -EIO)
+                       NOTICE("Failed to write console log to log file");
+               else
+                       ERROR("Failed to retrieve console log");
+       }
+
+       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 +4630,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..9ea67df7f1d83b665aa67cad7a017c1a0e2328fa 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,34 @@ 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;
+
+       /* If a console log file was specified this flag indicates whether the
+        * contents of the ringbuffer should be written to the logfile when a
+        * request is sent to the ringbuffer.
+        */
+       bool write_logfile;
+};
+
 /*!
  * \brief Create a new container.
  *
index ca80136d53391b7e13bc6c6299f1ea53b832ed3e..41035a11bde179cd34633a6789b723d35b4c916c 100644 (file)
@@ -904,7 +904,7 @@ static int do_start(void *data)
        }
 
        /* Setup the container, ip, names, utsname, ... */
-       ret = lxc_setup_child(handler);
+       ret = lxc_setup(handler);
        close(handler->data_sock[0]);
        close(handler->data_sock[1]);
        if (ret < 0) {
@@ -1266,10 +1266,6 @@ static int lxc_spawn(struct lxc_handler *handler)
                flags &= ~CLONE_NEWNET;
        }
 
-       ret = lxc_setup_parent(handler);
-       if (ret < 0)
-               goto out_delete_net;
-
        if (fork_before_clone)
                handler->pid = lxc_fork_attach_clone(do_start, handler, flags | CLONE_PARENT);
        else
index 1fd58b2cb1ca2955afcad5c966bfb7152bee4cfb..c9a2dd27592d8c89c01a28caa3ca2a804249192a 100644 (file)
@@ -15,6 +15,7 @@ lxc_test_lxcpath_SOURCES = lxcpath.c
 lxc_test_cgpath_SOURCES = cgpath.c
 lxc_test_clonetest_SOURCES = clonetest.c
 lxc_test_console_SOURCES = console.c
+lxc_test_console_log_SOURCES = console_log.c lxctest.h
 lxc_test_snapshot_SOURCES = snapshot.c
 lxc_test_concurrent_SOURCES = concurrent.c
 lxc_test_may_control_SOURCES = may_control.c
@@ -52,7 +53,7 @@ endif
 bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
        lxc-test-destroytest lxc-test-saveconfig lxc-test-createtest \
        lxc-test-shutdowntest lxc-test-get_item lxc-test-getkeys lxc-test-lxcpath \
-       lxc-test-cgpath lxc-test-clonetest lxc-test-console \
+       lxc-test-cgpath lxc-test-clonetest lxc-test-console lxc-test-console-log \
        lxc-test-snapshot lxc-test-concurrent lxc-test-may-control \
        lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \
        lxc-test-apparmor lxc-test-utils lxc-test-parse-config-file \
@@ -85,6 +86,7 @@ EXTRA_DIST = \
        concurrent.c \
        config_jump_table.c \
        console.c \
+       console_log.c \
        containertests.c \
        createtest.c \
        destroytest.c \
diff --git a/src/tests/console_log.c b/src/tests/console_log.c
new file mode 100644 (file)
index 0000000..83a7229
--- /dev/null
@@ -0,0 +1,217 @@
+/* liblxcapi
+ *
+ * Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define __STDC_FORMAT_MACROS
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "lxctest.h"
+#include "utils.h"
+
+int main(int argc, char *argv[])
+{
+       int logfd, ret;
+       char buf[4096 + 1];
+       ssize_t bytes;
+       struct stat st;
+       struct lxc_container *c;
+       struct lxc_console_log log;
+       bool do_unlink = false;
+       int fret = EXIT_FAILURE;
+
+       c = lxc_container_new("console-log", NULL);
+       if (!c) {
+               lxc_error("%s", "Failed to create container \"console-log\"");
+               exit(fret);
+       }
+
+       if (c->is_defined(c)) {
+               lxc_error("%s\n", "Container \"console-log\" is defined");
+               goto on_error_put;
+       }
+
+       /* Set console ringbuffer size. */
+       if (!c->set_config_item(c, "lxc.console.logsize", "4096")) {
+               lxc_error("%s\n", "Failed to set config item \"lxc.console.logsize\"");
+               goto on_error_put;
+       }
+
+       /* Set on-disk logfile. */
+       if (!c->set_config_item(c, "lxc.console.logfile", "/tmp/console-log.log")) {
+               lxc_error("%s\n", "Failed to set config item \"lxc.console.logfile\"");
+               goto on_error_put;
+       }
+
+       if (!c->createl(c, "busybox", NULL, NULL, 0, NULL)) {
+               lxc_error("%s\n", "Failed to create busybox container \"console-log\"");
+               goto on_error_put;
+       }
+
+       if (!c->is_defined(c)) {
+               lxc_error("%s\n", "Container \"console-log\" is not defined");
+               goto on_error_put;
+       }
+
+       c->clear_config(c);
+
+       if (!c->load_config(c, NULL)) {
+               lxc_error("%s\n", "Failed to load config for container \"console-log\"");
+               goto on_error_stop;
+       }
+
+       if (!c->want_daemonize(c, true)) {
+               lxc_error("%s\n", "Failed to mark container \"console-log\" daemonized");
+               goto on_error_stop;
+       }
+
+       if (!c->startl(c, 0, NULL)) {
+               lxc_error("%s\n", "Failed to start container \"console-log\" daemonized");
+               goto on_error_stop;
+       }
+
+       /* Leave some time for the container to write something to the log. */
+       sleep(2);
+
+       /* Retrieve the contents of the ringbuffer. */
+       log.clear = false;
+       log.read_max = &(uint64_t){0};
+       log.read = true;
+       log.write_logfile = false;
+
+       ret = c->console_log(c, &log);
+       if (ret < 0) {
+               lxc_error("%s - Failed to retrieve console log \n", strerror(-ret));
+               goto on_error_stop;
+       } else {
+               lxc_debug("Retrieved %" PRIu64
+                         " bytes from console log. Contents are \"%s\"\n",
+                         *log.read_max, log.data);
+       }
+
+       /* Leave another two seconds to ensure boot is finished. */
+       sleep(2);
+
+       /* Clear the console ringbuffer. */
+       log.read_max = &(uint64_t){0};
+       log.read = false;
+       log.write_logfile = false;
+       log.clear = true;
+       ret = c->console_log(c, &log);
+       if (ret < 0) {
+               if (ret != -ENODATA) {
+                       lxc_error("%s - Failed to retrieve console log\n", strerror(-ret));
+                       goto on_error_stop;
+               }
+       }
+
+       if (!c->stop(c)) {
+               lxc_error("%s\n", "Failed to stop container \"console-log\"");
+               goto on_error_stop;
+       }
+
+       if (!c->startl(c, 0, NULL)) {
+               lxc_error("%s\n", "Failed to start container \"console-log\" daemonized");
+               goto on_error_destroy;
+       }
+       do_unlink = true;
+
+       /* Leave some time for the container to write something to the log. */
+       sleep(2);
+
+       log.read_max = &(uint64_t){0};
+       log.read = true;
+       log.write_logfile = true;
+       log.clear = false;
+       ret = c->console_log(c, &log);
+       if (ret < 0) {
+               lxc_error("%s - Failed to retrieve console log \n", strerror(-ret));
+               goto on_error_stop;
+       } else {
+               lxc_debug("Retrieved %" PRIu64
+                         " bytes from console log. Contents are \"%s\"\n",
+                         *log.read_max, log.data);
+       }
+
+       logfd = open("/tmp/console-log.log", O_RDONLY);
+       if (logfd < 0) {
+               lxc_error("%s - Failed to open console log file "
+                         "\"/tmp/console-log.log\"\n", strerror(errno));
+               goto on_error_stop;
+       }
+
+       bytes = lxc_read_nointr(logfd, buf, 4096 + 1);
+       close(logfd);
+       if (bytes < 0 || ((uint64_t)bytes != *log.read_max)) {
+               lxc_error("%s - Failed to read console log file "
+                         "\"/tmp/console-log.log\"\n", strerror(errno));
+               goto on_error_stop;
+       }
+
+       ret = stat("/tmp/console-log.log", &st);
+       if (ret < 0) {
+               lxc_error("%s - Failed to stat on-disk logfile\n", strerror(errno));
+               goto on_error_stop;
+       }
+
+       if ((uint64_t)st.st_size != *log.read_max) {
+               lxc_error("On-disk logfile size and used ringbuffer size do "
+                         "not match: %" PRIu64 " != %" PRIu64 "\n",
+                         (uint64_t)st.st_size, *log.read_max);
+               goto on_error_stop;
+       }
+
+       if (memcmp(log.data, buf, *log.read_max)) {
+               lxc_error("%s - Contents of in-memory ringbuffer and on-disk "
+                         "logfile do not match\n", strerror(errno));
+               goto on_error_stop;
+       } else {
+               lxc_debug("Retrieved %" PRIu64 " bytes from console log and "
+                         "console log file. Contents are: \"%s\" - \"%s\"\n",
+                         *log.read_max, log.data, buf);
+       }
+
+       fret = 0;
+
+on_error_stop:
+       if (c->is_running(c) && !c->stop(c))
+               lxc_error("%s\n", "Failed to stop container \"console-log\"");
+
+on_error_destroy:
+       if (!c->destroy(c))
+               lxc_error("%s\n", "Failed to destroy container \"console-log\"");
+
+on_error_put:
+       lxc_container_put(c);
+       if (do_unlink) {
+               ret = unlink("/tmp/console-log.log");
+               if (ret < 0)
+                       lxc_error("%s - Failed to remove container log file\n", strerror(errno));
+       }
+       exit(fret);
+}