mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-26 08:44:53 +00:00
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 <christian.brauner@ubuntu.com>
This commit is contained in:
parent
9e49a491de
commit
861813e52b
@ -832,6 +832,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term>
|
<term>
|
||||||
<option>lxc.console.buffer.logfile</option>
|
<option>lxc.console.buffer.logfile</option>
|
||||||
|
@ -1003,10 +1003,6 @@ static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req,
|
|||||||
|
|
||||||
rsp.ret = 0;
|
rsp.ret = 0;
|
||||||
if (log->clear) {
|
if (log->clear) {
|
||||||
int ret;
|
|
||||||
size_t len;
|
|
||||||
char *tmp;
|
|
||||||
|
|
||||||
/* clear the ringbuffer */
|
/* clear the ringbuffer */
|
||||||
lxc_ringbuf_clear(buf);
|
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 */
|
rsp.ret = lxc_console_rotate_log_file(console);
|
||||||
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);
|
|
||||||
} else if (rsp.datalen > 0) {
|
} else if (rsp.datalen > 0) {
|
||||||
lxc_ringbuf_move_read_addr(buf, rsp.datalen);
|
lxc_ringbuf_move_read_addr(buf, rsp.datalen);
|
||||||
}
|
}
|
||||||
|
@ -2614,6 +2614,7 @@ struct lxc_conf *lxc_conf_init(void)
|
|||||||
new->console.buffer_size = 0;
|
new->console.buffer_size = 0;
|
||||||
new->console.log_path = NULL;
|
new->console.log_path = NULL;
|
||||||
new->console.log_fd = -1;
|
new->console.log_fd = -1;
|
||||||
|
new->console.log_size = 0;
|
||||||
new->console.path = NULL;
|
new->console.path = NULL;
|
||||||
new->console.peer = -1;
|
new->console.peer = -1;
|
||||||
new->console.peerpty.busy = -1;
|
new->console.peerpty.busy = -1;
|
||||||
|
@ -171,12 +171,24 @@ struct lxc_console {
|
|||||||
struct lxc_pty_info peerpty;
|
struct lxc_pty_info peerpty;
|
||||||
struct lxc_epoll_descr *descr;
|
struct lxc_epoll_descr *descr;
|
||||||
char *path;
|
char *path;
|
||||||
char *log_path;
|
|
||||||
int log_fd;
|
|
||||||
unsigned int log_rotate;
|
|
||||||
char name[MAXPATHLEN];
|
char name[MAXPATHLEN];
|
||||||
struct termios *tios;
|
struct termios *tios;
|
||||||
struct lxc_tty_state *tty_state;
|
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 */ {
|
struct /* lxc_console_ringbuf */ {
|
||||||
/* size of the ringbuffer */
|
/* size of the ringbuffer */
|
||||||
uint64_t buffer_size;
|
uint64_t buffer_size;
|
||||||
|
@ -82,11 +82,12 @@ lxc_config_define(cap_keep);
|
|||||||
lxc_config_define(cgroup_controller);
|
lxc_config_define(cgroup_controller);
|
||||||
lxc_config_define(cgroup2_controller);
|
lxc_config_define(cgroup2_controller);
|
||||||
lxc_config_define(cgroup_dir);
|
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_logfile);
|
||||||
lxc_config_define(console_buffer_size);
|
lxc_config_define(console_buffer_size);
|
||||||
|
lxc_config_define(console_logfile);
|
||||||
lxc_config_define(console_path);
|
lxc_config_define(console_path);
|
||||||
|
lxc_config_define(console_rotate);
|
||||||
|
lxc_config_define(console_size);
|
||||||
lxc_config_define(environment);
|
lxc_config_define(environment);
|
||||||
lxc_config_define(ephemeral);
|
lxc_config_define(ephemeral);
|
||||||
lxc_config_define(execute_cmd);
|
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.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.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.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.environment", set_config_environment, get_config_environment, clr_config_environment, },
|
||||||
{ "lxc.ephemeral", set_config_ephemeral, get_config_ephemeral, clr_config_ephemeral, },
|
{ "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, },
|
{ "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;
|
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,
|
static int set_config_console_buffer_logfile(const char *key, const char *value,
|
||||||
struct lxc_conf *lxc_conf,
|
struct lxc_conf *lxc_conf,
|
||||||
void *data)
|
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);
|
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,
|
static int get_config_console_buffer_logfile(const char *key, char *retv,
|
||||||
int inlen, struct lxc_conf *c,
|
int inlen, struct lxc_conf *c,
|
||||||
void *data)
|
void *data)
|
||||||
@ -3840,6 +3896,13 @@ static inline int clr_config_console_buffer_size(const char *key,
|
|||||||
return 0;
|
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,
|
static inline int clr_config_console_buffer_logfile(const char *key,
|
||||||
struct lxc_conf *c,
|
struct lxc_conf *c,
|
||||||
void *data)
|
void *data)
|
||||||
|
@ -204,6 +204,152 @@ void lxc_console_signal_fini(struct lxc_tty_state *ts)
|
|||||||
free(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,
|
int lxc_console_cb_con(int fd, uint32_t events, void *data,
|
||||||
struct lxc_epoll_descr *descr)
|
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)
|
if (console->buffer_size > 0)
|
||||||
w_rbuf = lxc_ringbuf_write(&console->ringbuf, buf, r);
|
w_rbuf = lxc_ringbuf_write(&console->ringbuf, buf, r);
|
||||||
|
|
||||||
/* write to console log */
|
if (console->log_fd > 0)
|
||||||
if (console->log_fd >= 0)
|
w_log = lxc_console_write_log_file(console, buf, r);
|
||||||
w_log = lxc_write_nointr(console->log_fd, buf, r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (w != r)
|
if (w != r)
|
||||||
|
Loading…
Reference in New Issue
Block a user