1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

log: separate output and make it possible to use given FDs

Currently, the output is separated in 3 parts and each part can go into
a separate and user-defined file descriptor:

  - common output (stdout by default, customizable by LVM_OUT_FD environment variable)
  - error output (stderr by default, customizable by LVM_ERR_FD environment variable)
  - report output (stdout by default, customizable by LVM_REPORT_FD environment variable)

For example, each type of output goes to different output file:

  [0] fedora/~ # export LVM_REPORT_FD=3

  [0] fedora/~ # lvs fedora vg/abc 1>out 2>err 3>report

  [0] fedora/~ # cat out

  [0] fedora/~ # cat err
    Volume group "vg" not found
    Cannot process volume group vg

  [0] fedora/~ # cat report
    LV   VG     Attr       LSize   Layout     Role       CTime
    root fedora -wi-ao----  19.00g linear     public     Wed May 27 2015 08:09:21
    swap fedora -wi-ao---- 500.00m linear     public     Wed May 27 2015 08:09:21

Another example in LVM shell where the report goes to "report" file:

  [0] fedora/~ # export LVM_REPORT_FD=3
  [0] fedora/~ # lvm 3>report

  (in lvm shell)
  lvm> vgs

  (content of "report" file)
  [1] fedora/~ # cat report
    VG     #PV #LV #SN Attr   VSize  VFree
    fedora   1   2   0 wz--n- 19.49g    0

  (in lvm shell)
  lvm> lvs

  (content of "report" file)
  [1] fedora/~ # cat report
    VG     #PV #LV #SN Attr   VSize  VFree
    fedora   1   2   0 wz--n- 19.49g    0
    LV   VG     Attr       LSize   Layout     Role       CTime
    root fedora -wi-ao----  19.00g linear     public     Wed May 27 2015 08:09:21
    swap fedora -wi-ao---- 500.00m linear     public     Wed May 27 2015 08:09:21
This commit is contained in:
Peter Rajnoha 2016-07-08 16:47:51 +02:00
parent bb9789f2b3
commit 06ce9b4e42
6 changed files with 245 additions and 59 deletions

View File

