]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
confile: add lxc.console.size
authorChristian Brauner <christian.brauner@ubuntu.com>
Thu, 15 Feb 2018 10:27:56 +0000 (11:27 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Tue, 27 Feb 2018 20:38:29 +0000 (21:38 +0100)
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 <christian.brauner@ubuntu.com>
doc/lxc.container.conf.sgml.in
src/lxc/commands.c
src/lxc/conf.c
src/lxc/conf.h
src/lxc/confile.c
src/lxc/console.c

index 23c213e99015aaec4873449c2d9aac18f41022d8..f44072148451857da1313d4dd82fe653268469cd 100644 (file)
@@ -832,6 +832,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
           </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>
index 2246b0c4a4397b139f8c431d9918cdc9ab05b4fa..37443c0d02e7077b0a0f4d32c19649dddff0279d 100644 (file)
@@ -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);
        }
index 1ec915191f8f85569e1f4e67bb8ed454db86bb3d..e6d385c2ce169354231f82b96821d41d0abedec3 100644 (file)
@@ -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;
index 388c0518c22da950da5212ecb4df6f66f1578b04..7bbb3fdfc1223fea1747cb57ec62da7c7bfbc9f3 100644 (file)
@@ -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;
index 8c3eccccc7427fa93bfc49f9ec6e28a38564502e..b1e2192c68341668c70a154308662dd976439e4f 100644 (file)
@@ -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)
index a415e6a68627eb4af242700f9b74be4c236385c6..b4f13f38a29145f03c1e677c668c25e920338a06 100644 (file)
@@ -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)