@ -7,10 +7,13 @@
*
*
* IDENTIFICATION
* $ Header : / cvsroot / pgsql / src / backend / utils / error / elog . c , v 1.47 1999 / 07 / 17 20 : 18 : 03 momjian Exp $
* $ Header : / cvsroot / pgsql / src / backend / utils / error / elog . c , v 1.48 1999 / 09 / 11 19 : 06 : 31 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
# include "postgres.h"
# include <time.h>
# include <fcntl.h>
# ifndef O_RDONLY
@ -20,8 +23,6 @@
# include <errno.h>
# include <unistd.h>
# include <signal.h>
# include "postgres.h"
# ifdef USE_SYSLOG
# include <syslog.h>
# endif
@ -33,6 +34,10 @@
# include "tcop/tcopprot.h"
# include "utils/trace.h"
extern int errno ;
extern int sys_nerr ;
# ifdef USE_SYSLOG
/*
* Global option to control the use of syslog ( 3 ) for logging :
@ -51,84 +56,198 @@ static int Debugfile = -1;
static int Err_file = - 1 ;
static int ElogDebugIndentLevel = 0 ;
/*
/*--------------------
* elog
* Old error logging function .
* Primary error logging function .
*
* ' lev ' : error level ; indicates recovery action to take , if any .
* ' fmt ' : a printf - style string .
* Additional arguments , if any , are formatted per % - escapes in ' fmt ' .
*
* In addition to the usual % - escapes recognized by printf , " %m " in
* fmt is replaced by the error message for the current value of errno .
*
* Note : no newline is needed at the end of the fmt string , since
* elog will provide one for the output methods that need it .
*
* If ' lev ' is ERROR or worse , control does not return to the caller .
* See elog . h for the error level definitions .
* - - - - - - - - - - - - - - - - - - - -
*/
void
elog ( int lev , const char * fmt , . . . )
elog ( int lev , const char * fmt , . . . )
{
va_list ap ;
char buf [ ELOG_MAXLEN ] ,
line [ ELOG_MAXLEN ] ;
char * bp ;
/*
* The expanded format and final output message are dynamically
* allocated if necessary , but not if they fit in the " reasonable
* size " buffers shown here. In extremis, we'd rather depend on
* having a few hundred bytes of stack space than on malloc ( ) still
* working ( since memory - clobber errors often take out malloc first ) .
* Don ' t make these buffers unreasonably large though , on pain of
* having to chase a bug with no error message .
*/
char fmt_fixedbuf [ 128 ] ;
char msg_fixedbuf [ 256 ] ;
char * fmt_buf = fmt_fixedbuf ;
char * msg_buf = msg_fixedbuf ;
/* this buffer is only used if errno has a bogus value: */
char errorstr_buf [ 32 ] ;
const char * errorstr ;
const char * prefix ;
const char * cp ;
extern int errno ,
sys_nerr ;
char * bp ;
int indent = 0 ;
int space_needed ;
# ifdef USE_SYSLOG
int log_level ;
# endif
int len ;
int i = 0 ;
va_start ( ap , fmt ) ;
if ( lev = = DEBUG & & Debugfile < 0 )
return ;
if ( lev < = DEBUG & & Debugfile < 0 )
return ; /* ignore debug msgs if noplace to send */
/* choose message prefix and indent level */
switch ( lev )
{
case NOIND :
i = ElogDebugIndentLevel - 1 ;
if ( i < 0 )
i = 0 ;
if ( i > 30 )
i = i % 30 ;
c p = " DEBUG: " ;
indent = ElogDebugIndentLevel - 1 ;
if ( indent < 0 )
indent = 0 ;
if ( indent > 30 )
indent = indent % 30 ;
prefix = " DEBUG: " ;
break ;
case DEBUG :
i = ElogDebugIndentLevel ;
if ( i < 0 )
i = 0 ;
if ( i > 30 )
i = i % 30 ;
c p = " DEBUG: " ;
indent = ElogDebugIndentLevel ;
if ( indent < 0 )
indent = 0 ;
if ( indent > 30 )
indent = indent % 30 ;
prefix = " DEBUG: " ;
break ;
case NOTICE :
c p = " NOTICE: " ;
prefix = " NOTICE: " ;
break ;
case ERROR :
c p = " ERROR: " ;
prefix = " ERROR: " ;
break ;
default :
sprintf ( line , " FATAL %d: " , lev ) ;
cp = line ;
/* temporarily use msg buf for prefix */
sprintf ( msg_fixedbuf , " FATAL %d: " , lev ) ;
prefix = msg_fixedbuf ;
break ;
}
/* get errno string for %m */
if ( errno < sys_nerr & & errno > = 0 )
errorstr = strerror ( errno ) ;
else
{
sprintf ( errorstr_buf , " error %d " , errno ) ;
errorstr = errorstr_buf ;
}
/*
* Set up the expanded format , consisting of the prefix string
* plus input format , with any % m replaced by strerror ( ) string
* ( since vsnprintf won ' t know what to do with % m ) . To keep
* space calculation simple , we only allow one % m .
*/
space_needed = TIMESTAMP_SIZE + strlen ( prefix ) + indent
+ strlen ( fmt ) + strlen ( errorstr ) + 1 ;
if ( space_needed > ( int ) sizeof ( fmt_fixedbuf ) )
{
fmt_buf = ( char * ) malloc ( space_needed ) ;
if ( fmt_buf = = NULL )
{
/* We're up against it, convert to fatal out-of-memory error */
fmt_buf = fmt_fixedbuf ;
lev = REALLYFATAL ;
fmt = " elog: out of memory " ; /* this must fit in fmt_fixedbuf! */
}
}
# ifdef ELOG_TIMESTAMPS
strcpy ( buf , tprintf_timestamp ( ) ) ;
strcat ( buf , cp ) ;
strcpy ( fmt_ buf, tprintf_timestamp ( ) ) ;
strcat ( fmt_ buf, prefix ) ;
# else
strcpy ( buf , cp ) ;
strcpy ( fmt_ buf, prefix ) ;
# endif
bp = buf + strlen ( buf ) ;
while ( i - - > 0 )
bp = fmt_ buf + strlen ( fmt_ buf) ;
while ( indent - - > 0 )
* bp + + = ' ' ;
for ( cp = fmt ; * cp ; cp + + )
if ( * cp = = ' % ' & & * ( cp + 1 ) = = ' m ' )
{
if ( errno < sys_nerr & & errno > = 0 )
strcpy ( bp , strerror ( errno ) ) ;
else
sprintf ( bp , " error %d " , errno ) ;
if ( cp [ 0 ] = = ' % ' & & cp [ 1 ] ! = ' \0 ' )
{
if ( cp [ 1 ] = = ' m ' )
{
/* XXX If there are any %'s in errorstr then vsnprintf
* will do the Wrong Thing ; do we need to cope ?
* Seems unlikely that % would appear in system errors .
*/
strcpy ( bp , errorstr ) ;
/* copy the rest of fmt literally, since we can't
* afford to insert another % m .
*/
strcat ( bp , cp + 2 ) ;
bp + = strlen ( bp ) ;
cp + + ;
break ;
}
else
{
/* copy % and next char --- this avoids trouble with %%m */
* bp + + = * cp + + ;
* bp + + = * cp ;
}
}
else
* bp + + = * cp ;
}
* bp = ' \0 ' ;
vsnprintf ( line , ELOG_MAXLEN - 1 , buf , ap ) ;
/*
* Now generate the actual output text using vsnprintf ( ) .
* Be sure to leave space for \ n added later as well as trailing null .
*/
space_needed = sizeof ( msg_fixedbuf ) ;
for ( ; ; )
{
int nprinted ;
va_start ( ap , fmt ) ;
nprinted = vsnprintf ( msg_buf , space_needed - 2 , fmt_buf , ap ) ;
va_end ( ap ) ;
/*
* Note : some versions of vsnprintf return the number of chars
* actually stored , but at least one returns - 1 on failure .
* Be conservative about believing whether the print worked .
*/
if ( nprinted > = 0 & & nprinted < space_needed - 3 )
break ;
/* It didn't work, try to get a bigger buffer */
if ( msg_buf ! = msg_fixedbuf )
free ( msg_buf ) ;
space_needed * = 2 ;
msg_buf = ( char * ) malloc ( space_needed ) ;
if ( msg_buf = = NULL )
{
/* We're up against it, convert to fatal out-of-memory error */
msg_buf = msg_fixedbuf ;
lev = REALLYFATAL ;
# ifdef ELOG_TIMESTAMPS
strcpy ( msg_buf , tprintf_timestamp ( ) ) ;
strcat ( msg_buf , " FATAL: elog: out of memory " ) ;
# else
strcpy ( msg_buf , " FATAL: elog: out of memory " ) ;
# endif
break ;
}
}
/*
* Message prepared ; send it where it should go
*/
# ifdef USE_SYSLOG
switch ( lev )
@ -150,14 +269,16 @@ elog(int lev, const char *fmt,...)
log_level = LOG_ERR ;
break ;
}
write_syslog ( log_level , line + TIMESTAMP_SIZE ) ;
write_syslog ( log_level , msg_buf + TIMESTAMP_SIZE ) ;
# endif
len = strlen ( strcat ( line , " \n " ) ) ;
if ( ( Debugfile > - 1 ) & & ( UseSyslog < = 1 ) )
write ( Debugfile , line , len ) ;
if ( lev = = DEBUG | | lev = = NOIND )
return ;
/* syslog doesn't want a trailing newline, but other destinations do */
strcat ( msg_buf , " \n " ) ;
len = strlen ( msg_buf ) ;
if ( Debugfile > = 0 & & UseSyslog < = 1 )
write ( Debugfile , msg_buf , len ) ;
/*
* If there ' s an error log file other than our channel to the
@ -167,33 +288,31 @@ elog(int lev, const char *fmt,...)
* then writing here can cause this backend to exit without warning
* that is , write ( ) does an exit ( ) . In this case , our only hope of
* finding out what ' s going on is if Err_file was set to some disk
* log . This is a major pain .
* log . This is a major pain . ( It ' s probably also long - dead code . . .
* does anyone still use ultrix ? )
*/
if ( Err_file > - 1 & & Debugfile ! = Err_file & & ( UseSyslog < = 1 ) )
if ( lev > DEBUG & & Err_file > = 0 & &
Debugfile ! = Err_file & & UseSyslog < = 1 )
{
if ( write ( Err_file , line , len ) < 0 )
if ( write ( Err_file , msg_buf , len ) < 0 )
{
write ( open ( " /dev/console " , O_WRONLY , 0666 ) , line , len ) ;
fflush ( stdout ) ;
fflush ( stderr ) ;
proc_exit ( lev ) ;
write ( open ( " /dev/console " , O_WRONLY , 0666 ) , msg_buf , len ) ;
lev = REALLYFATAL ;
}
fsync ( Err_file ) ;
}
# ifndef PG_STANDALONE
/* Send IPC message to the front-end program */
if ( IsUnderPostmaster & & lev > DEBUG )
if ( lev > DEBUG & & IsUnderPostmaster )
{
/* notices are not errors, handle 'em differently */
/* Send IPC message to the front-end program */
char msgtype ;
if ( lev = = NOTICE )
msgtype = ' N ' ;
else
{
/*
* Abort any COPY OUT in progress when an error is detected .
* This hack is necessary because of poor design of copy
@ -203,7 +322,7 @@ elog(int lev, const char *fmt,...)
msgtype = ' E ' ;
}
/* exclude the timestamp from msg sent to frontend */
pq_puttextmessage ( msgtype , line + TIMESTAMP_SIZE ) ;
pq_puttextmessage ( msgtype , msg_buf + TIMESTAMP_SIZE ) ;
/*
* This flush is normally not necessary , since postgres . c will
@ -211,35 +330,45 @@ elog(int lev, const char *fmt,...)
* But it seems best to leave it here , so that the client has some
* clue what happened if the backend dies before getting back to
* the main loop . . . error / notice messages should not be a
* performance - critical path anyway , so an extra flush won ' t hurt
* performance - critical path anyway , so an extra flush won ' t hurt
* much . . .
*/
pq_flush ( ) ;
}
if ( ! IsUnderPostmaster )
{
/*
* There is no socket . One explanation for this is we are running
* as the Postmaster . So we ' ll write the message to stderr .
if ( lev > DEBUG & & ! IsUnderPostmaster )
{
/* We are running as an interactive backend, so just send
* the message to stderr .
*/
fputs ( line , stderr ) ;
fputs ( msg_buf , stderr ) ;
}
# endif /* !PG_STANDALONE */
/* done with the message, release space */
if ( fmt_buf ! = fmt_fixedbuf )
free ( fmt_buf ) ;
if ( msg_buf ! = msg_fixedbuf )
free ( msg_buf ) ;
/*
* Perform error recovery action as specified by lev .
*/
if ( lev = = ERROR )
{
ProcReleaseSpins ( NULL ) ; /* get rid of spinlocks we hold */
if ( ! InError )
if ( InError )
{
/* error reported during error recovery; don't loop forever */
elog ( REALLYFATAL , " elog: error during error recovery, giving up! " ) ;
}
/* exit to main loop */
ProcReleaseSpins ( NULL ) ; /* get rid of spinlocks we hold */
siglongjmp ( Warn_restart , 1 ) ;
}
}
if ( lev = = FATAL )
{
/*
* Assume that if we have detected the failure we can exit with a
* normal exit status . This will prevent the postmaster from
@ -254,13 +383,20 @@ elog(int lev, const char *fmt,...)
if ( lev > FATAL )
{
/*
* Serious crash time . Postmaster will observe nonzero
* process exit status and kill the other backends too .
*/
fflush ( stdout ) ;
fflush ( stderr ) ;
proc_exit ( lev ) ;
}
/* We reach here if lev <= NOTICE. OK to return to caller. */
}
# ifndef PG_STANDALONE
int
DebugFileOpen ( void )
{