[lxc-devel] [lxc/master] console: add lxc.console.buffer.size, lxc.console.buffer.logfile, lxc.console.rotate

brauner on Github lxc-bot at linuxcontainers.org
Wed Nov 15 15:55:47 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20171115/f2b7f671/attachment.bin>
-------------- next part --------------
From 029a316388a6b93783c7118d8b41bfd0864ad04c Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 14 Nov 2017 23:55:33 +0100
Subject: [PATCH 1/8] confile: add lxc.console.buffer.size

Determines the size of the ringbuffer.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 doc/lxc.container.conf.sgml.in | 25 +++++++++++++++++++++++++
 src/lxc/commands.c             |  4 ++--
 src/lxc/conf.c                 |  4 ++--
 src/lxc/conf.h                 |  2 +-
 src/lxc/confile.c              | 35 ++++++++++++++++++-----------------
 src/lxc/console.c              |  6 +++---
 6 files changed, 51 insertions(+), 25 deletions(-)

diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
index 059942bc9..2ed83ca78 100644
--- a/doc/lxc.container.conf.sgml.in
+++ b/doc/lxc.container.conf.sgml.in
@@ -682,6 +682,31 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
         where the output of this console goes.
       </para>
       <variablelist>
+
+        <varlistentry>
+          <term>
+            <option>lxc.console.buffer.size</option>
+          </term>
+          <listitem>
+            <para>
+            Setting this option instructs liblxc to allocate an in-memory
+            ringbuffer. The container's console output will be written to the
+            ringbuffer. Note that ringbuffer must be at least as big as a
+            standard page size. When passed a value smaller than a single page
+            size liblxc will allocate a ringbuffer of a single page size. A page
+            size is usually 4kB.
+
+            The keyword 'auto' will cause liblxc to allocate a ringbuffer of
+            128kB.
+
+            When manually specifying a size for the ringbuffer the value should
+            be a power of 2 when converted to bytes. Valid size prefixes are
+            'kB', 'MB', 'GB'. (Note that all conversions are based on multiples
+            of 1024. That means 'kb' == 'KiB', 'MB' == 'MiB', 'GB' == 'GiB'.)
+            </para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term>
             <option>lxc.console.logfile</option>
diff --git a/src/lxc/commands.c b/src/lxc/commands.c
index 880cb28c2..653fd994f 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -1026,7 +1026,7 @@ 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;
+	uint64_t buffer_size = handler->conf->console.buffer_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;
@@ -1034,7 +1034,7 @@ static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req,
 	rsp.ret = -EFAULT;
 	rsp.datalen = 0;
 	rsp.data = NULL;
-	if (logsize <= 0)
+	if (buffer_size <= 0)
 		goto out;
 
 	rsp.datalen = lxc_ringbuf_used(buf);
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 2e9077c1b..a2b3673a7 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2432,7 +2432,7 @@ struct lxc_conf *lxc_conf_init(void)
 	new->autodev = 1;
 	new->console.log_path = NULL;
 	new->console.log_fd = -1;
-	new->console.log_size = 0;
+	new->console.buffer_size = 0;
 	new->console.path = NULL;
 	new->console.peer = -1;
 	new->console.peerpty.busy = -1;
@@ -3463,7 +3463,7 @@ void lxc_conf_free(struct lxc_conf *conf)
 		current_config = NULL;
 	free(conf->console.log_path);
 	free(conf->console.path);
-	if (conf->console.log_size > 0 && conf->console.ringbuf.addr)
+	if (conf->console.buffer_size > 0 && conf->console.ringbuf.addr)
 		lxc_ringbuf_release(&conf->console.ringbuf);
 	free(conf->rootfs.mount);
 	free(conf->rootfs.bdev_type);
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 43eeb4ded..1dc892430 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -153,7 +153,7 @@ struct lxc_console {
 	char name[MAXPATHLEN];
 	struct termios *tios;
 	struct lxc_tty_state *tty_state;
-	uint64_t log_size;
+	uint64_t buffer_size;
 	struct lxc_ringbuf ringbuf;
 };
 
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index c4d4c2394..852589531 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -83,7 +83,7 @@ lxc_config_define(cap_keep);
 lxc_config_define(cgroup_controller);
 lxc_config_define(cgroup_dir);
 lxc_config_define(console_logfile);
-lxc_config_define(console_logsize);
+lxc_config_define(console_buffer_size);
 lxc_config_define(console_path);
 lxc_config_define(environment);
 lxc_config_define(ephemeral);
