From: Christian Brauner Date: Thu, 15 Feb 2018 10:27:56 +0000 (+0100) Subject: confile: add lxc.console.size X-Git-Tag: lxc-3.0.0.beta1~6^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=861813e52bdba0cda7e3b72ba4932db77d47b08b;p=thirdparty%2Flxc.git confile: add lxc.console.size lxc.console.size regulates the size of the console log file. This is intended to replace lxc.console.buffer.logfile. The current semantics are: - if lxc.console.size is not set: - no limit is placed on the size of the log file - if lxc.console.size is set: - if lxc.console.rotate is set and the next write would exceed the limit: - write as much as possible into the old log file - rotate the log file - write as much as posible into the new log file - discard remaining bytes (scenario shouldn't be possible in normal circumstances) - if lxc.console.rotate is not set and the next write would exceed the limit: - keep overwriting the current log file To make the log file a mirror of the in-memory ringbuffer simply set: lxc.console.buffer.size == lxc.console.size. Signed-off-by: Christian Brauner --- diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in index 23c213e99..f44072148 100644 --- a/doc/lxc.container.conf.sgml.in +++ b/doc/lxc.container.conf.sgml.in @@ -832,6 +832,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + + + + + + Setting this option instructs liblxc to place a limit on the size of + the console log file specified in + . 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 + equal to + . + + + + diff --git a/src/lxc/commands.c b/src/lxc/commands.c index 2246b0c4a..37443c0d0 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -1003,10 +1003,6 @@ 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); @@ -1030,30 +1026,7 @@ static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req, } } - /* 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); } diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 1ec915191..e6d385c2c 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2614,6 +2614,7 @@ struct lxc_conf *lxc_conf_init(void) 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; diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 388c0518c..7bbb3fdfc 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -171,12 +171,24 @@ struct lxc_console { 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; diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 8c3eccccc..b1e2192c6 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -82,11 +82,12 @@ lxc_config_define(cap_keep); 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); @@ -160,6 +161,7 @@ static struct lxc_config_t config[] = { { "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, }, @@ -1961,6 +1963,53 @@ static int set_config_console_buffer_size(const char *key, const char *value, 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) @@ -3298,6 +3347,13 @@ static int get_config_console_buffer_size(const char *key, char *retv, 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) @@ -3840,6 +3896,13 @@ static inline int clr_config_console_buffer_size(const char *key, 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) diff --git a/src/lxc/console.c b/src/lxc/console.c index a415e6a68..b4f13f38a 100644 --- a/src/lxc/console.c +++ b/src/lxc/console.c @@ -204,6 +204,152 @@ void lxc_console_signal_fini(struct lxc_tty_state *ts) 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) { @@ -245,9 +391,9 @@ int lxc_console_cb_con(int fd, uint32_t events, void *data, 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)