2001-08-21 16:56:08 +04:00
/*
2008-01-30 17:00:02 +03:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All rights reserved .
2001-08-21 16:56:08 +04:00
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2 .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2004-03-30 23:35:44 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2001-08-21 16:56:08 +04:00
*/
2002-11-18 17:01:16 +03:00
# include "lib.h"
2003-07-05 02:34:56 +04:00
# include "device.h"
# include "memlock.h"
2006-05-12 23:16:48 +04:00
# include "defaults.h"
2002-11-18 17:01:16 +03:00
2015-04-20 11:04:18 +03:00
# include <stdio.h>
2001-08-21 16:56:08 +04:00
# include <stdarg.h>
# include <syslog.h>
2015-04-20 20:10:29 +03:00
# include <ctype.h>
2001-08-21 16:56:08 +04:00
2003-07-05 02:34:56 +04:00
static FILE * _log_file ;
2015-04-20 11:04:18 +03:00
static char _log_file_path [ PATH_MAX ] ;
2003-07-05 02:34:56 +04:00
static struct device _log_dev ;
2014-05-29 11:41:03 +04:00
static struct dm_str_list _log_dev_alias ;
2001-08-21 16:56:08 +04:00
static int _syslog = 0 ;
2003-07-05 02:34:56 +04:00
static int _log_to_file = 0 ;
static int _log_direct = 0 ;
static int _log_while_suspended = 0 ;
2002-01-22 18:33:40 +03:00
static int _indent = 1 ;
2002-03-16 01:54:04 +03:00
static int _log_suppress = 0 ;
2002-01-22 18:33:40 +03:00
static char _msg_prefix [ 30 ] = " " ;
2003-07-05 02:34:56 +04:00
static int _already_logging = 0 ;
2015-07-20 17:48:59 +03:00
static int _abort_on_internal_errors_config = 0 ;
2001-08-21 16:56:08 +04:00
2004-03-26 17:47:14 +03:00
static lvm2_log_fn_t _lvm2_log_fn = NULL ;
2009-07-16 03:57:54 +04:00
static int _lvm_errno = 0 ;
static int _store_errmsg = 0 ;
static char * _lvm_errmsg = NULL ;
2011-03-30 16:53:04 +04:00
static size_t _lvm_errmsg_size = 0 ;
static size_t _lvm_errmsg_len = 0 ;
# define MAX_ERRMSG_LEN (512 * 1024) /* Max size of error buffer 512KB */
2009-07-16 03:57:54 +04:00
2004-03-26 17:47:14 +03:00
void init_log_fn ( lvm2_log_fn_t log_fn )
{
if ( log_fn )
_lvm2_log_fn = log_fn ;
else
_lvm2_log_fn = NULL ;
}
2015-04-20 11:04:18 +03:00
/*
* Support envvar LVM_LOG_FILE_EPOCH and allow to attach
2015-04-20 20:10:29 +03:00
* extra keyword ( consist of upto 32 alpha chars ) to
* opened log file . After this ' epoch ' word pid and starttime
* ( in kernel units , read from / proc / self / stat )
2015-04-20 11:04:18 +03:00
* is automatically attached .
* If command / daemon forks multiple times , it could create multiple
2015-04-20 20:10:29 +03:00
* log files ensuring , there are no overwrites .
2015-04-20 11:04:18 +03:00
*/
2003-07-05 02:34:56 +04:00
void init_log_file ( const char * log_file , int append )
2002-03-16 01:54:04 +03:00
{
2015-04-20 11:04:18 +03:00
static const char statfile [ ] = " /proc/self/stat " ;
const char * env ;
int pid ;
2015-08-04 10:33:55 +03:00
unsigned long long starttime ;
2015-04-20 11:04:18 +03:00
FILE * st ;
2015-04-20 20:10:29 +03:00
int i = 0 ;
2015-04-20 11:04:18 +03:00
_log_file_path [ 0 ] = ' \0 ' ;
if ( ( env = getenv ( " LVM_LOG_FILE_EPOCH " ) ) ) {
2015-04-20 20:10:29 +03:00
while ( isalpha ( env [ i ] ) & & i < 32 ) /* Up to 32 alphas */
i + + ;
if ( env [ i ] ) {
if ( i )
log_warn ( " WARNING: Ignoring invalid LVM_LOG_FILE_EPOCH envvar \" %s \" . " , env ) ;
goto no_epoch ;
}
2015-04-20 11:04:18 +03:00
if ( ! ( st = fopen ( statfile , " r " ) ) )
log_sys_error ( " fopen " , statfile ) ;
else if ( fscanf ( st , " %d %*s %*c %*d %*d %*d %*d " /* tty_nr */
" %*d %*u %*u %*u %*u " /* mjflt */
" %*u %*u %*u %*d %*d " /* cstim */
" %*d %*d %*d %*d " /* itrealvalue */
" %llu " , & pid , & starttime ) ! = 2 ) {
log_warn ( " WARNING: Cannot parse content of %s. " , statfile ) ;
} else {
if ( dm_snprintf ( _log_file_path , sizeof ( _log_file_path ) ,
" %s_%s_%d_%lld " , log_file , env , pid , starttime ) < 0 ) {
log_warn ( " WARNING: Debug log file path is too long for epoch. " ) ;
_log_file_path [ 0 ] = ' \0 ' ;
} else {
log_file = _log_file_path ;
append = 1 ; /* force */
}
}
2015-07-08 16:35:37 +03:00
if ( st & & fclose ( st ) )
log_sys_debug ( " fclose " , statfile ) ;
2015-04-20 11:04:18 +03:00
}
2015-07-08 14:56:06 +03:00
2015-04-20 20:10:29 +03:00
no_epoch :
2015-04-20 11:04:18 +03:00
if ( ! ( _log_file = fopen ( log_file , append ? " a " : " w " ) ) ) {
2003-07-05 02:34:56 +04:00
log_sys_error ( " fopen " , log_file ) ;
return ;
}
_log_to_file = 1 ;
}
2015-04-20 11:04:18 +03:00
/*
* Unlink the log file depeding on command ' s return value
*
2015-04-20 20:10:29 +03:00
* When envvar LVM_EXPECTED_EXIT_STATUS is set , compare
2015-04-20 11:04:18 +03:00
* resulting status with this string .
*
* It ' s possible to specify 2 variants - having it equal to
* a single number or having it different from a single number .
*
2015-04-20 20:10:29 +03:00
* i . e . LVM_EXPECTED_EXIT_STATUS = " >1 " # delete when ret > 1.
2015-04-20 11:04:18 +03:00
*/
void unlink_log_file ( int ret )
{
const char * env ;
if ( _log_file_path [ 0 ] & &
2015-04-20 20:10:29 +03:00
( env = getenv ( " LVM_EXPECTED_EXIT_STATUS " ) ) & &
( ( env [ 0 ] = = ' > ' & & ret > atoi ( env + 1 ) ) | |
2015-04-20 11:04:18 +03:00
( atoi ( env ) = = ret ) ) ) {
if ( unlink ( _log_file_path ) )
log_sys_error ( " unlink " , _log_file_path ) ;
_log_file_path [ 0 ] = ' \0 ' ;
}
}
2003-07-05 02:34:56 +04:00
void init_log_direct ( const char * log_file , int append )
{
int open_flags = append ? 0 : O_TRUNC ;
2005-05-03 21:28:23 +04:00
dev_create_file ( log_file , & _log_dev , & _log_dev_alias , 1 ) ;
2003-07-05 02:34:56 +04:00
if ( ! dev_open_flags ( & _log_dev , O_RDWR | O_CREAT | open_flags , 1 , 0 ) )
return ;
_log_direct = 1 ;
}
void init_log_while_suspended ( int log_while_suspended )
{
_log_while_suspended = log_while_suspended ;
2001-08-21 16:56:08 +04:00
}
2002-03-16 01:54:04 +03:00
void init_syslog ( int facility )
{
2001-08-21 16:56:08 +04:00
openlog ( " lvm " , LOG_PID , facility ) ;
_syslog = 1 ;
}
2006-04-13 21:32:24 +04:00
int log_suppress ( int suppress )
2002-03-16 01:54:04 +03:00
{
2006-04-13 21:32:24 +04:00
int old_suppress = _log_suppress ;
2002-03-16 01:54:04 +03:00
_log_suppress = suppress ;
2006-04-13 21:32:24 +04:00
return old_suppress ;
2002-03-16 01:54:04 +03:00
}
2003-07-05 02:34:56 +04:00
void release_log_memory ( void )
2002-03-16 01:54:04 +03:00
{
2004-03-08 21:13:22 +03:00
if ( ! _log_direct )
return ;
2005-10-17 03:03:59 +04:00
dm_free ( ( char * ) _log_dev_alias . str ) ;
2003-07-05 02:34:56 +04:00
_log_dev_alias . str = " activate_log file " ;
}
void fin_log ( void )
{
if ( _log_direct ) {
2012-03-02 01:12:37 +04:00
( void ) dev_close ( & _log_dev ) ;
2003-07-05 02:34:56 +04:00
_log_direct = 0 ;
}
if ( _log_to_file ) {
2007-07-24 21:48:08 +04:00
if ( dm_fclose ( _log_file ) ) {
if ( errno )
fprintf ( stderr , " failed to write log file: %s \n " ,
strerror ( errno ) ) ;
else
fprintf ( stderr , " failed to write log file \n " ) ;
}
2003-07-05 02:34:56 +04:00
_log_to_file = 0 ;
}
2001-08-21 16:56:08 +04:00
}
2011-04-08 18:13:08 +04:00
void fin_syslog ( void )
2002-03-16 01:54:04 +03:00
{
2002-01-19 00:26:37 +03:00
if ( _syslog )
closelog ( ) ;
2001-08-21 16:56:08 +04:00
_syslog = 0 ;
}
2002-03-16 01:54:04 +03:00
void init_msg_prefix ( const char * prefix )
{
2012-02-08 15:05:04 +04:00
strncpy ( _msg_prefix , prefix , sizeof ( _msg_prefix ) - 1 ) ;
2002-01-22 18:33:40 +03:00
_msg_prefix [ sizeof ( _msg_prefix ) - 1 ] = ' \0 ' ;
}
2002-03-16 01:54:04 +03:00
void init_indent ( int indent )
{
2002-01-22 18:33:40 +03:00
_indent = indent ;
}
2015-07-20 17:48:59 +03:00
/* If present, environment setting will override this. */
2009-11-30 20:17:11 +03:00
void init_abort_on_internal_errors ( int fatal )
{
2015-07-20 17:48:59 +03:00
_abort_on_internal_errors_config = fatal ;
2009-11-30 20:17:11 +03:00
}
2009-07-16 03:57:54 +04:00
void reset_lvm_errno ( int store_errmsg )
{
_lvm_errno = 0 ;
if ( _lvm_errmsg ) {
dm_free ( _lvm_errmsg ) ;
_lvm_errmsg = NULL ;
2011-03-30 16:53:04 +04:00
_lvm_errmsg_size = _lvm_errmsg_len = 0 ;
2009-07-16 03:57:54 +04:00
}
_store_errmsg = store_errmsg ;
}
2009-07-16 04:36:59 +04:00
int stored_errno ( void )
2009-07-16 03:57:54 +04:00
{
return _lvm_errno ;
}
2009-07-16 04:36:59 +04:00
const char * stored_errmsg ( void )
2009-07-16 03:57:54 +04:00
{
return _lvm_errmsg ? : " " ;
}
2013-09-26 21:19:18 +04:00
const char * stored_errmsg_with_clear ( void )
{
const char * rc = strdup ( stored_errmsg ( ) ) ;
reset_lvm_errno ( 1 ) ;
return rc ;
}
2010-05-06 02:37:52 +04:00
static struct dm_hash_table * _duplicated = NULL ;
void reset_log_duplicated ( void ) {
2012-03-28 15:11:25 +04:00
if ( _duplicated ) {
2010-05-06 02:37:52 +04:00
dm_hash_destroy ( _duplicated ) ;
2012-03-28 15:11:25 +04:00
_duplicated = NULL ;
}
2010-05-06 02:37:52 +04:00
}
2013-01-08 02:25:19 +04:00
void print_log ( int level , const char * file , int line , int dm_errno_or_class ,
2009-07-16 03:57:54 +04:00
const char * format , . . . )
2002-03-16 01:54:04 +03:00
{
2001-08-21 16:56:08 +04:00
va_list ap ;
2011-10-22 20:52:00 +04:00
char buf [ 1024 ] , locn [ 4096 ] ;
2003-07-05 02:34:56 +04:00
int bufused , n ;
2004-03-26 17:47:14 +03:00
const char * message ;
const char * trformat ; /* Translated format string */
2009-07-16 03:57:54 +04:00
char * newbuf ;
2007-06-28 21:33:44 +04:00
int use_stderr = level & _LOG_STDERR ;
2010-05-06 02:37:52 +04:00
int log_once = level & _LOG_ONCE ;
2010-01-11 23:41:39 +03:00
int fatal_internal_error = 0 ;
2011-03-30 16:53:04 +04:00
size_t msglen ;
2012-03-26 15:50:44 +04:00
const char * indent_spaces = " " ;
FILE * stream ;
2015-07-20 17:48:59 +03:00
static int _abort_on_internal_errors_env_present = - 1 ;
static int _abort_on_internal_errors_env = 0 ;
char * env_str ;
2007-06-28 21:33:44 +04:00
2010-05-06 02:37:52 +04:00
level & = ~ ( _LOG_STDERR | _LOG_ONCE ) ;
2004-02-14 01:56:45 +03:00
2015-07-20 17:48:59 +03:00
if ( _abort_on_internal_errors_env_present < 0 ) {
if ( ( env_str = getenv ( " DM_ABORT_ON_INTERNAL_ERRORS " ) ) ) {
_abort_on_internal_errors_env_present = 1 ;
/* Set when env DM_ABORT_ON_INTERNAL_ERRORS is not "0" */
_abort_on_internal_errors_env = strcmp ( env_str , " 0 " ) ;
} else
_abort_on_internal_errors_env_present = 0 ;
}
/* Use value from environment if present, otherwise use value from config. */
if ( ( ( _abort_on_internal_errors_env_present & & _abort_on_internal_errors_env ) | |
( ! _abort_on_internal_errors_env_present & & _abort_on_internal_errors_config ) ) & &
2013-07-25 16:18:18 +04:00
! strncmp ( format , INTERNAL_ERROR , sizeof ( INTERNAL_ERROR ) - 1 ) ) {
2010-01-11 23:41:39 +03:00
fatal_internal_error = 1 ;
2010-01-11 23:30:32 +03:00
/* Internal errors triggering abort cannot be suppressed. */
2010-01-11 23:41:39 +03:00
_log_suppress = 0 ;
level = _LOG_FATAL ;
}
if ( _log_suppress = = 2 )
2006-04-13 21:32:24 +04:00
return ;
2008-06-06 23:28:35 +04:00
if ( level < = _LOG_ERR )
2008-10-30 20:27:28 +03:00
init_error_message_produced ( 1 ) ;
2008-06-06 23:28:35 +04:00
2004-02-14 01:56:45 +03:00
trformat = _ ( format ) ;
2001-08-21 16:56:08 +04:00
2013-01-08 02:25:19 +04:00
if ( level < _LOG_DEBUG & & dm_errno_or_class & & ! _lvm_errno )
_lvm_errno = dm_errno_or_class ;
2009-07-16 03:57:54 +04:00
2010-05-06 02:37:52 +04:00
if ( _lvm2_log_fn | |
( _store_errmsg & & ( level < = _LOG_ERR ) ) | |
log_once ) {
2004-03-26 17:47:14 +03:00
va_start ( ap , format ) ;
2011-10-22 20:52:00 +04:00
n = vsnprintf ( locn , sizeof ( locn ) - 1 , trformat , ap ) ;
2004-03-26 17:47:14 +03:00
va_end ( ap ) ;
if ( n < 0 ) {
fprintf ( stderr , _ ( " vsnprintf failed: skipping external "
" logging function " ) ) ;
goto log_it ;
}
2011-10-22 20:52:00 +04:00
locn [ sizeof ( locn ) - 1 ] = ' \0 ' ;
2012-03-28 15:11:25 +04:00
message = locn ;
2009-07-16 03:57:54 +04:00
}
2004-03-26 17:47:14 +03:00
2011-04-29 04:21:13 +04:00
/* FIXME Avoid pointless use of message buffer when it'll never be read! */
2011-03-30 16:53:04 +04:00
if ( _store_errmsg & & ( level < = _LOG_ERR ) & &
_lvm_errmsg_len < MAX_ERRMSG_LEN ) {
msglen = strlen ( message ) ;
if ( ( _lvm_errmsg_len + msglen + 1 ) > = _lvm_errmsg_size ) {
_lvm_errmsg_size = 2 * ( _lvm_errmsg_len + msglen + 1 ) ;
if ( ( newbuf = dm_realloc ( _lvm_errmsg ,
_lvm_errmsg_size ) ) )
_lvm_errmsg = newbuf ;
else
_lvm_errmsg_size = _lvm_errmsg_len ;
}
if ( _lvm_errmsg & &
( _lvm_errmsg_len + msglen + 2 ) < _lvm_errmsg_size ) {
/* prepend '\n' and copy with '\0' but do not count in */
if ( _lvm_errmsg_len )
_lvm_errmsg [ _lvm_errmsg_len + + ] = ' \n ' ;
memcpy ( _lvm_errmsg + _lvm_errmsg_len , message , msglen + 1 ) ;
_lvm_errmsg_len + = msglen ;
2009-07-16 03:57:54 +04:00
}
}
2010-05-06 02:37:52 +04:00
if ( log_once ) {
if ( ! _duplicated )
_duplicated = dm_hash_create ( 128 ) ;
if ( _duplicated ) {
if ( dm_hash_lookup ( _duplicated , message ) )
level = _LOG_NOTICE ;
2014-04-07 19:51:42 +04:00
else
( void ) dm_hash_insert ( _duplicated , message , ( void * ) 1 ) ;
2010-05-06 02:37:52 +04:00
}
}
2009-07-16 03:57:54 +04:00
if ( _lvm2_log_fn ) {
_lvm2_log_fn ( level , file , line , 0 , message ) ;
2010-03-23 21:18:49 +03:00
if ( fatal_internal_error )
abort ( ) ;
2004-03-26 17:47:14 +03:00
return ;
}
log_it :
2012-03-26 15:50:44 +04:00
if ( ( verbose_level ( ) > = level ) & & ! _log_suppress ) {
if ( verbose_level ( ) > _LOG_DEBUG ) {
( void ) dm_snprintf ( buf , sizeof ( buf ) , " #%s:%d " ,
2011-08-11 23:21:42 +04:00
file , line ) ;
2012-03-26 15:50:44 +04:00
} else
buf [ 0 ] = ' \0 ' ;
if ( _indent )
switch ( level ) {
case _LOG_NOTICE : indent_spaces = " " ; break ;
case _LOG_INFO : indent_spaces = " " ; break ;
case _LOG_DEBUG : indent_spaces = " " ; break ;
default : /* nothing to do */ ;
}
2004-12-10 19:01:35 +03:00
2002-03-16 01:54:04 +03:00
va_start ( ap , format ) ;
switch ( level ) {
case _LOG_DEBUG :
2013-01-08 02:25:19 +04:00
if ( verbose_level ( ) < _LOG_DEBUG )
break ;
if ( ! debug_class_is_logged ( dm_errno_or_class ) )
break ;
2015-04-13 12:39:23 +03:00
if ( ( verbose_level ( ) = = level ) & &
( strcmp ( " <backtrace> " , format ) = = 0 ) )
break ;
2012-03-26 15:50:44 +04:00
/* fall through */
2002-03-16 01:54:04 +03:00
default :
2012-03-26 15:50:44 +04:00
/* Typically only log_warn goes to stdout */
stream = ( use_stderr | | ( level ! = _LOG_WARN ) ) ? stderr : stdout ;
2015-06-19 17:24:32 +03:00
if ( stream = = stderr )
fflush ( stdout ) ;
2012-03-26 15:50:44 +04:00
fprintf ( stream , " %s%s%s%s " , buf , log_command_name ( ) ,
_msg_prefix , indent_spaces ) ;
vfprintf ( stream , trformat , ap ) ;
fputc ( ' \n ' , stream ) ;
2001-08-21 16:56:08 +04:00
}
2002-03-16 01:54:04 +03:00
va_end ( ap ) ;
2001-08-21 16:56:08 +04:00
}
2013-03-28 16:33:20 +04:00
if ( ( level > debug_level ( ) ) | |
( level > = _LOG_DEBUG & & ! debug_class_is_logged ( dm_errno_or_class ) ) ) {
if ( fatal_internal_error )
abort ( ) ;
2013-01-08 02:25:19 +04:00
return ;
2013-03-28 16:33:20 +04:00
}
2013-01-08 02:25:19 +04:00
2011-02-18 17:16:11 +03:00
if ( _log_to_file & & ( _log_while_suspended | | ! critical_section ( ) ) ) {
2008-10-30 20:27:28 +03:00
fprintf ( _log_file , " %s:%d %s%s " , file , line , log_command_name ( ) ,
2003-07-05 02:34:56 +04:00
_msg_prefix ) ;
2001-08-21 16:56:08 +04:00
va_start ( ap , format ) ;
2004-02-14 01:56:45 +03:00
vfprintf ( _log_file , trformat , ap ) ;
2001-08-21 16:56:08 +04:00
va_end ( ap ) ;
2015-04-13 12:39:23 +03:00
fputc ( ' \n ' , _log_file ) ;
2003-07-05 02:34:56 +04:00
fflush ( _log_file ) ;
2001-08-21 16:56:08 +04:00
}
2011-02-18 17:16:11 +03:00
if ( _syslog & & ( _log_while_suspended | | ! critical_section ( ) ) ) {
2002-03-16 01:54:04 +03:00
va_start ( ap , format ) ;
2004-02-14 01:56:45 +03:00
vsyslog ( level , trformat , ap ) ;
2001-08-21 16:56:08 +04:00
va_end ( ap ) ;
}
2003-07-05 02:34:56 +04:00
2012-12-14 16:57:01 +04:00
if ( fatal_internal_error )
abort ( ) ;
2003-07-05 02:34:56 +04:00
/* FIXME This code is unfinished - pre-extend & condense. */
2011-02-18 17:16:11 +03:00
if ( ! _already_logging & & _log_direct & & critical_section ( ) ) {
2003-07-05 02:34:56 +04:00
_already_logging = 1 ;
memset ( & buf , ' ' , sizeof ( buf ) ) ;
bufused = 0 ;
2010-10-26 12:53:25 +04:00
if ( ( n = dm_snprintf ( buf , sizeof ( buf ) - 1 ,
2008-10-30 20:27:28 +03:00
" %s:%d %s%s " , file , line , log_command_name ( ) ,
2003-07-05 02:34:56 +04:00
_msg_prefix ) ) = = - 1 )
goto done ;
bufused + = n ;
va_start ( ap , format ) ;
n = vsnprintf ( buf + bufused - 1 , sizeof ( buf ) - bufused - 1 ,
2004-02-14 01:56:45 +03:00
trformat , ap ) ;
2003-07-05 02:34:56 +04:00
va_end ( ap ) ;
bufused + = n ;
buf [ bufused - 1 ] = ' \n ' ;
2012-02-08 15:31:29 +04:00
done :
2003-07-05 02:34:56 +04:00
buf [ bufused ] = ' \n ' ;
buf [ sizeof ( buf ) - 1 ] = ' \n ' ;
/* FIXME real size bufused */
dev_append ( & _log_dev , sizeof ( buf ) , buf ) ;
_already_logging = 0 ;
}
2001-08-21 16:56:08 +04:00
}