@@ -149,8 +149,8 @@ static struct lxc_config_t config[] = {
 	{ "lxc.cap.keep",                  false,                  set_config_cap_keep,                    get_config_cap_keep,                    clr_config_cap_keep,                  },
 	{ "lxc.cgroup.dir",                false,                  set_config_cgroup_dir,                  get_config_cgroup_dir,                  clr_config_cgroup_dir,                },
 	{ "lxc.cgroup",                    false,                  set_config_cgroup_controller,           get_config_cgroup_controller,           clr_config_cgroup_controller,         },
+	{ "lxc.console.buffer.size",       false,                  set_config_console_buffer_size,         get_config_console_buffer_size,         clr_config_console_buffer_size,       },
 	{ "lxc.console.logfile",           false,                  set_config_console_logfile,             get_config_console_logfile,             clr_config_console_logfile,           },
-	{ "lxc.console.logsize",           false,                  set_config_console_logsize,             get_config_console_logsize,             clr_config_console_logsize,           },
 	{ "lxc.console.path",              false,                  set_config_console_path,                get_config_console_path,                clr_config_console_path,              },
 	{ "lxc.environment",               false,                  set_config_environment,                 get_config_environment,                 clr_config_environment,               },
 	{ "lxc.ephemeral",                 false,                  set_config_ephemeral,                   get_config_ephemeral,                   clr_config_ephemeral,                 },
@@ -1794,21 +1794,21 @@ static int set_config_console_logfile(const char *key, const char *value,
 	return set_config_path_item(&lxc_conf->console.log_path, value);
 }
 
-static int set_config_console_logsize(const char *key, const char *value,
-				      struct lxc_conf *lxc_conf, void *data)
+static int set_config_console_buffer_size(const char *key, const char *value,
+					  struct lxc_conf *lxc_conf, void *data)
 {
 	int ret;
 	int64_t size;
-	uint64_t logsize, pgsz;
+	uint64_t buffer_size, pgsz;
 
 	if (lxc_config_value_empty(value)) {
-		lxc_conf->console.log_size = 0;
+		lxc_conf->console.buffer_size = 0;
 		return 0;
 	}
 
 	/* If the user specified "auto" the default log size is 2^17 = 128 Kib */
 	if (!strcmp(value, "auto")) {
-		lxc_conf->console.log_size = 1 << 17;
+		lxc_conf->console.buffer_size = 1 << 17;
 		return 0;
 	}
 
@@ -1829,15 +1829,15 @@ static int set_config_console_logsize(const char *key, const char *value,
 		size = pgsz;
 	}
 
-	logsize = lxc_find_next_power2((uint64_t)size);
-	if (logsize == 0)
+	buffer_size = lxc_find_next_power2((uint64_t)size);
+	if (buffer_size == 0)
 		return -EINVAL;
 
-	if (logsize != size)
+	if (buffer_size != size)
 		NOTICE("Passed size was not a power of 2. Rounding log size to "
-		       "next power of two: %" PRIu64 " bytes", logsize);
+		       "next power of two: %" PRIu64 " bytes", buffer_size);
 
-	lxc_conf->console.log_size = logsize;
+	lxc_conf->console.buffer_size = buffer_size;
 	return 0;
 }
 
@@ -3091,8 +3091,9 @@ static int get_config_console_logfile(const char *key, char *retv, int inlen,
 	return lxc_get_conf_str(retv, inlen, c->console.log_path);
 }
 
-static int get_config_console_logsize(const char *key, char *retv, int inlen,
-				      struct lxc_conf *c, void *data)
+static int get_config_console_buffer_size(const char *key, char *retv,
+					  int inlen, struct lxc_conf *c,
+					  void *data)
 {
 	return lxc_get_conf_uint64(c, retv, inlen, c->autodev);
 }
@@ -3501,10 +3502,10 @@ static inline int clr_config_console_logfile(const char *key,
 	return 0;
 }
 
-static inline int clr_config_console_logsize(const char *key,
-					     struct lxc_conf *c, void *data)
+static inline int clr_config_console_buffer_size(const char *key,
+						 struct lxc_conf *c, void *data)
 {
-	c->console.log_size = 0;
+	c->console.buffer_size = 0;
 	return 0;
 }
 
