Unverified Commit 7263b97f by Stéphane Graber Committed by GitHub

Merge pull request #2167 from brauner/2018-02-15/simplify_console_logging

console: simplify console logging
parents d931b243 89962c6c
......@@ -834,20 +834,28 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<varlistentry>
<term>
<option>lxc.console.buffer.logfile</option>
<option>lxc.console.size</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 requested. Note that the this
option is only used by liblxc when
<option>lxc.console.buffer.size</option> is set.
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.
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.
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>
......
......@@ -934,7 +934,6 @@ int lxc_cmd_console_log(const char *name, const char *lxcpath,
data.clear = log->clear;
data.read = log->read;
data.read_max = *log->read_max;
data.write_logfile = log->write_logfile;
cmd.req.cmd = LXC_CMD_CONSOLE_LOG;
cmd.req.data = &data;
......@@ -968,7 +967,6 @@ static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req,
struct lxc_cmd_rsp rsp;
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;
rsp.ret = -EFAULT;
......@@ -991,72 +989,12 @@ static int lxc_cmd_console_log_callback(int fd, struct lxc_cmd_req *req,
if (log->read && (buf->r_off == buf->w_off))
goto out;
if (log->write_logfile && rsp.datalen > 0) {
rsp.ret = -ENOENT;
if (!console->buffer_log_file)
goto out;
rsp.ret = lxc_console_write_ringbuffer(console);
if (rsp.ret < 0)
goto out;
}
rsp.ret = 0;
if (log->clear) {
int ret;
size_t len;
char *tmp;
if (log->clear)
/* clear the ringbuffer */
lxc_ringbuf_clear(buf);
/* 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;
}
}
/* 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);
} else if (rsp.datalen > 0) {
else if (rsp.datalen > 0)
lxc_ringbuf_move_read_addr(buf, rsp.datalen);
}
out:
return lxc_cmd_rsp_send(fd, &rsp);
......
......@@ -2609,11 +2609,10 @@ 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.log_size = 0;
new->console.path = NULL;
new->console.peer = -1;
new->console.peerpty.busy = -1;
......
......@@ -171,22 +171,28 @@ 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;
/* 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;
};
......
......@@ -82,11 +82,11 @@ 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);
......@@ -155,11 +155,11 @@ static struct lxc_config_t config[] = {
{ "lxc.cgroup2", set_config_cgroup2_controller, get_config_cgroup2_controller, clr_config_cgroup2_controller, },
{ "lxc.cgroup.dir", set_config_cgroup_dir, get_config_cgroup_dir, clr_config_cgroup_dir, },
{ "lxc.cgroup", set_config_cgroup_controller, get_config_cgroup_controller, clr_config_cgroup_controller, },
{ "lxc.console.buffer.logfile", set_config_console_buffer_logfile, get_config_console_buffer_logfile, clr_config_console_buffer_logfile, },
{ "lxc.console.buffer.size", set_config_console_buffer_size, get_config_console_buffer_size, clr_config_console_buffer_size, },
{ "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,11 +1961,51 @@ 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)
static int set_config_console_size(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 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;
}
int append_unexp_config_line(const char *line, struct lxc_conf *conf)
......@@ -3298,13 +3338,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_buffer_logfile(const char *key, char *retv,
int inlen, struct lxc_conf *c,
void *data)
static int get_config_console_size(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);
return lxc_get_conf_uint64(c, retv, inlen, c->console.log_size);
}
static int get_config_seccomp_profile(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
......@@ -3840,12 +3880,10 @@ 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)
static inline int clr_config_console_size(const char *key, struct lxc_conf *c,
void *data)
{
free(c->console.buffer_log_file);
c->console.buffer_log_file = NULL;
c->console.log_size = 0;
return 0;
}
......
......@@ -204,6 +204,150 @@ 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;
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);
}
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'd 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 +389,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)
......@@ -566,15 +710,28 @@ int lxc_console_write_ringbuffer(struct lxc_console *console)
uint64_t used;
struct lxc_ringbuf *buf = &console->ringbuf;
if (!console->buffer_log_file)
/* There's not log file where we can dump the ringbuffer to. */
if (console->log_fd < 0)
return 0;
/* The log file is simply appended to. */
if (console->log_size == 0)
return 0;
used = lxc_ringbuf_used(buf);
if (used == 0)
return 0;
ret = lxc_console_truncate_log_file(console);
if (ret < 0)
return ret;
/* Write as much as we can without exceeding the limit. */
if (console->log_size < used)
used = console->log_size;
r_addr = lxc_ringbuf_get_read_addr(buf);
ret = lxc_write_nointr(console->buffer_log_file_fd, r_addr, used);
ret = lxc_write_nointr(console->log_fd, r_addr, used);
if (ret < 0)
return -EIO;
......@@ -587,7 +744,7 @@ void lxc_console_delete(struct lxc_console *console)
ret = lxc_console_write_ringbuffer(console);
if (ret < 0)
WARN("Failed to write console log to disk");
WARN("Failed to write console ringbuffer to console log file");
if (console->tios && console->peer >= 0) {
ret = tcsetattr(console->peer, TCSAFLUSH, console->tios);
......@@ -612,31 +769,6 @@ void lxc_console_delete(struct lxc_console *console)
if (console->log_fd >= 0)
close(console->log_fd);
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;
}
/**
......@@ -766,11 +898,6 @@ int lxc_console_create(struct lxc_conf *conf)
if (ret < 0)
goto err;
/* create console ringbuffer log file */
ret = lxc_console_create_ringbuf_log_file(console);
if (ret < 0)
goto err;
return 0;
err:
......@@ -1020,13 +1147,11 @@ void lxc_pty_init(struct lxc_console *pty)
pty->master = -EBADF;
pty->peer = -EBADF;
pty->log_fd = -EBADF;
pty->buffer_log_file_fd = -EBADF;
lxc_pty_info_init(&pty->peerpty);
}
void lxc_pty_conf_free(struct lxc_console *console)
{
free(console->buffer_log_file);
free(console->log_path);
free(console->path);
if (console->buffer_size > 0 && console->ringbuf.addr)
......
......@@ -227,7 +227,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);
extern int lxc_console_cb_con(int fd, uint32_t events, void *data,
struct lxc_epoll_descr *descr);
......
......@@ -967,12 +967,6 @@ struct lxc_console_log {
* "data" is invalid.
*/
char *data;
/* If a console log file was specified this flag indicates whether the
* contents of the ringbuffer should be written to the logfile when a
* request is sent to the ringbuffer.
*/
bool write_logfile;
};
/*!
......
......@@ -36,10 +36,8 @@
int main(int argc, char *argv[])
{
int logfd, ret;
char buf[4096 + 1];
ssize_t bytes;
struct stat st_buffer_log_file, st_log_file, st_log_file_old;
int ret;
struct stat st_log_file;
struct lxc_container *c;
struct lxc_console_log log;
bool do_unlink = false;
......@@ -62,12 +60,6 @@ int main(int argc, char *argv[])
goto on_error_put;
}
/* 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\"");
......@@ -108,7 +100,6 @@ int main(int argc, char *argv[])
log.clear = false;
log.read_max = &(uint64_t){0};
log.read = true;
log.write_logfile = false;
ret = c->console_log(c, &log);
if (ret < 0) {
......@@ -126,7 +117,6 @@ int main(int argc, char *argv[])
/* Clear the console ringbuffer. */
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) {
......@@ -156,58 +146,6 @@ int main(int argc, char *argv[])
/* Leave some time for the container to write something to the log. */
sleep(2);
log.read_max = &(uint64_t){0};
log.read = true;
log.write_logfile = true;
log.clear = false;
ret = c->console_log(c, &log);
if (ret < 0) {
lxc_error("%s - Failed to retrieve console log \n", strerror(-ret));
goto on_error_stop;
} else {
lxc_debug("Retrieved %" PRIu64
" bytes from console log. Contents are \"%s\"\n",
*log.read_max, log.data);
}
logfd = open("/tmp/console-buffer-log.log", O_RDONLY);
if (logfd < 0) {
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 ringbuffer log file "
"\"/tmp/console-buffer-log.log\"\n", strerror(errno));
goto on_error_stop;
}
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_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_buffer_log_file.st_size, *log.read_max);
goto on_error_stop;
}
if (memcmp(log.data, buf, *log.read_max)) {
lxc_error("%s - Contents of in-memory ringbuffer and on-disk "
"logfile do not match\n", strerror(errno));
goto on_error_stop;
} else {
lxc_debug("Retrieved %" PRIu64 " bytes from console log and "
"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));
......@@ -233,59 +171,6 @@ int main(int argc, char *argv[])
/* 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), (size_t)st_log_file.st_size,
(size_t)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",
(size_t)st_log_file.st_size,
(size_t)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.1"
*/
ret = stat("/tmp/console-log.log.1", &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), (size_t)st_log_file.st_size,
(size_t)st_log_file_old.st_size);
goto on_error_stop;
}
fret = 0;
on_error_stop:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment