diff options
-rw-r--r-- | source3/lib/debug.c | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/source3/lib/debug.c b/source3/lib/debug.c new file mode 100644 index 0000000000..527ba05cd4 --- /dev/null +++ b/source3/lib/debug.c @@ -0,0 +1,533 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* -------------------------------------------------------------------------- ** + * This module implements Samba's debugging utility. + * + * The syntax of a debugging log file is represented as: + * + * <debugfile> :== { <debugmsg> } + * + * <debugmsg> :== <debughdr> '\n' <debugtext> + * + * <debughdr> :== '[' TIME ',' LEVEL ']' [ [FILENAME ':'] [FUNCTION '()'] ] + * + * <debugtext> :== { <debugline> } + * + * <debugline> :== TEXT '\n' + * + * TEXT is a string of characters excluding the newline character. + * LEVEL is the DEBUG level of the message (an integer in the range 0..10). + * TIME is a timestamp. + * FILENAME is the name of the file from which the debug message was generated. + * FUNCTION is the function from which the debug message was generated. + * + * Basically, what that all means is: + * + * - A debugging log file is made up of debug messages. + * + * - Each debug message is made up of a header and text. The header is + * separated from the text by a newline. + * + * - The header begins with the timestamp and debug level of the message + * enclosed in brackets. The filename and function from which the + * message was generated may follow. The filename is terminated by a + * colon, and the function name is terminated by parenthesis. + * + * - The message text is made up of zero or more lines, each terminated by + * a newline. + */ + +/* -------------------------------------------------------------------------- ** + * External variables. + * + * dbf - Global debug file handle. + * debugf - Debug file name. + * append_log - If True, then the output file will be opened in append + * mode. + * DEBUGLEVEL - System-wide debug message limit. Messages with message- + * levels higher than DEBUGLEVEL will not be processed. + */ + +FILE *dbf = NULL; +pstring debugf = ""; +BOOL append_log = False; +int DEBUGLEVEL = 1; + + +/* -------------------------------------------------------------------------- ** + * Internal variables. + * + * stdout_logging - Default False, if set to True then dbf will be set to + * stdout and debug output will go to dbf only, and not + * to syslog. Set in setup_logging() and read in Debug1(). + * + * debug_count - Number of debug messages that have been output. + * Used to check log size. + * + * syslog_level - Internal copy of the message debug level. Written by + * dbghdr() and read by Debug1(). + * + * format_bufr - Used to format debug messages. The dbgtext() function + * prints debug messages to a string, and then passes the + * string to format_debug_text(), which uses format_bufr + * to build the formatted output. + * + * format_pos - Marks the first free byte of the format_bufr. + */ + +static BOOL stdout_logging = False; +static int debug_count = 0; +static int syslog_level = 0; +static pstring format_bufr = { '\0' }; +static int format_pos = 0; + + +/* -------------------------------------------------------------------------- ** + * Functions... + */ + +#if defined(SIGUSR2) +/* ************************************************************************** ** + * catch a sigusr2 - decrease the debug log level. + * ************************************************************************** ** + */ +int sig_usr2( void ) + { + BlockSignals( True, SIGUSR2 ); + + DEBUGLEVEL--; + if( DEBUGLEVEL < 0 ) + DEBUGLEVEL = 0; + + DEBUG( 0, ( "Got SIGUSR2; set debug level to %d.\n", DEBUGLEVEL ) ); + + BlockSignals( False, SIGUSR2 ); + +#ifndef DONT_REINSTALL_SIG + signal( SIGUSR2, SIGNAL_CAST sig_usr2 ); +#endif + return( 0 ); + } /* sig_usr2 */ +#endif /* SIGUSR1 */ + +#if defined(SIGUSR1) +/* ************************************************************************** ** + * catch a sigusr1 - increase the debug log level. + * ************************************************************************** ** + */ +int sig_usr1( void ) + { + BlockSignals( True, SIGUSR1 ); + + DEBUGLEVEL++; + + if( DEBUGLEVEL > 10 ) + DEBUGLEVEL = 10; + + DEBUG( 0, ( "Got SIGUSR1; set debug level to %d.\n", DEBUGLEVEL ) ); + + BlockSignals( False, SIGUSR1 ); +#ifndef DONT_REINSTALL_SIG + signal( SIGUSR1, SIGNAL_CAST sig_usr1 ); +#endif + return( 0 ); + } /* sig_usr1 */ +#endif /* SIGUSR1 */ + + +/* ************************************************************************** ** + * get ready for syslog stuff + * ************************************************************************** ** + */ +void setup_logging( char *pname, BOOL interactive ) + { + if( interactive ) + { + stdout_logging = True; + dbf = stdout; + } +#ifdef SYSLOG + else + { + char *p = strrchr( pname,'/' ); + + if( p ) + pname = p + 1; +#ifdef LOG_DAEMON + openlog( pname, LOG_PID, SYSLOG_FACILITY ); +#else /* for old systems that have no facility codes. */ + openlog( pname, LOG_PID ); +#endif + } +#endif + } /* setup_logging */ + +/* ************************************************************************** ** + * reopen the log files + * ************************************************************************** ** + */ +void reopen_logs( void ) + { + pstring fname; + + if( DEBUGLEVEL > 0 ) + { + pstrcpy( fname, debugf ); + if( lp_loaded() && (*lp_logfile()) ) + pstrcpy( fname, lp_logfile() ); + + if( !strcsequal( fname, debugf ) || !dbf || !file_exist( debugf, NULL ) ) + { + int oldumask = umask( 022 ); + + pstrcpy( debugf, fname ); + if( dbf ) + fclose( dbf ); + if( append_log ) + dbf = fopen( debugf, "a" ); + else + dbf = fopen( debugf, "w" ); + /* + * Fix from klausr@ITAP.Physik.Uni-Stuttgart.De + * to fix problem where smbd's that generate less + * than 100 messages keep growing the log. + */ + force_check_log_size(); + if( dbf ) + setbuf( dbf, NULL ); + umask( oldumask ); + } + } + else + { + if( dbf ) + { + fclose( dbf ); + dbf = NULL; + } + } + } /* reopen_logs */ + +/* ************************************************************************** ** + * Force a check of the log size. + * ************************************************************************** ** + */ +void force_check_log_size( void ) + { + debug_count = 100; + } /* force_check_log_size */ + +/* ************************************************************************** ** + * Check to see if the log has grown to be too big. + * ************************************************************************** ** + */ +static void check_log_size( void ) + { + int maxlog; + struct stat st; + + if( debug_count++ < 100 || getuid() != 0) + return; + + maxlog = lp_max_log_size() * 1024; + if( !dbf || maxlog <= 0 ) + return; + + if( fstat( fileno( dbf ), &st ) == 0 && st.st_size > maxlog ) + { + fclose( dbf ); + dbf = NULL; + reopen_logs(); + if( dbf && file_size( debugf ) > maxlog ) + { + pstring name; + + fclose( dbf ); + dbf = NULL; + slprintf( name, sizeof(name)-1, "%s.old", debugf ); + rename( debugf, name ); + reopen_logs(); + } + } + debug_count = 0; + } /* check_log_size */ + +/* ************************************************************************** ** + * Write an debug message on the debugfile. + * This is called by dbghdr() and format_debug_text(). + * ************************************************************************** ** + */ +#ifdef __STDC__ + int Debug1( char *format_str, ... ) +{ +#else + int Debug1(va_alist) +va_dcl +{ + char *format_str; +#endif + va_list ap; + int old_errno = errno; + + if( stdout_logging ) + { +#ifdef __STDC__ + va_start( ap, format_str ); +#else + va_start( ap ); + format_str = va_arg( ap, char * ); +#endif + vfprintf( dbf, format_str, ap ); + va_end( ap ); + errno = old_errno; + return( 0 ); + } + +#ifdef SYSLOG + if( !lp_syslog_only() ) +#endif + { + if( !dbf ) + { + int oldumask = umask( 022 ); + + if( append_log ) + dbf = fopen( debugf, "a" ); + else + dbf = fopen( debugf, "w" ); + umask( oldumask ); + if( dbf ) + { + setbuf( dbf, NULL ); + } + else + { + errno = old_errno; + return(0); + } + } + } + +#ifdef SYSLOG + if( syslog_level < lp_syslog() ) + { + /* map debug levels to syslog() priorities + * note that not all DEBUG(0, ...) calls are + * necessarily errors + */ + static int priority_map[] = { + LOG_ERR, /* 0 */ + LOG_WARNING, /* 1 */ + LOG_NOTICE, /* 2 */ + LOG_INFO, /* 3 */ + }; + int priority; + pstring msgbuf; + + if( syslog_level >= ( sizeof(priority_map) / sizeof(priority_map[0]) ) + || syslog_level < 0) + priority = LOG_DEBUG; + else + priority = priority_map[syslog_level]; + +#ifdef __STDC__ + va_start( ap, format_str ); +#else + va_start( ap ); + format_str = va_arg( ap, char * ); +#endif + vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap ); + va_end( ap ); + + msgbuf[255] = '\0'; + syslog( priority, "%s", msgbuf ); + } +#endif + +#ifdef SYSLOG + if( !lp_syslog_only() ) +#endif + { +#ifdef __STDC__ + va_start( ap, format_str ); +#else + va_start( ap ); + format_str = va_arg( ap, char * ); +#endif + vfprintf( dbf, format_str, ap ); + va_end( ap ); + fflush( dbf ); + } + + check_log_size(); + + errno = old_errno; + + return( 0 ); + } /* Debug1 */ + +/* ************************************************************************** ** + * Format the debug message text. + * + * Input: msg - Text to be added to the "current" debug message text. + * + * Output: none. + * + * Notes: The purpose of this is two-fold. First, each call to syslog() + * (used by Debug1(), see above) generates a new line of syslog + * output. This is fixed by storing the partial lines until the + * newline character is encountered. Second, printing the debug + * message lines when a newline is encountered allows us to add + * spaces, thus indenting the body of the message and making it + * more readable. + * + * ************************************************************************** ** + */ +static void format_debug_text( char *msg ) + { + int max = sizeof( format_bufr ) - 1; + int i; + + for( i = 0; msg[i]; i++ ) + { + /* Indent two spaces at each new line. */ + if( 0 == format_pos ) + { + format_bufr[0] = format_bufr[1] = ' '; + format_pos = 2; + } + + /* If there's room, copy the character to the format buffer. */ + if( format_pos < max ) + format_bufr[format_pos++] = msg[i]; + + /* If a newline is encountered, print & restart. */ + if( '\n' == msg[i] ) + { + format_bufr[format_pos] = '\0'; + Debug1( format_bufr ); + format_pos = 0; + } + } + + /* Just to be safe... */ + format_bufr[format_pos] = '\0'; + } /* format_debug_text */ + +/* ************************************************************************** ** + * Print a Debug Header. + * + * Input: level - Debug level of the message (not the system-wide debug + * level. + * file - Pointer to a string containing the name of the file + * from which this function was called, or an empty string + * if the __FILE__ macro is not implemented. + * func - Pointer to a string containing the name of the function + * from which this function was called, or an empty string + * if the __FUNCTION__ macro is not implemented. + * line - line number of the call to dbghdr, assuming __LINE__ + * works. + * + * Output: Always True. This makes it easy to fudge a call to dbghdr() + * in a macro, since the function can be called as part of a test. + * Eg: ( (level <= DEBUGLEVEL) && (dbghdr(level,"",line)) ) + * + * Notes: This function takes care of setting syslog_level. + * + * ************************************************************************** ** + */ +BOOL dbghdr( int level, char *file, char *func, int line ) + { + if( format_pos ) + { + /* This is a fudge. If there is stuff sitting in the format_bufr, then + * the *right* thing to do is to call + * format_debug_text( "\n" ); + * to write the remainder, and then proceed with the new header. + * Unfortunately, there are several places in the code at which + * the DEBUG() macro is used to build partial lines. That in mind, + * we'll work under the assumption that an incomplete line indicates + * that a new header is *not* desired. + */ + return( True ); + } + + /* Set syslog_level. */ + syslog_level = level; + + /* Print it all out at once. */ + Debug1( "[%s, %d] %s%s%s(%d)\n", + timestring(), level, file, (*file)?":":"", func, line ); + return( True ); + } /* dbghdr */ + +/* ************************************************************************** ** + * Add text to the body of the "current" debug message via the format buffer. + * + * Input: format_str - Format string, as used in printf(), et. al. + * ... - Variable argument list. + * + * ..or.. va_alist - Old style variable parameter list starting point. + * + * Output: Always True. See dbghdr() for more info, though this is not + * likely to be used in the same way. + * + * ************************************************************************** ** + */ +#ifdef __STDC__ + BOOL dbgtext( char *format_str, ... ) + { + va_list ap; + pstring msgbuf; + + va_start( ap, format_str ); + vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap ); + va_end( ap ); + + format_debug_text( msgbuf ); + + return( True ); + } /* dbgtext */ + +#else + BOOL dbgtext( va_alist ) + va_dcl + { + char *format_str; + va_list ap; + pstring msgbuf; + + va_start( ap ); + format_str = va_arg( ap, char * ); + vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap ); + va_end( ap ); + + format_debug_text( msgbuf ); + + return( True ); + } /* dbgtext */ + +#endif + +/* ************************************************************************** */ |