diff --git a/src/lxc/console.c b/src/lxc/console.c
index e152005f4..c16e39bb6 100644
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -238,7 +238,7 @@ static int lxc_console_cb_con(int fd, uint32_t events, void *data,
 			w = lxc_write_nointr(console->peer, buf, r);
 
 		/* write to console ringbuffer */
-		if (console->log_size > 0)
+		if (console->buffer_size > 0)
 			w_rbuf = lxc_ringbuf_write(&console->ringbuf, buf, r);
 
 		/* write to console log */
@@ -617,7 +617,7 @@ static int lxc_setup_console_ringbuf(struct lxc_console *console)
 {
 	int ret;
 	struct lxc_ringbuf *buf = &console->ringbuf;
-	uint64_t size = console->log_size;
+	uint64_t size = console->buffer_size;
 
 	/* no ringbuffer previously allocated and no ringbuffer requested */
 	if (!buf->addr && size <= 0)
@@ -698,7 +698,7 @@ int lxc_console_create(struct lxc_conf *conf)
 		goto err;
 	}
 
-	if (console->log_path && console->log_size <= 0) {
+	if (console->log_path && console->buffer_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);

From fc46e6682d05375c3233eec97f0150248f576916 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 15 Nov 2017 00:23:07 +0100
Subject: [PATCH 2/8] confile: add lxc.console.buffer.logfile

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 doc/lxc.container.conf.sgml.in | 20 ++++++++++++++++++++
 src/lxc/commands.c             |  2 +-
 src/lxc/conf.c                 |  5 ++++-
 src/lxc/conf.h                 | 10 ++++++++++
 src/lxc/confile.c              | 27 ++++++++++++++++++++++++++-
 src/lxc/console.c              | 10 +++++-----
 6 files changed, 66 insertions(+), 8 deletions(-)

diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
index 2ed83ca78..ac0143ba2 100644
--- a/doc/lxc.container.conf.sgml.in
+++ b/doc/lxc.container.conf.sgml.in
@@ -709,6 +709,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
         <varlistentry>
           <term>
+            <option>lxc.console.buffer.logfile</option>
+          </term>
+          <listitem>
+            <para>
+            Setting this option instructs liblxc to write the in-memory
+            ringbuffer to disk. For performance reasons liblxc will only write
+            the in-memory ringbuffer to disk when an appropriate API request is
+            sent. Note that the this option is only used by liblxc when
+            <option>lxc.console.buffer.size</option> is set.
+
+            By default liblxc will dump the contents of the in-memory ringbuffer
+            to disk when the container terminates. This allows users to diagnose
+            boot failures when the container crashed before an API request to
+            retrieve the in-memory ringbuffer could be sent or handled.
+            </para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>
             <option>lxc.console.logfile</option>
           </term>
           <listitem>
diff --git a/src/lxc/commands.c b/src/lxc/commands.c
index 653fd994f..c158672ed 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -1051,7 +1051,7 @@ static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req,
 
 	if (log->write_logfile && rsp.datalen > 0) {
 		rsp.ret = -ENOENT;
-		if (!console->log_path)
+		if (!console->buffer_log_file)
 			goto out;
 
 		rsp.ret = lxc_console_write_ringbuffer(console);
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index a2b3673a7..4681a7274 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2430,9 +2430,11 @@ struct lxc_conf *lxc_conf_init(void)
 	new->loglevel = LXC_LOG_LEVEL_NOTSET;
 	new->personality = -1;
 	new->autodev = 1;
+	new->console.buffer_log_file = NULL;
+	new->console.buffer_log_file_fd = -1;
+	new->console.buffer_size = 0;
 	new->console.log_path = NULL;
 	new->console.log_fd = -1;
-	new->console.buffer_size = 0;
 	new->console.path = NULL;
 	new->console.peer = -1;
 	new->console.peerpty.busy = -1;
@@ -3461,6 +3463,7 @@ void lxc_conf_free(struct lxc_conf *conf)
 		return;
 	if (current_config == conf)
 		current_config = NULL;
+	free(conf->console.buffer_log_file);
 	free(conf->console.log_path);
 	free(conf->console.path);
 	if (conf->console.buffer_size > 0 && conf->console.ringbuf.addr)
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 1dc892430..b07c92e74 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -153,7 +153,17 @@ struct lxc_console {
 	char name[MAXPATHLEN];
 	struct termios *tios;
 	struct lxc_tty_state *tty_state;
+
+	/* size of the ringbuffer */
 	uint64_t buffer_size;
+
+	/* path to the log file for the ringbuffer */
+	char *buffer_log_file;
+
+	/* fd to the log file for the ringbuffer */
+	int buffer_log_file_fd;
+
+	/* the in-memory ringbuffer */
 	struct lxc_ringbuf ringbuf;
 };
 
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 852589531..977bea2ed 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -83,6 +83,7 @@ lxc_config_define(cap_keep);
 lxc_config_define(cgroup_controller);
 lxc_config_define(cgroup_dir);
 lxc_config_define(console_logfile);
+lxc_config_define(console_buffer_logfile);
 lxc_config_define(console_buffer_size);
 lxc_config_define(console_path);
 lxc_config_define(environment);
@@ -149,12 +150,13 @@ static struct lxc_config_t config[] = {
 	{ "lxc.cap.keep",                  false,                  set_config_cap_keep,                    get_config_cap_keep,                    clr_config_cap_keep,                  },
 	{ "lxc.cgroup.dir",                false,                  set_config_cgroup_dir,                  get_config_cgroup_dir,                  clr_config_cgroup_dir,                },
 	{ "lxc.cgroup",                    false,                  set_config_cgroup_controller,           get_config_cgroup_controller,           clr_config_cgroup_controller,         },
+	{ "lxc.console.buffer.logfile",    false,                  set_config_console_buffer_logfile,      get_config_console_buffer_logfile,      clr_config_console_buffer_logfile,    },
 	{ "lxc.console.buffer.size",       false,                  set_config_console_buffer_size,         get_config_console_buffer_size,         clr_config_console_buffer_size,       },
 	{ "lxc.console.logfile",           false,                  set_config_console_logfile,             get_config_console_logfile,             clr_config_console_logfile,           },
 	{ "lxc.console.path",              false,                  set_config_console_path,                get_config_console_path,                clr_config_console_path,              },
 	{ "lxc.environment",               false,                  set_config_environment,                 get_config_environment,                 clr_config_environment,               },
 	{ "lxc.ephemeral",                 false,                  set_config_ephemeral,                   get_config_ephemeral,                   clr_config_ephemeral,                 },
-	{ "lxc.execute.cmd",               false,                  set_config_execute_cmd,                 get_config_execute_cmd,                 clr_config_execute_cmd,                  },
+	{ "lxc.execute.cmd",               false,                  set_config_execute_cmd,                 get_config_execute_cmd,                 clr_config_execute_cmd,               },
 	{ "lxc.group",                     false,                  set_config_group,                       get_config_group,                       clr_config_group,                     },
 	{ "lxc.hook.autodev",              false,                  set_config_hooks,                       get_config_hooks,                       clr_config_hooks,                     },
 	{ "lxc.hook.clone",                false,                  set_config_hooks,                       get_config_hooks,                       clr_config_hooks,                     },
@@ -1841,6 +1843,13 @@ static int set_config_console_buffer_size(const char *key, const char *value,
 	return 0;
 }
 
+static int set_config_console_buffer_logfile(const char *key, const char *value,
+					     struct lxc_conf *lxc_conf,
+					     void *data)
+{
+	return set_config_path_item(&lxc_conf->console.buffer_log_file, value);
+}
+
 int append_unexp_config_line(const char *line, struct lxc_conf *conf)
 {
 	size_t len = conf->unexpanded_len, linelen = strlen(line);
@@ -3098,6 +3107,13 @@ static int get_config_console_buffer_size(const char *key, char *retv,
 	return lxc_get_conf_uint64(c, retv, inlen, c->autodev);
 }
 
+static int get_config_console_buffer_logfile(const char *key, char *retv,
+					     int inlen, struct lxc_conf *c,
+					     void *data)
+{
+	return lxc_get_conf_str(retv, inlen, c->console.buffer_log_file);
+}
+
 static int get_config_seccomp_profile(const char *key, char *retv, int inlen,
 				      struct lxc_conf *c, void *data)
 {
@@ -3509,6 +3525,15 @@ static inline int clr_config_console_buffer_size(const char *key,
 	return 0;
 }
 
+static inline int clr_config_console_buffer_logfile(const char *key,
+						    struct lxc_conf *c,
+						    void *data)
+{
+	free(c->console.buffer_log_file);
+	c->console.buffer_log_file = NULL;
+	return 0;
+}
+
 static inline int clr_config_seccomp_profile(const char *key,
 					     struct lxc_conf *c, void *data)
 {
diff --git a/src/lxc/console.c b/src/lxc/console.c
index c16e39bb6..d83b3830e 100644
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -558,19 +558,19 @@ int lxc_console_write_ringbuffer(struct lxc_console *console)
 	uint64_t used;
 	struct lxc_ringbuf *buf = &console->ringbuf;
 
-	if (!console->log_path)
+	if (!console->buffer_log_file)
 		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));
+	fd = lxc_unpriv(open(console->buffer_log_file, O_CLOEXEC | O_RDWR | O_CREAT | O_TRUNC, 0600));
 	if (fd < 0) {
-		SYSERROR("Failed to open console log file \"%s\"", console->log_path);
+		SYSERROR("Failed to open console log file \"%s\"", console->buffer_log_file);
 		return -EIO;
 	}
-	DEBUG("Using \"%s\" as console log file", console->log_path);
+	DEBUG("Using \"%s\" as console log file", console->buffer_log_file);
 
 	r_addr = lxc_ringbuf_get_read_addr(buf);
 	ret = lxc_write_nointr(fd, r_addr, used);
@@ -698,7 +698,7 @@ int lxc_console_create(struct lxc_conf *conf)
 		goto err;
 	}
 
-	if (console->log_path && console->buffer_size <= 0) {
+	if (console->log_path) {
 		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);

From ff08927084dc72b4db69d90fbc9fdca0bb3c4735 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 15 Nov 2017 00:35:02 +0100
Subject: [PATCH 3/8] confile: add lxc.console.rotate

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 doc/lxc.container.conf.sgml.in | 31 +++++++++++++++++++++++++++++--
 src/lxc/conf.h                 |  1 +
 src/lxc/confile.c              | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
index ac0143ba2..b432cc582 100644
--- a/doc/lxc.container.conf.sgml.in
+++ b/doc/lxc.container.conf.sgml.in
@@ -733,11 +733,38 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
           </term>
           <listitem>
             <para>
-              Specify a path to a file where the console output will
-              be written.
+              Specify a path to a file where the console output will be written.
+              Note that in contrast to the on-disk ringbuffer logfile this file
+              will keep growing potentially filling up the users disks if not
+              rotated and deleted. This problem can also be avoided by using the
+              in-memory ringbuffer options
+              <option>lxc.console.buffer.size</option> and
+              <option>lxc.console.buffer.logfile</option>.
             </para>
           </listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term>
+            <option>lxc.console.rotate</option>
+          </term>
+          <listitem>
+            <para>
+              Whether to rotate the console logfile specified
+              in <option>lxc.console.logfile</option>. Users can send an
+              appropriate API request to rotate the logfile. Note that the old
+              logfile will have the same name as the original with the suffix
+              ".old" appended.
+              Users wishing to prevent the console log file from filling the
+              disk should rotate the logfile via an appropriate API request.
+              rotated and deleted. This problem can also be avoided by using the
+              in-memory ringbuffer options
+              <option>lxc.console.buffer.size</option> and
+              <option>lxc.console.buffer.logfile</option>.
+            </para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term>
             <option>lxc.console.path</option>
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index b07c92e74..58302cf30 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -150,6 +150,7 @@ struct lxc_console {
 	char *path;
 	char *log_path;
 	int log_fd;
+	unsigned int log_rotate;
 	char name[MAXPATHLEN];
 	struct termios *tios;
 	struct lxc_tty_state *tty_state;
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 977bea2ed..24e74adff 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -83,6 +83,7 @@ lxc_config_define(cap_keep);
 lxc_config_define(cgroup_controller);
 lxc_config_define(cgroup_dir);
 lxc_config_define(console_logfile);
+lxc_config_define(console_rotate);
 lxc_config_define(console_buffer_logfile);
 lxc_config_define(console_buffer_size);
 lxc_config_define(console_path);
@@ -154,6 +155,7 @@ static struct lxc_config_t config[] = {
 	{ "lxc.console.buffer.size",       false,                  set_config_console_buffer_size,         get_config_console_buffer_size,         clr_config_console_buffer_size,       },
 	{ "lxc.console.logfile",           false,                  set_config_console_logfile,             get_config_console_logfile,             clr_config_console_logfile,           },
 	{ "lxc.console.path",              false,                  set_config_console_path,                get_config_console_path,                clr_config_console_path,              },
+	{ "lxc.console.rotate",            false,                  set_config_console_rotate,              get_config_console_rotate,              clr_config_console_rotate,            },
 	{ "lxc.environment",               false,                  set_config_environment,                 get_config_environment,                 clr_config_environment,               },
 	{ "lxc.ephemeral",                 false,                  set_config_ephemeral,                   get_config_ephemeral,                   clr_config_ephemeral,                 },
 	{ "lxc.execute.cmd",               false,                  set_config_execute_cmd,                 get_config_execute_cmd,                 clr_config_execute_cmd,               },
@@ -1790,6 +1792,23 @@ static int set_config_console_path(const char *key, const char *value,
 	return set_config_path_item(&lxc_conf->console.path, value);
 }
 
+static int set_config_console_rotate(const char *key, const char *value,
+				     struct lxc_conf *lxc_conf, void *data)
+{
+	if (lxc_config_value_empty(value)) {
+		lxc_conf->console.log_rotate = 0;
+		return 0;
+	}
+
+	if (lxc_safe_uint(value, &lxc_conf->console.log_rotate) < 0)
+		return -1;
+
+	if (lxc_conf->console.log_rotate > 1)
+		return -1;
+
+	return 0;
+}
+
 static int set_config_console_logfile(const char *key, const char *value,
 				      struct lxc_conf *lxc_conf, void *data)
 {
@@ -3100,6 +3119,13 @@ static int get_config_console_logfile(const char *key, char *retv, int inlen,
 	return lxc_get_conf_str(retv, inlen, c->console.log_path);
 }
 
+static int get_config_console_rotate(const char *key, char *retv, int inlen,
+				     struct lxc_conf *c, void *data)
+{
+	return lxc_get_conf_int(c, retv, inlen, c->console.log_rotate);
+}
+
+
 static int get_config_console_buffer_size(const char *key, char *retv,
 					  int inlen, struct lxc_conf *c,
 					  void *data)
@@ -3518,6 +3544,13 @@ static inline int clr_config_console_logfile(const char *key,
 	return 0;
 }
 
+static inline int clr_config_console_rotate(const char *key, struct lxc_conf *c,
+					    void *data)
+{
+	c->console.log_rotate = 0;
+	return 0;
+}
+
 static inline int clr_config_console_buffer_size(const char *key,
 						 struct lxc_conf *c, void *data)
 {

From 927b392ec01382858b463429afa54998f9c8f9cd Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 15 Nov 2017 16:04:12 +0100
Subject: [PATCH 4/8] commands: only set ringbuffer datalen when needed

If we do it unconditionally a request to only clear the ringbuffer and not read
or write anything will fail.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/commands.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/lxc/commands.c b/src/lxc/commands.c
index c158672ed..759346b8b 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -1037,7 +1037,9 @@ static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req,
 	if (buffer_size <= 0)
 		goto out;
 
-	rsp.datalen = lxc_ringbuf_used(buf);
+	if (log->read || log->write_logfile)
+		rsp.datalen = lxc_ringbuf_used(buf);
+
 	if (log->read)
 		rsp.data = lxc_ringbuf_get_read_addr(buf);
 

From a68f827040e28631fb844a034c38f9777ff86b26 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 15 Nov 2017 12:18:51 +0100
Subject: [PATCH 5/8] console: split console setup into logical parts

The lxc_console_create() function used to munge the ringbuffer setup and the
log file setup already. This made somewhat sense when we didn't have a separate
ringbuffer log file. Now it's just plain confusing. So split this into logical
helpers that future maintainers can understand:

- lxc_console_create_log_file()
- lxc_console_create_ringbuf(console);
- lxc_console_create_ringbuf_log_file(console);

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/console.c | 76 ++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 56 insertions(+), 20 deletions(-)

diff --git a/src/lxc/console.c b/src/lxc/console.c
index d83b3830e..14fe40240 100644
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -552,7 +552,6 @@ static int lxc_console_peer_default(struct lxc_console *console)
 
 int lxc_console_write_ringbuffer(struct lxc_console *console)
 {
-	int fd;
 	char *r_addr;
 	ssize_t ret;
 	uint64_t used;
@@ -565,16 +564,8 @@ int lxc_console_write_ringbuffer(struct lxc_console *console)
 	if (used == 0)
 		return 0;
 
-	fd = lxc_unpriv(open(console->buffer_log_file, O_CLOEXEC | O_RDWR | O_CREAT | O_TRUNC, 0600));
-	if (fd < 0) {
-		SYSERROR("Failed to open console log file \"%s\"", console->buffer_log_file);
-		return -EIO;
-	}
-	DEBUG("Using \"%s\" as console log file", console->buffer_log_file);
-
 	r_addr = lxc_ringbuf_get_read_addr(buf);
-	ret = lxc_write_nointr(fd, r_addr, used);
-	close(fd);
+	ret = lxc_write_nointr(console->buffer_log_file_fd, r_addr, used);
 	if (ret < 0)
 		return -EIO;
 
@@ -606,6 +597,30 @@ void lxc_console_delete(struct lxc_console *console)
 	console->master = -1;
 	console->slave = -1;
 	console->log_fd = -1;
+	if (console->buffer_log_file_fd >= 0)
+		close(console->buffer_log_file_fd);
+	console->buffer_log_file_fd = -1;
+}
+
+/* This is the console ringbuffer log file. Please note that the console
+ * ringbuffer log file is (implementation wise not content wise) independent of
+ * the console log file.
+ */
+static int lxc_console_create_ringbuf_log_file(struct lxc_console *console)
+{
+	if (!console->buffer_log_file)
+		return 0;
+
+	console->buffer_log_file_fd = lxc_unpriv(open(console->buffer_log_file,
+			    O_CLOEXEC | O_RDWR | O_CREAT | O_TRUNC, 0600));
+	if (console->buffer_log_file_fd < 0) {
+		SYSERROR("Failed to open console ringbuffer log file \"%s\"",
+			 console->buffer_log_file);
+		return -EIO;
+	}
+
+	DEBUG("Using \"%s\" as console ringbuffer log file", console->buffer_log_file);
+	return 0;
 }
 
 /**
@@ -613,7 +628,7 @@ void lxc_console_delete(struct lxc_console *console)
  * 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)
+static int lxc_console_create_ringbuf(struct lxc_console *console)
 {
 	int ret;
 	struct lxc_ringbuf *buf = &console->ringbuf;
@@ -655,6 +670,25 @@ static int lxc_setup_console_ringbuf(struct lxc_console *console)
 	return 0;
 }
 
+/**
+ * This is the console log file. Please note that the console log file is
+ * (implementation wise not content wise) independent of the console ringbuffer.
+ */
+static int lxc_console_create_log_file(struct lxc_console *console)
+{
+	if (!console->log_path)
+		return 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);
+		return -1;
+	}
+
+	DEBUG("Using \"%s\" as console log file", console->log_path);
+	return 0;
+}
+
 int lxc_console_create(struct lxc_conf *conf)
 {
 	int ret, saved_errno;
@@ -698,16 +732,18 @@ int lxc_console_create(struct lxc_conf *conf)
 		goto err;
 	}
 
-	if (console->log_path) {
-		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);
-			goto err;
-		}
-		DEBUG("Using \"%s\" as console log file", console->log_path);
-	}
+	/* create console log file */
+	ret = lxc_console_create_log_file(console);
+	if (ret < 0)
+		goto err;
+
+	/* create console ringbuffer */
+	ret = lxc_console_create_ringbuf(console);
+	if (ret < 0)
+		goto err;
 
-	ret = lxc_setup_console_ringbuf(console);
+	/* create console ringbuffer log file */
+	ret = lxc_console_create_ringbuf_log_file(console);
 	if (ret < 0)
 		goto err;
 

From 282b9b9ac70f60f69bba5dd2db9bc44a0e9fc320 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 9 Nov 2017 19:39:59 +0100
Subject: [PATCH 6/8] commands: truncate console ringbuffer log file

When a "clear" request is sent to the console ringbuffer we should truncate the
console log file as well.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/commands.c | 28 +++++++++++++++++++++++++---
 1 file changed, 25 insertions(+), 3 deletions(-)

diff --git a/src/lxc/commands.c b/src/lxc/commands.c
index 759346b8b..133b6e6c1 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -23,6 +23,7 @@
 
 #include "config.h"
 
+#include <caps.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <malloc.h>
@@ -1062,11 +1063,32 @@ static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req,
 	}
 
 	rsp.ret = 0;
-
-	if (log->clear)
+	if (log->clear) {
+		/* clear the ringbuffer */
 		lxc_ringbuf_clear(buf);
-	else if (rsp.datalen > 0)
+
+		/* truncate the ringbuffer log file */
+		if (console->buffer_log_file) {
+			rsp.ret = -ENOENT;
+			if (!file_exists(console->buffer_log_file))
+				goto out;
+
+			/* be very certain things are kosher */
+			rsp.ret = -EBADF;
+			if (console->buffer_log_file_fd < 0)
+				goto out;
+
+			rsp.ret = lxc_unpriv(ftruncate(console->buffer_log_file_fd, 0));
+			if (rsp.ret < 0) {
+				ERROR("%s - Failed to truncate console "
+				      "ringbuffer log file \"%s\"",
+				      strerror(errno), console->buffer_log_file);
+				goto out;
+			}
+		}
+	} else if (rsp.datalen > 0) {
 		lxc_ringbuf_move_read_addr(buf, rsp.datalen);
+	}
 
 out:
 	return lxc_cmd_rsp_send(fd, &rsp);

From 65b12760d40b4ec5fe4652c56755905124b1c401 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 15 Nov 2017 13:29:49 +0100
Subject: [PATCH 7/8] commands: rotate console log file

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/commands.c | 29 +++++++++++++++++++++++++++++
 src/lxc/console.c  |  2 +-
 src/lxc/console.h  |  1 +
 3 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/src/lxc/commands.c b/src/lxc/commands.c
index 133b6e6c1..e75b1a9ab 100644
--- a/src/lxc/commands.c
+++ b/src/lxc/commands.c
@@ -1064,6 +1064,10 @@ static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req,
 
 	rsp.ret = 0;
 	if (log->clear) {
+		int ret;
+		size_t len;
+		char *tmp;
+
 		/* clear the ringbuffer */
 		lxc_ringbuf_clear(buf);
 
@@ -1086,6 +1090,31 @@ static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req,
 				goto out;
 			}
 		}
+
+		/* rotate the console log file */
+		if (!console->log_path || console->log_rotate == 0)
+			goto out;
+
+		/* be very certain things are kosher */
+		rsp.ret = -EBADF;
+		if (console->log_fd < 0)
+			goto out;
+
+		len = strlen(console->log_path) + sizeof(".old");
+		tmp = alloca(len);
+
+		rsp.ret = -EFBIG;
+		ret = snprintf(tmp, len, "%s.old", console->log_path);
+		if (ret < 0 || (size_t)ret >= len)
+			goto out;
+
+		close(console->log_fd);
+		console->log_fd = -1;
+		rsp.ret = lxc_unpriv(rename(console->log_path, tmp));
+		if (rsp.ret < 0)
+			goto out;
+
+		rsp.ret = lxc_console_create_log_file(console);
 	} else if (rsp.datalen > 0) {
 		lxc_ringbuf_move_read_addr(buf, rsp.datalen);
 	}
diff --git a/src/lxc/console.c b/src/lxc/console.c
index 14fe40240..c8775de6c 100644
--- a/src/lxc/console.c
+++ b/src/lxc/console.c
@@ -674,7 +674,7 @@ static int lxc_console_create_ringbuf(struct lxc_console *console)
  * This is the console log file. Please note that the console log file is
  * (implementation wise not content wise) independent of the console ringbuffer.
  */
-static int lxc_console_create_log_file(struct lxc_console *console)
+int lxc_console_create_log_file(struct lxc_console *console)
 {
 	if (!console->log_path)
 		return 0;
diff --git a/src/lxc/console.h b/src/lxc/console.h
index 389914ab5..60b299631 100644
--- a/src/lxc/console.h
+++ b/src/lxc/console.h
@@ -222,5 +222,6 @@ extern int lxc_console_cb_signal_fd(int fd, uint32_t events, void *cbdata,
 extern void lxc_console_signal_fini(struct lxc_tty_state *ts);
 
 extern int lxc_console_write_ringbuffer(struct lxc_console *console);
+extern int lxc_console_create_log_file(struct lxc_console *console);
 
 #endif

From 0b7d9f30ca5ecf036e426af18ee4fb2837dde60f Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 15 Nov 2017 16:05:49 +0100
Subject: [PATCH 8/8] test: expand console log tests

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/tests/console_log.c | 127 ++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 111 insertions(+), 16 deletions(-)

diff --git a/src/tests/console_log.c b/src/tests/console_log.c
index 83a722967..21aa303e4 100644
--- a/src/tests/console_log.c
+++ b/src/tests/console_log.c
@@ -39,7 +39,7 @@ int main(int argc, char *argv[])
 	int logfd, ret;
 	char buf[4096 + 1];
 	ssize_t bytes;
-	struct stat st;
+	struct stat st_buffer_log_file, st_log_file, st_log_file_old;
 	struct lxc_container *c;
 	struct lxc_console_log log;
 	bool do_unlink = false;
@@ -57,12 +57,18 @@ int main(int argc, char *argv[])
 	}
 
 	/* 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\"");
+	if (!c->set_config_item(c, "lxc.console.buffer.size", "4096")) {
+		lxc_error("%s\n", "Failed to set config item \"lxc.console.buffer.size\"");
 		goto on_error_put;
 	}
 
-	/* Set on-disk logfile. */
+	/* Set ringbuffer log file. */
+	if (!c->set_config_item(c, "lxc.console.buffer.logfile", "/tmp/console-buffer-log.log")) {
+		lxc_error("%s\n", "Failed to set config item \"lxc.console.buffer.logfile\"");
+		goto on_error_put;
+	}
+
+	/* Set console log file. */
 	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;
@@ -135,11 +141,17 @@ int main(int argc, char *argv[])
 		goto on_error_stop;
 	}
 
+	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->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);
@@ -158,31 +170,31 @@ int main(int argc, char *argv[])
 			  *log.read_max, log.data);
 	}
 
