</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <option>lxc.console.size</option>
+ </term>
+ <listitem>
+ <para>
+ Setting this option instructs liblxc to place a limit on the size of
+ the console log file specified in
+ <option>lxc.console.logfile</option>. Note that size of the log file
+ must be at least as big as a standard page size. When passed a value
+ smaller than a single page size liblxc will set the size of log file
+ to a single page size. A page size is usually 4kB.
+
+ The keyword 'auto' will cause liblxc to place a limit of 128kB on
+ the log file.
+
+ When manually specifying a size for the log file 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'.)
+
+ If users want to mirror the console ringbuffer on disk they should set
+ <option>lxc.console.size</option> equal to
+ <option>lxc.console.buffer.size</option>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term>
<option>lxc.console.buffer.logfile</option>
rsp.ret = 0;
if (log->clear) {
- int ret;
- size_t len;
- char *tmp;
-
/* clear the ringbuffer */
lxc_ringbuf_clear(buf);
}
}
- /* 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(".1");
- tmp = alloca(len);
-
- rsp.ret = -EFBIG;
- ret = snprintf(tmp, len, "%s.1", 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);
+ rsp.ret = lxc_console_rotate_log_file(console);
} else if (rsp.datalen > 0) {
lxc_ringbuf_move_read_addr(buf, rsp.datalen);
}
new->console.buffer_size = 0;
new->console.log_path = NULL;
new->console.log_fd = -1;
+ new->console.log_size = 0;
new->console.path = NULL;
new->console.peer = -1;
new->console.peerpty.busy = -1;
struct lxc_pty_info peerpty;
struct lxc_epoll_descr *descr;
char *path;
- char *log_path;
- int log_fd;
- unsigned int log_rotate;
char name[MAXPATHLEN];
struct termios *tios;
struct lxc_tty_state *tty_state;
+
+ struct /* lxc_console_log */ {
+ /* size of the log file */
+ uint64_t log_size;
+
+ /* path to the log file */
+ char *log_path;
+
+ /* fd to the log file */
+ int log_fd;
+
+ /* whether the log file will be rotated */
+ unsigned int log_rotate;
+ };
+
struct /* lxc_console_ringbuf */ {
/* size of the ringbuffer */
uint64_t buffer_size;
lxc_config_define(cgroup_controller);
lxc_config_define(cgroup2_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_logfile);
lxc_config_define(console_path);
+lxc_config_define(console_rotate);
+lxc_config_define(console_size);
lxc_config_define(environment);
lxc_config_define(ephemeral);
lxc_config_define(execute_cmd);
{ "lxc.console.logfile", set_config_console_logfile, get_config_console_logfile, clr_config_console_logfile, },
{ "lxc.console.path", set_config_console_path, get_config_console_path, clr_config_console_path, },
{ "lxc.console.rotate", set_config_console_rotate, get_config_console_rotate, clr_config_console_rotate, },
+ { "lxc.console.size", set_config_console_size, get_config_console_size, clr_config_console_size, },
{ "lxc.environment", set_config_environment, get_config_environment, clr_config_environment, },
{ "lxc.ephemeral", set_config_ephemeral, get_config_ephemeral, clr_config_ephemeral, },
{ "lxc.execute.cmd", set_config_execute_cmd, get_config_execute_cmd, clr_config_execute_cmd, },
return 0;
}
+static int set_config_console_size(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ int ret;
+ int64_t size;
+ uint64_t log_size, pgsz;
+
+ if (lxc_config_value_empty(value)) {
+ lxc_conf->console.log_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;
+ return 0;
+ }
+
+ ret = parse_byte_size_string(value, &size);
+ if (ret < 0)
+ return -1;
+
+ if (size < 0)
+ return -EINVAL;
+
+ /* must be at least a page size */
+ pgsz = lxc_getpagesize();
+ if ((uint64_t)size < pgsz) {
+ NOTICE("Requested ringbuffer size for the console is %" PRId64
+ " but must be at least %" PRId64
+ " bytes. Setting ringbuffer size to %" PRId64 " bytes",
+ size, pgsz, pgsz);
+ size = pgsz;
+ }
+
+ log_size = lxc_find_next_power2((uint64_t)size);
+ if (log_size == 0)
+ return -EINVAL;
+
+ if (log_size != size)
+ NOTICE("Passed size was not a power of 2. Rounding log size to "
+ "next power of two: %" PRIu64 " bytes", log_size);
+
+ lxc_conf->console.log_size = log_size;
+ return 0;
+}
+
static int set_config_console_buffer_logfile(const char *key, const char *value,
struct lxc_conf *lxc_conf,
void *data)
return lxc_get_conf_uint64(c, retv, inlen, c->console.buffer_size);
}
+static int get_config_console_size(const char *key, char *retv, int inlen,
+ struct lxc_conf *c, void *data)
+{
+ return lxc_get_conf_uint64(c, retv, inlen, c->console.log_size);
+}
+
+
static int get_config_console_buffer_logfile(const char *key, char *retv,
int inlen, struct lxc_conf *c,
void *data)
return 0;
}
+static inline int clr_config_console_size(const char *key, struct lxc_conf *c,
+ void *data)
+{
+ c->console.log_size = 0;
+ return 0;
+}
+
static inline int clr_config_console_buffer_logfile(const char *key,
struct lxc_conf *c,
void *data)
free(ts);
}
+static int lxc_console_truncate_log_file(struct lxc_console *console)
+{
+ /* be very certain things are kosher */
+ if (!console->log_path || console->log_fd < 0)
+ return -EBADF;
+
+ return lxc_unpriv(ftruncate(console->log_fd, 0));
+}
+
+static int lxc_console_rotate_log_file(struct lxc_console *console)
+{
+ int ret;
+ size_t len;
+ char *tmp;
+
+ /* rotate the console log file */
+ if (!console->log_path || console->log_rotate == 0)
+ return -EOPNOTSUPP;
+
+ /* be very certain things are kosher */
+ if (console->log_fd < 0)
+ return -EBADF;
+
+ len = strlen(console->log_path) + sizeof(".1");
+ tmp = alloca(len);
+
+ ret = snprintf(tmp, len, "%s.1", console->log_path);
+ if (ret < 0 || (size_t)ret >= len)
+ return -EFBIG;
+
+ close(console->log_fd);
+ console->log_fd = -1;
+ ret = lxc_unpriv(rename(console->log_path, tmp));
+ if (ret < 0)
+ return ret;
+
+ return lxc_console_create_log_file(console);
+}
+
+static int lxc_console_write_log_file(struct lxc_console *console, char *buf,
+ int bytes_read)
+{
+ int ret;
+ int64_t space_left = -1;
+ struct stat st;
+
+ if (console->log_fd < 0)
+ return 0;
+
+ /* A log size <= 0 means that there's no limit on the size of the log
+ * file at which point we simply ignore whether the log is supposed to
+ * be rotated or not.
+ */
+ if (console->log_size <= 0)
+ return lxc_write_nointr(console->log_fd, buf, bytes_read);
+
+ /* Get current size of the log file. */
+ ret = fstat(console->log_fd, &st);
+ if (ret < 0) {
+ SYSERROR("Failed to stat the console log file descriptor");
+ return -1;
+ }
+
+ /* handle non-regular files */
+ if ((st.st_mode & S_IFMT) != S_IFREG) {
+ /* This isn't a regular file. so rotating the file seems a
+ * dangerous thing to do, size limits are also very
+ * questionable. Let's not risk anything and tell the user that
+ * he's requesting us to do weird stuff.
+ */
+ if (console->log_rotate > 0 || console->log_size > 0)
+ return -EINVAL;
+
+ /* I mean, sure log wherever you want to. */
+ return lxc_write_nointr(console->log_fd, buf, bytes_read);
+ }
+
+ /* check how much space we have left */
+ space_left = console->log_size - st.st_size;
+
+ /* User doesn't want to rotate the log file and there's no more space
+ * left so simply truncate it.
+ */
+ if (space_left <= 0 && console->log_rotate <= 0) {
+ ret = lxc_console_truncate_log_file(console);
+ if (ret < 0)
+ return ret;
+
+ if (bytes_read <= console->log_size)
+ return lxc_write_nointr(console->log_fd, buf, bytes_read);
+
+ /* Write as much as we can into the buffer and loose the rest. */
+ return lxc_write_nointr(console->log_fd, buf, console->log_size);
+ }
+
+ /* There's enough space left. */
+ if (bytes_read <= space_left)
+ return lxc_write_nointr(console->log_fd, buf, bytes_read);
+
+ /* There's not enough space left but at least write as much as we can
+ * into the old log file.
+ */
+ ret = lxc_write_nointr(console->log_fd, buf, space_left);
+ if (ret < 0)
+ return -1;
+
+ /* Calculate how many bytes we still need to write. */
+ bytes_read -= space_left;
+
+ /* There be more to write but we aren't instructed to rotate the log
+ * file so simply return. There's no error on our side here.
+ */
+ if (console->log_rotate > 0)
+ ret = lxc_console_rotate_log_file(console);
+ else
+ ret = lxc_console_truncate_log_file(console);
+ if (ret < 0)
+ return ret;
+
+ if (console->log_size < bytes_read) {
+ /* Well, this is unfortunate because it means that there is more
+ * to write than the user has granted us space. There are
+ * multiple ways to handle this but let's use the simplest one:
+ * write as much as we can, tell the user that there was more
+ * stuff to write and move on.
+ * Note that this scenario shouldn't actually happen with the
+ * standard pty-based console that LXC allocates since it will
+ * be switched into raw mode. In raw mode only 1 byte at a time
+ * should be read and written.
+ */
+ WARN("Size of console log file is smaller than the bytes to write");
+ ret = lxc_write_nointr(console->log_fd, buf, console->log_size);
+ if (ret < 0)
+ return -1;
+ bytes_read -= ret;
+ return bytes_read;
+ }
+
+ /* Yay, we made it. */
+ ret = lxc_write_nointr(console->log_fd, buf, bytes_read);
+ if (ret < 0)
+ return -1;
+ bytes_read -= ret;
+ return bytes_read;
+}
+
int lxc_console_cb_con(int fd, uint32_t events, void *data,
struct lxc_epoll_descr *descr)
{
if (console->buffer_size > 0)
w_rbuf = lxc_ringbuf_write(&console->ringbuf, buf, r);
- /* write to console log */
- if (console->log_fd >= 0)
- w_log = lxc_write_nointr(console->log_fd, buf, r);
+ if (console->log_fd > 0)
+ w_log = lxc_console_write_log_file(console, buf, r);
+
}
if (w != r)