@ -1649,37 +1649,6 @@ static void _init_globals(struct cmd_context *cmd)
init_mirror_in_sync(0); init_mirror_in_sync(0);
} }
/*
* Close and reopen stream on file descriptor fd.
*/
static int _reopen_stream(FILE *stream, int fd, const char *mode, const char *name, FILE **new_stream)
{
int fd_copy, new_fd;
if ((fd_copy = dup(fd)) < 0) {
log_sys_error("dup", name);
return 0;
}
if (fclose(stream))
log_sys_error("fclose", name);
if ((new_fd = dup2(fd_copy, fd)) < 0)
log_sys_error("dup2", name);
else if (new_fd != fd)
log_error("dup2(%d, %d) returned %d", fd_copy, fd, new_fd);
if (close(fd_copy) < 0)
log_sys_error("close", name);
if (!(*new_stream = fdopen(fd, mode))) {
log_sys_error("fdopen", name);
return 0;
}
return 1;
}
/* /*
* init_connections(); * init_connections();
* _init_lvmetad(); * _init_lvmetad();
@ -1835,7 +1804,6 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
unsigned set_filters) unsigned set_filters)
{ {
struct cmd_context *cmd; struct cmd_context *cmd;
FILE *new_stream;
int flags; int flags;
#ifdef M_MMAP_MAX #ifdef M_MMAP_MAX
@ -1885,9 +1853,8 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
if (is_valid_fd(STDIN_FILENO) && if (is_valid_fd(STDIN_FILENO) &&
((flags = fcntl(STDIN_FILENO, F_GETFL)) > 0) && ((flags = fcntl(STDIN_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_WRONLY) { (flags & O_ACCMODE) != O_WRONLY) {
if (!_reopen_stream(stdin, STDIN_FILENO, "r", "stdin", &new_stream)) if (!reopen_standard_stream(&stdin, "r"))
goto_out; goto_out;
stdin = new_stream;
if (setvbuf(stdin, cmd->linebuffer, _IOLBF, linebuffer_size)) { if (setvbuf(stdin, cmd->linebuffer, _IOLBF, linebuffer_size)) {
log_sys_error("setvbuf", ""); log_sys_error("setvbuf", "");
goto out; goto out;
@ -1897,9 +1864,8 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
if (is_valid_fd(STDOUT_FILENO) && if (is_valid_fd(STDOUT_FILENO) &&
((flags = fcntl(STDOUT_FILENO, F_GETFL)) > 0) && ((flags = fcntl(STDOUT_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_RDONLY) { (flags & O_ACCMODE) != O_RDONLY) {
if (!_reopen_stream(stdout, STDOUT_FILENO, "w", "stdout", &new_stream)) if (!reopen_standard_stream(&stdout, "w"))
goto_out; goto_out;
stdout = new_stream;
if (setvbuf(stdout, cmd->linebuffer + linebuffer_size, if (setvbuf(stdout, cmd->linebuffer + linebuffer_size,
_IOLBF, linebuffer_size)) { _IOLBF, linebuffer_size)) {
log_sys_error("setvbuf", ""); log_sys_error("setvbuf", "");
@ -1911,7 +1877,6 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
/* Without buffering, must not use stdin/stdout */ /* Without buffering, must not use stdin/stdout */
init_silent(1); init_silent(1);
#endif #endif
/* /*
* Environment variable LVM_SYSTEM_DIR overrides this below. * Environment variable LVM_SYSTEM_DIR overrides this below.
*/ */
@ -2227,7 +2192,6 @@ int refresh_toolcontext(struct cmd_context *cmd)
void destroy_toolcontext(struct cmd_context *cmd) void destroy_toolcontext(struct cmd_context *cmd)
{ {
struct dm_config_tree *cft_cmdline; struct dm_config_tree *cft_cmdline;
FILE *new_stream;
int flags; int flags;
if (cmd->dump_filter && cmd->filter && cmd->filter->dump && if (cmd->dump_filter && cmd->filter && cmd->filter->dump &&
@ -2266,20 +2230,18 @@ void destroy_toolcontext(struct cmd_context *cmd)
if (is_valid_fd(STDIN_FILENO) && if (is_valid_fd(STDIN_FILENO) &&
((flags = fcntl(STDIN_FILENO, F_GETFL)) > 0) && ((flags = fcntl(STDIN_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_WRONLY) { (flags & O_ACCMODE) != O_WRONLY) {
if (_reopen_stream(stdin, STDIN_FILENO, "r", "stdin", &new_stream)) { if (reopen_standard_stream(&stdin, "r"))
stdin = new_stream;
setlinebuf(stdin); setlinebuf(stdin);
} else else
cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */ cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */
} }
if (is_valid_fd(STDOUT_FILENO) && if (is_valid_fd(STDOUT_FILENO) &&
((flags = fcntl(STDOUT_FILENO, F_GETFL)) > 0) && ((flags = fcntl(STDOUT_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_RDONLY) { (flags & O_ACCMODE) != O_RDONLY) {
if (_reopen_stream(stdout, STDOUT_FILENO, "w", "stdout", &new_stream)) { if (reopen_standard_stream(&stdout, "w"))
stdout = new_stream;
setlinebuf(stdout); setlinebuf(stdout);
} else else
cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */ cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */
} }

View File

@ -18,6 +18,7 @@
#include "memlock.h" #include "memlock.h"
#include "defaults.h" #include "defaults.h"
#include "report.h" #include "report.h"
#include "lvm-file.h"
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
@ -59,6 +60,154 @@ static log_report_t _log_report = {
.object_group = NULL .object_group = NULL
}; };
#define LOG_STREAM_BUFFER_SIZE 4096
struct log_stream_item {
FILE *stream;
char *buffer;
};
struct log_stream {
struct log_stream_item out;
struct log_stream_item err;
struct log_stream_item report;
} _log_stream = {{NULL, NULL},
{NULL, NULL},
{NULL, NULL}};
#define out_stream (_log_stream.out.stream ? : stdout)
#define err_stream (_log_stream.err.stream ? : stderr)
#define report_stream (_log_stream.report.stream ? : stdout)
static int _set_custom_log_stream(struct log_stream_item *stream_item, int custom_fd)
{
FILE *final_stream = NULL;
int flags;
int r = 1;
if (custom_fd < 0)
goto out;
if (is_valid_fd(custom_fd)) {
if ((flags = fcntl(custom_fd, F_GETFL)) > 0) {
if ((flags & O_ACCMODE) == O_RDONLY) {
log_error("File descriptor %d already open in read-only "
"mode, expected write-only or read-write mode.",
(int) custom_fd);
r = 0;
goto out;
}
}
if (custom_fd == STDIN_FILENO) {
log_error("Can't set standard input for log output.");
r = 0;
goto out;
}
if (custom_fd == STDOUT_FILENO) {
final_stream = stdout;
goto out;
}
if (custom_fd == STDERR_FILENO) {
final_stream = stderr;
goto out;
}
}
if (!(final_stream = fdopen(custom_fd, "w"))) {
log_error("Failed to open stream for file descriptor %d.",
(int) custom_fd);
r = 0;
goto out;
}
if (!(stream_item->buffer = dm_malloc(LOG_STREAM_BUFFER_SIZE))) {
log_error("Failed to allocate buffer for stream on file "
"descriptor %d.", (int) custom_fd);
} else {
if (setvbuf(final_stream, stream_item->buffer, _IOLBF, LOG_STREAM_BUFFER_SIZE)) {
log_sys_error("setvbuf", "");
dm_free(stream_item->buffer);
stream_item->buffer = NULL;
}
}
out:
stream_item->stream = final_stream;
return r;
}
int init_custom_log_streams(struct custom_fds *custom_fds)
{
return _set_custom_log_stream(&_log_stream.out, custom_fds->out) &&
_set_custom_log_stream(&_log_stream.err, custom_fds->err) &&
_set_custom_log_stream(&_log_stream.report, custom_fds->report);
}
static void _check_and_replace_standard_log_streams(FILE *old_stream, FILE *new_stream)
{
if (_log_stream.out.stream == old_stream)
_log_stream.out.stream = new_stream;
if (_log_stream.err.stream == old_stream)
_log_stream.err.stream = new_stream;
if (_log_stream.report.stream == old_stream)
_log_stream.report.stream = new_stream;
}
/*
* Close and reopen standard stream on file descriptor fd.
*/
int reopen_standard_stream(FILE **stream, const char *mode)
{
int fd, fd_copy, new_fd;
const char *name;
FILE *old_stream = *stream;
FILE *new_stream;
if (old_stream == stdin) {
fd = STDIN_FILENO;
name = "stdin";
} else if (old_stream == stdout) {
fd = STDOUT_FILENO;
name = "stdout";
} else if (old_stream == stderr) {
fd = STDERR_FILENO;
name = "stderr";
} else {
log_error(INTERNAL_ERROR "reopen_standard_stream called on non-standard stream");
return 0;
}
if ((fd_copy = dup(fd)) < 0) {
log_sys_error("dup", name);
return 0;
}
if (fclose(old_stream))
log_sys_error("fclose", name);
if ((new_fd = dup2(fd_copy, fd)) < 0)
log_sys_error("dup2", name);
else if (new_fd != fd)
log_error("dup2(%d, %d) returned %d", fd_copy, fd, new_fd);
if (close(fd_copy) < 0)
log_sys_error("close", name);
if (!(new_stream = fdopen(fd, mode))) {
log_sys_error("fdopen", name);
return 0;
}
_check_and_replace_standard_log_streams(old_stream, new_stream);
*stream = new_stream;
return 1;
}
void init_log_fn(lvm2_log_fn_t log_fn) void init_log_fn(lvm2_log_fn_t log_fn)
{ {
_lvm2_log_fn = log_fn; _lvm2_log_fn = log_fn;
@ -207,10 +356,10 @@ void fin_log(void)
if (_log_to_file) { if (_log_to_file) {
if (dm_fclose(_log_file)) { if (dm_fclose(_log_file)) {
if (errno) if (errno)
fprintf(stderr, "failed to write log file: %s\n", fprintf(err_stream, "failed to write log file: %s\n",
strerror(errno)); strerror(errno));
else else
fprintf(stderr, "failed to write log file\n"); fprintf(err_stream, "failed to write log file\n");
} }
_log_to_file = 0; _log_to_file = 0;
@ -378,7 +527,7 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
/* When newer glibc returns >= sizeof(locn), we will just log what /* When newer glibc returns >= sizeof(locn), we will just log what
* has fit into buffer, it's '\0' terminated string */ * has fit into buffer, it's '\0' terminated string */
if (n < 0) { if (n < 0) {
fprintf(stderr, _("vsnprintf failed: skipping external " fprintf(err_stream, _("vsnprintf failed: skipping external "
"logging function")); "logging function"));
goto log_it; goto log_it;
} }
@ -426,7 +575,7 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
_log_report.object_name, _log_report.object_id, _log_report.object_name, _log_report.object_id,
_log_report.object_group, _log_report.object_group_id, _log_report.object_group, _log_report.object_group_id,
message, _lvm_errno, 0)) message, _lvm_errno, 0))
fprintf(stderr, _("failed to report cmdstatus")); fprintf(err_stream, _("failed to report cmdstatus"));
else else
logged_via_report = 1; logged_via_report = 1;
@ -468,10 +617,10 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
break; break;
/* fall through */ /* fall through */
default: default:
/* Typically only log_warn goes to stdout */ /* Typically only log_warn goes to out_stream */
stream = (use_stderr || (level != _LOG_WARN)) ? stderr : stdout; stream = (use_stderr || (level != _LOG_WARN)) ? err_stream : out_stream;
if (stream == stderr) if (stream == err_stream)
fflush(stdout); fflush(out_stream);
fprintf(stream, "%s%s%s%s", buf, log_command_name(), fprintf(stream, "%s%s%s%s", buf, log_command_name(),
_msg_prefix, indent_spaces); _msg_prefix, indent_spaces);
vfprintf(stream, trformat, ap); vfprintf(stream, trformat, ap);
@ -556,6 +705,7 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
void print_log_libdm(int level, const char *file, int line, int dm_errno_or_class, void print_log_libdm(int level, const char *file, int line, int dm_errno_or_class,
const char *format, ...) const char *format, ...)
{ {
FILE *orig_out_stream = out_stream;
va_list ap; va_list ap;
/* /*
@ -567,9 +717,13 @@ void print_log_libdm(int level, const char *file, int line, int dm_errno_or_clas
((level & ~(_LOG_STDERR|_LOG_ONCE|_LOG_BYPASS_REPORT)) == _LOG_WARN)) ((level & ~(_LOG_STDERR|_LOG_ONCE|_LOG_BYPASS_REPORT)) == _LOG_WARN))
level |= _LOG_BYPASS_REPORT; level |= _LOG_BYPASS_REPORT;
_log_stream.out.stream = report_stream;
va_start(ap, format); va_start(ap, format);
_vprint_log(level, file, line, dm_errno_or_class, format, ap); _vprint_log(level, file, line, dm_errno_or_class, format, ap);
va_end(ap); va_end(ap);
_log_stream.out.stream = orig_out_stream;
} }
log_report_t log_get_report_state(void) log_report_t log_get_report_state(void)

View File

@ -16,6 +16,8 @@
#ifndef _LVM_LOGGING_H #ifndef _LVM_LOGGING_H
#define _LVM_LOGGING_H #define _LVM_LOGGING_H
#include "lvm-file.h"
__attribute__ ((format(printf, 5, 6))) __attribute__ ((format(printf, 5, 6)))
void print_log(int level, const char *file, int line, int dm_errno_or_class, void print_log(int level, const char *file, int line, int dm_errno_or_class,
const char *format, ...); const char *format, ...);
@ -35,6 +37,9 @@ void print_log_libdm(int level, const char *file, int line, int dm_errno_or_clas
#include "log.h" #include "log.h"
int init_custom_log_streams(struct custom_fds *custom_fds);
int reopen_standard_stream(FILE **stream, const char *mode);
typedef void (*lvm2_log_fn_t) (int level, const char *file, int line, typedef void (*lvm2_log_fn_t) (int level, const char *file, int line,
int dm_errno_or_class, const char *message); int dm_errno_or_class, const char *message);

View File

@ -16,6 +16,12 @@
#ifndef _LVM_FILE_H #ifndef _LVM_FILE_H
#define _LVM_FILE_H #define _LVM_FILE_H
struct custom_fds {
int out;
int err;
int report;
};
/* /*
* Create a temporary filename, and opens a descriptor to the file. * Create a temporary filename, and opens a descriptor to the file.
*/ */

View File

@ -748,6 +748,15 @@ All tools return a status code of zero on success or non-zero on failure.
Directory containing \fI.lvm_history\fP if the internal readline Directory containing \fI.lvm_history\fP if the internal readline
shell is invoked. shell is invoked.
.TP .TP
.B LVM_OUT_FD
File descriptor to use for common output from LVM commands.
.TP
.B LVM_ERR_FD
File descriptor to use for error output from LVM commands.
.TP
.B LVM_REPORT_FD
File descriptor to use for report output from LVM commands.
.TP
.B LVM_COMMAND_PROFILE .B LVM_COMMAND_PROFILE
Name of default command profile to use for LVM commands. This profile Name of default command profile to use for LVM commands. This profile
is overriden by direct use of \fB\-\-commandprofile\fP command line option. is overriden by direct use of \fB\-\-commandprofile\fP command line option.

View File

@ -1838,6 +1838,39 @@ static int _check_standard_fds(void)
return 1; return 1;
} }
#define LVM_OUT_FD_ENV_VAR_NAME "LVM_OUT_FD"
#define LVM_ERR_FD_ENV_VAR_NAME "LVM_ERR_FD"
#define LVM_REPORT_FD_ENV_VAR_NAME "LVM_REPORT_FD"
static int _do_get_custom_fd(const char *env_var_name, int *fd)
{
const char *str;
char *endptr;
int tmp_fd;
*fd = -1;
if (!(str = getenv(env_var_name)))
return 1;
errno = 0;
tmp_fd = strtol(str, &endptr, 10);
if (errno || *endptr || (tmp_fd < 0) || (tmp_fd > INT_MAX)) {
log_error("%s: invalid file descriptor.", env_var_name);
return 0;
}
*fd = tmp_fd;
return 1;
}
static int _get_custom_fds(struct custom_fds *custom_fds)
{
return _do_get_custom_fd(LVM_OUT_FD_ENV_VAR_NAME, &custom_fds->out) &&
_do_get_custom_fd(LVM_ERR_FD_ENV_VAR_NAME, &custom_fds->err) &&
_do_get_custom_fd(LVM_REPORT_FD_ENV_VAR_NAME, &custom_fds->report);
}
static const char *_get_cmdline(pid_t pid) static const char *_get_cmdline(pid_t pid)
{ {
static char _proc_cmdline[32]; static char _proc_cmdline[32];
@ -1905,7 +1938,7 @@ static void _close_descriptor(int fd, unsigned suppress_warnings,
fprintf(stderr, " Parent PID %" PRIpid_t ": %s\n", ppid, parent_cmdline); fprintf(stderr, " Parent PID %" PRIpid_t ": %s\n", ppid, parent_cmdline);
} }
static int _close_stray_fds(const char *command) static int _close_stray_fds(const char *command, struct custom_fds *custom_fds)
{ {
#ifndef VALGRIND_POOL #ifndef VALGRIND_POOL
struct rlimit rlim; struct rlimit rlim;
@ -1939,18 +1972,28 @@ static int _close_stray_fds(const char *command)
return 1; return 1;
} }
for (fd = 3; fd < (int)rlim.rlim_cur; fd++) for (fd = 3; fd < (int)rlim.rlim_cur; fd++) {
if ((fd != custom_fds->out) &&
(fd != custom_fds->err) &&
(fd != custom_fds->report)) {
_close_descriptor(fd, suppress_warnings, command, ppid, _close_descriptor(fd, suppress_warnings, command, ppid,
parent_cmdline); parent_cmdline);
}
}
return 1; return 1;
} }
while ((dirent = readdir(d))) { while ((dirent = readdir(d))) {
fd = atoi(dirent->d_name); fd = atoi(dirent->d_name);
if (fd > 2 && fd != dirfd(d)) if ((fd > 2) &&
(fd != dirfd(d)) &&
(fd != custom_fds->out) &&
(fd != custom_fds->err) &&
(fd != custom_fds->report)) {
_close_descriptor(fd, suppress_warnings, _close_descriptor(fd, suppress_warnings,
command, ppid, parent_cmdline); command, ppid, parent_cmdline);
} }
}
if (closedir(d)) if (closedir(d))
log_sys_error("closedir", _fd_dir); log_sys_error("closedir", _fd_dir);
@ -2111,6 +2154,7 @@ int lvm2_main(int argc, char **argv)
{ {
const char *base; const char *base;
int ret, alias = 0; int ret, alias = 0;
struct custom_fds custom_fds;
struct cmd_context *cmd; struct cmd_context *cmd;
if (!argv) if (!argv)
@ -2124,7 +2168,13 @@ int lvm2_main(int argc, char **argv)
if (!_check_standard_fds()) if (!_check_standard_fds())
return -1; return -1;
if (!_close_stray_fds(base)) if (!_get_custom_fds(&custom_fds))
return -1;
if (!_close_stray_fds(base, &custom_fds))
return -1;
if (!init_custom_log_streams(&custom_fds))
return -1; return -1;
if (is_static() && strcmp(base, "lvm.static") && if (is_static() && strcmp(base, "lvm.static") &&