-	logfd = open("/tmp/console-log.log", O_RDONLY);
+	logfd = open("/tmp/console-buffer-log.log", O_RDONLY);
 	if (logfd < 0) {
-		lxc_error("%s - Failed to open console log file "
-			  "\"/tmp/console-log.log\"\n", strerror(errno));
+		lxc_error("%s - Failed to open console ringbuffer log file "
+			  "\"/tmp/console-buffer-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));
+		lxc_error("%s - Failed to read console ringbuffer log file "
+			  "\"/tmp/console-buffer-log.log\"\n", strerror(errno));
 		goto on_error_stop;
 	}
 
-	ret = stat("/tmp/console-log.log", &st);
+	ret = stat("/tmp/console-buffer-log.log", &st_buffer_log_file);
 	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) {
+	if ((uint64_t)st_buffer_log_file.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);
+			  (uint64_t)st_buffer_log_file.st_size, *log.read_max);
 		goto on_error_stop;
 	}
 
@@ -192,8 +204,85 @@ int main(int argc, char *argv[])
 		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);
+			  "console ringbuffer log file. Contents are: \"%s\" - "
+			  "\"%s\"\n", *log.read_max, log.data, buf);
+	}
+
+	ret = stat("/tmp/console-log.log", &st_log_file);
+	if (ret < 0) {
+		lxc_error("%s - Failed to stat on-disk logfile\n", strerror(errno));
+		goto on_error_stop;
+	}
+
+	/* Turn on rotation for the console log file. */
+	if (!c->set_config_item(c, "lxc.console.rotate", "1")) {
+		lxc_error("%s\n", "Failed to set config item \"lxc.console.rotate\"");
+		goto on_error_put;
+	}
+
+	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;
+	}
+
+	/* Leave some time for the container to write something to the log. */
+	sleep(2);
+
+	/* The console log file size must be greater than the console log file
+	 * size since we append to the latter and we truncated the former
+	 * already.
+	 */
+	if (st_log_file.st_size <= st_buffer_log_file.st_size) {
+		lxc_error("%s - Console log file size was smaller than the "
+			  "console buffer log file size: %zu < %zu\n",
+			  strerror(errno), st_log_file.st_size,
+			  st_buffer_log_file.st_size);
+		goto on_error_stop;
+	} else {
+		lxc_debug("Console log file size is %zu bytes and console "
+			  "buffer log file size is %zu bytes\n",
+			  st_log_file.st_size, st_buffer_log_file.st_size);
+	}
+
+	ret = stat("/tmp/console-log.log", &st_log_file);
+	if (ret < 0) {
+		lxc_error("%s - Failed to stat on-disk logfile\n", strerror(errno));
+		goto on_error_stop;
+	}
+
+	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) {
+		lxc_error("%s - Failed to retrieve console log \n", strerror(-ret));
+		goto on_error_stop;
+	}
+
+	/* There should now be a rotated log file called
+	 * "/tmp/console-log.log.old"
+	 */
+	ret = stat("/tmp/console-log.log.old", &st_log_file_old);
+	if (ret < 0) {
+		lxc_error("%s - Failed to stat on-disk logfile\n", strerror(errno));
+		goto on_error_stop;
+	}
+
+	/* The rotated log file should have the same size as before the
+	 * rotation.
+	 */
+	if (st_log_file.st_size != st_log_file_old.st_size) {
+		lxc_error("%s - Console log file size changed during log "
+			  "rotation: %zu != %zu\n",
+			  strerror(errno), st_log_file.st_size,
+			  st_log_file_old.st_size);
+		goto on_error_stop;
 	}
 
 	fret = 0;
@@ -211,7 +300,13 @@ int main(int argc, char *argv[])
 	if (do_unlink) {
 		ret = unlink("/tmp/console-log.log");
 		if (ret < 0)
-			lxc_error("%s - Failed to remove container log file\n", strerror(errno));
+			lxc_error("%s - Failed to remove container log file\n",
+				  strerror(errno));
+
+		ret = unlink("/tmp/console-log.log.old");
+		if (ret < 0)
+			lxc_error("%s - Failed to remove container log file\n",
+				  strerror(errno));
 	}
 	exit(fret);
 }


More information about the lxc-devel mailing list