/*
- Unix SMB/Netbios implementation.
- Version 1.9.
+ Unix SMB/CIFS implementation.
Samba utility functions
Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Elrond 2002
+ Copyright (C) Simo Sorce 2002
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
*
* FORMAT_BUFR_MAX - Index of the last byte of the format buffer;
* format_bufr[FORMAT_BUFR_MAX] should always be reserved
- * for a terminating nul byte.
+ * for a terminating null byte.
*/
#define FORMAT_BUFR_MAX ( sizeof( format_bufr ) - 1 )
*
* dbf - Global debug file handle.
* debugf - Debug file name.
- * append_log - If True, then the output file will be opened in append
- * mode.
- * timestamp_log -
* DEBUGLEVEL - System-wide debug message limit. Messages with message-
* levels higher than DEBUGLEVEL will not be processed.
*/
-FILE *dbf = NULL;
+XFILE *dbf = NULL;
pstring debugf = "";
-BOOL append_log = False;
-BOOL timestamp_log = True;
-int DEBUGLEVEL = 1;
+BOOL debug_warn_unknown_class = True;
+BOOL debug_auto_add_unknown_class = True;
+BOOL AllowDebugChange = True;
+
+/*
+ used to check if the user specified a
+ logfile on the command line
+*/
+BOOL override_logfile;
+
+
+/*
+ * This is to allow assignment to DEBUGLEVEL before the debug
+ * system has been initialised.
+ */
+static int debug_all_class_hack = 1;
+static BOOL debug_all_class_isset_hack = True;
+
+static int debug_num_classes = 0;
+int *DEBUGLEVEL_CLASS = &debug_all_class_hack;
+BOOL *DEBUGLEVEL_CLASS_ISSET = &debug_all_class_isset_hack;
+
+/* DEBUGLEVEL is #defined to *debug_level */
+int DEBUGLEVEL = &debug_all_class_hack;
/* -------------------------------------------------------------------------- **
* to build the formatted output.
*
* format_pos - Marks the first free byte of the format_bufr.
+ *
+ *
+ * log_overflow - When this variable is True, never attempt to check the
+ * size of the log. This is a hack, so that we can write
+ * a message using DEBUG, from open_logs() when we
+ * are unable to open a new log file for some reason.
*/
static BOOL stdout_logging = False;
static int syslog_level = 0;
#endif
static pstring format_bufr = { '\0' };
-static int format_pos = 0;
+static size_t format_pos = 0;
+static BOOL log_overflow = False;
+
+/*
+ * Define all the debug class selection names here. Names *MUST NOT* contain
+ * white space. There must be one name for each DBGC_<class name>, and they
+ * must be in the table in the order of DBGC_<class name>..
+ */
+static const char *default_classname_table[] = {
+ "all", /* DBGC_ALL; index refs traditional DEBUGLEVEL */
+ "tdb", /* DBGC_TDB */
+ "printdrivers", /* DBGC_PRINTDRIVERS */
+ "lanman", /* DBGC_LANMAN */
+ "smb", /* DBGC_SMB */
+ "rpc_parse", /* DBGC_RPC_PARSE */
+ "rpc_srv", /* DBGC_RPC_SRV */
+ "rpc_cli", /* DBGC_RPC_CLI */
+ "passdb", /* DBGC_PASSDB */
+ "sam", /* DBGC_SAM */
+ "auth", /* DBGC_AUTH */
+ "winbind", /* DBGC_WINBIND */
+ "vfs", /* DBGC_VFS */
+ "idmap", /* DBGC_IDMAP */
+ "quota", /* DBGC_QUOTA */
+ "acls", /* DBGC_ACLS */
+ "locking", /* DBGC_LOCKING */
+ "msdfs", /* DBGC_MSDFS */
+ NULL
+};
+
+static char **classname_table = NULL;
/* -------------------------------------------------------------------------- **
* Functions...
*/
-/* ************************************************************************** **
- * tells us if interactive logging was requested
- * ************************************************************************** **
- */
-BOOL dbg_interactive(void)
+
+/****************************************************************************
+utility lists registered debug class names's
+****************************************************************************/
+
+#define MAX_CLASS_NAME_SIZE 1024
+
+static char *debug_list_class_names_and_levels(void)
{
- return stdout_logging;
+ int i, dim;
+ char **list;
+ char *buf = NULL;
+ char *b;
+ BOOL err = False;
+
+ if (DEBUGLEVEL_CLASS == &debug_all_class_hack)
+ return NULL;
+
+ list = SMB_CALLOC_ARRAY(char *, debug_num_classes + 1);
+ if (!list)
+ return NULL;
+
+ /* prepare strings */
+ for (i = 0, dim = 0; i < debug_num_classes; i++) {
+ int l = asprintf(&list[i],
+ "%s:%d ",
+ classname_table[i],
+ DEBUGLEVEL_CLASS_ISSET[i]?DEBUGLEVEL_CLASS[i]:DEBUGLEVEL);
+ if (l < 0 || l > MAX_CLASS_NAME_SIZE) {
+ err = True;
+ goto done;
+ }
+ dim += l;
+ }
+
+ /* create single string list - add space for newline */
+ b = buf = SMB_MALLOC(dim+1);
+ if (!buf) {
+ err = True;
+ goto done;
+ }
+ for (i = 0; i < debug_num_classes; i++) {
+ int l = strlen(list[i]);
+ strncpy(b, list[i], l);
+ b = b + l;
+ }
+ b[-1] = '\n'; /* replace last space with newline */
+ b[0] = '\0'; /* null terminate string */
+
+done:
+ /* free strings list */
+ for (i = 0; i < debug_num_classes; i++)
+ if (list[i]) free(list[i]);
+ free(list);
+
+ if (err) {
+ if (buf)
+ free(buf);
+ return NULL;
+ } else {
+ return buf;
+ }
}
-#if defined(SIGUSR2) && !defined(MEM_MAN)
-/* ************************************************************************** **
- * catch a sigusr2 - decrease the debug log level.
- * ************************************************************************** **
- */
-void sig_usr2( int sig )
- {
- BlockSignals( True, SIGUSR2 );
+/****************************************************************************
+ Utility access to debug class names's.
+****************************************************************************/
- DEBUGLEVEL--;
- if( DEBUGLEVEL < 0 )
- DEBUGLEVEL = 0;
+const char *debug_classname_from_index(int ndx)
+{
+ if (ndx < 0 || ndx >= debug_num_classes)
+ return NULL;
+ else
+ return classname_table[ndx];
+}
- DEBUG( 0, ( "Got SIGUSR2; set debug level to %d.\n", DEBUGLEVEL ) );
+/****************************************************************************
+ Utility to translate names to debug class index's (internal version).
+****************************************************************************/
- BlockSignals( False, SIGUSR2 );
- CatchSignal( SIGUSR2, SIGNAL_CAST sig_usr2 );
+static int debug_lookup_classname_int(const char* classname)
+{
+ int i;
- } /* sig_usr2 */
-#endif /* SIGUSR2 */
+ if (!classname) return -1;
-#if defined(SIGUSR1) && !defined(MEM_MAN)
-/* ************************************************************************** **
- * catch a sigusr1 - increase the debug log level.
- * ************************************************************************** **
- */
-void sig_usr1( int sig )
- {
- BlockSignals( True, SIGUSR1 );
+ for (i=0; i < debug_num_classes; i++) {
+ if (strcmp(classname, classname_table[i])==0)
+ return i;
+ }
+ return -1;
+}
- DEBUGLEVEL++;
+/****************************************************************************
+ Add a new debug class to the system.
+****************************************************************************/
- if( DEBUGLEVEL > 10 )
- DEBUGLEVEL = 10;
+int debug_add_class(const char *classname)
+{
+ int ndx;
+ void *new_ptr;
+
+ if (!classname)
+ return -1;
+
+ /* check the init has yet been called */
+ debug_init();
+
+ ndx = debug_lookup_classname_int(classname);
+ if (ndx >= 0)
+ return ndx;
+ ndx = debug_num_classes;
+
+ new_ptr = DEBUGLEVEL_CLASS;
+ if (DEBUGLEVEL_CLASS == &debug_all_class_hack) {
+ /* Initial loading... */
+ new_ptr = NULL;
+ }
+ new_ptr = SMB_REALLOC_ARRAY(new_ptr, int, debug_num_classes + 1);
+ if (!new_ptr)
+ return -1;
+ DEBUGLEVEL_CLASS = new_ptr;
+ DEBUGLEVEL_CLASS[ndx] = 0;
+
+ /* debug_level is the pointer used for the DEBUGLEVEL-thingy */
+ if (ndx==0) {
+ /* Transfer the initial level from debug_all_class_hack */
+ DEBUGLEVEL_CLASS[ndx] = DEBUGLEVEL;
+ }
+ debug_level = DEBUGLEVEL_CLASS;
+
+ new_ptr = DEBUGLEVEL_CLASS_ISSET;
+ if (new_ptr == &debug_all_class_isset_hack) {
+ new_ptr = NULL;
+ }
+ new_ptr = SMB_REALLOC_ARRAY(new_ptr, BOOL, debug_num_classes + 1);
+ if (!new_ptr)
+ return -1;
+ DEBUGLEVEL_CLASS_ISSET = new_ptr;
+ DEBUGLEVEL_CLASS_ISSET[ndx] = False;
+
+ new_ptr = SMB_REALLOC_ARRAY(classname_table, char *, debug_num_classes + 1);
+ if (!new_ptr)
+ return -1;
+ classname_table = new_ptr;
+
+ classname_table[ndx] = SMB_STRDUP(classname);
+ if (! classname_table[ndx])
+ return -1;
+
+ debug_num_classes++;
+
+ return ndx;
+}
- DEBUG( 0, ( "Got SIGUSR1; set debug level to %d.\n", DEBUGLEVEL ) );
+/****************************************************************************
+ Utility to translate names to debug class index's (public version).
+****************************************************************************/
- BlockSignals( False, SIGUSR1 );
- CatchSignal( SIGUSR1, SIGNAL_CAST sig_usr1 );
+int debug_lookup_classname(const char *classname)
+{
+ int ndx;
+
+ if (!classname || !*classname)
+ return -1;
+
+ ndx = debug_lookup_classname_int(classname);
+
+ if (ndx != -1)
+ return ndx;
+
+ if (debug_warn_unknown_class) {
+ DEBUG(0, ("debug_lookup_classname(%s): Unknown class\n",
+ classname));
+ }
+ if (debug_auto_add_unknown_class) {
+ return debug_add_class(classname);
+ }
+ return -1;
+}
- } /* sig_usr1 */
-#endif /* SIGUSR1 */
+/****************************************************************************
+ Dump the current registered debug levels.
+****************************************************************************/
+static void debug_dump_status(int level)
+{
+ int q;
+
+ DEBUG(level, ("INFO: Current debug levels:\n"));
+ for (q = 0; q < debug_num_classes; q++) {
+ DEBUGADD(level, (" %s: %s/%d\n",
+ classname_table[q],
+ (DEBUGLEVEL_CLASS_ISSET[q]
+ ? "True" : "False"),
+ DEBUGLEVEL_CLASS[q]));
+ }
+}
-/* ************************************************************************** **
- * get ready for syslog stuff
- * ************************************************************************** **
- */
-void setup_logging( char *pname, BOOL interactive )
- {
- if( interactive )
- {
- stdout_logging = True;
- dbf = stdout;
- }
-#ifdef WITH_SYSLOG
- else
- {
- char *p = strrchr( pname,'/' );
+/****************************************************************************
+ parse the debug levels from smbcontrol. Example debug level parameter:
+ printdrivers:7
+****************************************************************************/
+
+static BOOL debug_parse_params(char **params)
+{
+ int i, ndx;
+ char *class_name;
+ char *class_level;
+
+ if (!params)
+ return False;
+
+ /* Allow DBGC_ALL to be specified w/o requiring its class name e.g."10"
+ * v.s. "all:10", this is the traditional way to set DEBUGLEVEL
+ */
+ if (isdigit((int)params[0][0])) {
+ DEBUGLEVEL_CLASS[DBGC_ALL] = atoi(params[0]);
+ DEBUGLEVEL_CLASS_ISSET[DBGC_ALL] = True;
+ i = 1; /* start processing at the next params */
+ } else {
+ i = 0; /* DBGC_ALL not specified OR class name was included */
+ }
+
+ /* Fill in new debug class levels */
+ for (; i < debug_num_classes && params[i]; i++) {
+ if ((class_name=strtok(params[i],":")) &&
+ (class_level=strtok(NULL, "\0")) &&
+ ((ndx = debug_lookup_classname(class_name)) != -1)) {
+ DEBUGLEVEL_CLASS[ndx] = atoi(class_level);
+ DEBUGLEVEL_CLASS_ISSET[ndx] = True;
+ } else {
+ DEBUG(0,("debug_parse_params: unrecognized debug class name or format [%s]\n", params[i]));
+ return False;
+ }
+ }
+
+ return True;
+}
- if( p )
- pname = p + 1;
+/****************************************************************************
+ Parse the debug levels from smb.conf. Example debug level string:
+ 3 tdb:5 printdrivers:7
+ Note: the 1st param has no "name:" preceeding it.
+****************************************************************************/
+
+BOOL debug_parse_levels(const char *params_str)
+{
+ char **params;
+
+ /* Just in case */
+ debug_init();
+
+ if (AllowDebugChange == False)
+ return True;
+
+ params = str_list_make(params_str, NULL);
+
+ if (debug_parse_params(params)) {
+ debug_dump_status(5);
+ str_list_free(¶ms);
+ return True;
+ } else {
+ str_list_free(¶ms);
+ return False;
+ }
+}
+
+/****************************************************************************
+ Receive a "set debug level" message.
+****************************************************************************/
+
+static void debug_message(int msg_type, struct process_id src,
+ void *buf, size_t len)
+{
+ const char *params_str = buf;
+
+ /* Check, it's a proper string! */
+ if (params_str[len-1] != '\0') {
+ DEBUG(1, ("Invalid debug message from pid %u to pid %u\n",
+ (unsigned int)procid_to_pid(&src),
+ (unsigned int)getpid()));
+ return;
+ }
+
+ DEBUG(3, ("INFO: Remote set of debug to `%s' (pid %u from pid %u)\n",
+ params_str, (unsigned int)getpid(),
+ (unsigned int)procid_to_pid(&src)));
+
+ debug_parse_levels(params_str);
+}
+
+/****************************************************************************
+ Send a "set debug level" message.
+****************************************************************************/
+
+void debug_message_send(pid_t pid, const char *params_str)
+{
+ if (!params_str)
+ return;
+ message_send_pid(pid_to_procid(pid), MSG_DEBUG,
+ params_str, strlen(params_str) + 1,
+ False);
+}
+
+/****************************************************************************
+ Return current debug level.
+****************************************************************************/
+
+static void debuglevel_message(int msg_type, struct process_id src,
+ void *buf, size_t len)
+{
+ char *message = debug_list_class_names_and_levels();
+
+ DEBUG(1,("INFO: Received REQ_DEBUGLEVEL message from PID %u\n",
+ (unsigned int)procid_to_pid(&src)));
+ message_send_pid(src, MSG_DEBUGLEVEL, message, strlen(message) + 1, True);
+
+ SAFE_FREE(message);
+}
+
+/****************************************************************************
+Init debugging (one time stuff)
+****************************************************************************/
+
+void debug_init(void)
+{
+ static BOOL initialised = False;
+ const char **p;
+
+ if (initialised)
+ return;
+
+ initialised = True;
+
+ message_register(MSG_DEBUG, debug_message);
+ message_register(MSG_REQ_DEBUGLEVEL, debuglevel_message);
+
+ for(p = default_classname_table; *p; p++) {
+ debug_add_class(*p);
+ }
+}
+
+/***************************************************************************
+ Get ready for syslog stuff
+**************************************************************************/
+
+void setup_logging(const char *pname, BOOL interactive)
+{
+ debug_init();
+
+ /* reset to allow multiple setup calls, going from interactive to
+ non-interactive */
+ stdout_logging = False;
+ if (dbf) {
+ x_fflush(dbf);
+ (void) x_fclose(dbf);
+ }
+
+ dbf = NULL;
+
+ if (interactive) {
+ stdout_logging = True;
+ dbf = x_stdout;
+ x_setbuf( x_stdout, NULL );
+ }
+#ifdef WITH_SYSLOG
+ else {
+ const char *p = strrchr_m( 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 );
+ 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;
+/**************************************************************************
+ reopen the log files
+ note that we now do this unconditionally
+ We attempt to open the new debug fp before closing the old. This means
+ if we run out of fd's we just keep using the old fd rather than aborting.
+ Fix from dgibson@linuxcare.com.
+**************************************************************************/
+
+BOOL reopen_logs( void )
+{
+ pstring fname;
+ mode_t oldumask;
+ XFILE *new_dbf = NULL;
+ XFILE *old_dbf = NULL;
+ BOOL ret = True;
+
+ if (stdout_logging)
+ return True;
+
+ oldumask = umask( 022 );
- if( DEBUGLEVEL > 0 )
- {
- pstrcpy( fname, debugf );
- if( lp_loaded() && (*lp_logfile()) )
- pstrcpy( fname, lp_logfile() );
-
- if( !strcsequal( fname, debugf ) || !dbf || !file_exist( debugf, NULL ) )
- {
- mode_t oldumask = umask( 022 );
-
- pstrcpy( debugf, fname );
- if( dbf )
- (void)fclose( dbf );
- if( append_log )
- dbf = sys_fopen( debugf, "a" );
- else
- dbf = sys_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 );
- (void)umask( oldumask );
- }
- }
- else
- {
- if( dbf )
- {
- (void)fclose( dbf );
- dbf = NULL;
- }
- }
- } /* reopen_logs */
-
-/* ************************************************************************** **
- * Force a check of the log size.
- * ************************************************************************** **
- */
+ pstrcpy(fname, debugf );
+ debugf[0] = '\0';
+
+ if (lp_loaded()) {
+ char *logfname;
+
+ logfname = lp_logfile();
+ if (*logfname)
+ pstrcpy(fname, logfname);
+ }
+
+ pstrcpy( debugf, fname );
+ new_dbf = x_fopen( debugf, O_WRONLY|O_APPEND|O_CREAT, 0644);
+
+ if (!new_dbf) {
+ log_overflow = True;
+ DEBUG(0, ("Unable to open new log file %s: %s\n", debugf, strerror(errno)));
+ log_overflow = False;
+ if (dbf)
+ x_fflush(dbf);
+ ret = False;
+ } else {
+ x_setbuf(new_dbf, NULL);
+ old_dbf = dbf;
+ dbf = new_dbf;
+ if (old_dbf)
+ (void) x_fclose(old_dbf);
+ }
+
+ /* 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();
+ (void)umask(oldumask);
+
+ /* Take over stderr to catch ouput into logs */
+ if (dbf && sys_dup2(x_fileno(dbf), 2) == -1) {
+ close_low_fds(True); /* Close stderr too, if dup2 can't point it
+ at the logfile */
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ Force a check of the log size.
+ ***************************************************************************/
+
void force_check_log_size( void )
- {
- debug_count = 100;
- } /* force_check_log_size */
+{
+ debug_count = 100;
+}
-/* ************************************************************************** **
- * Check to see if the log has grown to be too big.
- * ************************************************************************** **
- */
-static void check_log_size( void )
- {
- int maxlog;
- SMB_STRUCT_STAT st;
-
- if( debug_count++ < 100 || getuid() != 0 )
- return;
-
- maxlog = lp_max_log_size() * 1024;
- if( !dbf || maxlog <= 0 )
- return;
-
- if( sys_fstat( fileno( dbf ), &st ) == 0 && st.st_size > maxlog )
- {
- (void)fclose( dbf );
- dbf = NULL;
- reopen_logs();
- if( dbf && file_size( debugf ) > maxlog )
- {
- pstring name;
-
- (void)fclose( dbf );
- dbf = NULL;
- slprintf( name, sizeof(name)-1, "%s.old", debugf );
- (void)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 HAVE_STDARG_H
- int Debug1( char *format_str, ... )
+/***************************************************************************
+ Check to see if there is any need to check if the logfile has grown too big.
+**************************************************************************/
+
+BOOL need_to_check_log_size( void )
{
-#else
- int Debug1(va_alist)
-va_dcl
-{
- char *format_str;
-#endif
- va_list ap;
- int old_errno = errno;
+ int maxlog;
+
+ if( debug_count < 100 )
+ return( False );
+
+ maxlog = lp_max_log_size() * 1024;
+ if( !dbf || maxlog <= 0 ) {
+ debug_count = 0;
+ return(False);
+ }
+ return( True );
+}
+
+/**************************************************************************
+ Check to see if the log has grown to be too big.
+ **************************************************************************/
+
+void check_log_size( void )
+{
+ int maxlog;
+ SMB_STRUCT_STAT st;
+
+ /*
+ * We need to be root to check/change log-file, skip this and let the main
+ * loop check do a new check as root.
+ */
+
+ if( geteuid() != 0 )
+ return;
+
+ if(log_overflow || !need_to_check_log_size() )
+ return;
+
+ maxlog = lp_max_log_size() * 1024;
+
+ if( sys_fstat( x_fileno( dbf ), &st ) == 0 && st.st_size > maxlog ) {
+ (void)reopen_logs();
+ if( dbf && get_file_size( debugf ) > maxlog ) {
+ pstring name;
+
+ slprintf( name, sizeof(name)-1, "%s.old", debugf );
+ (void)rename( debugf, name );
+
+ if (!reopen_logs()) {
+ /* We failed to reopen a log - continue using the old name. */
+ (void)rename(name, debugf);
+ }
+ }
+ }
+
+ /*
+ * Here's where we need to panic if dbf == NULL..
+ */
+
+ if(dbf == NULL) {
+ /* This code should only be reached in very strange
+ * circumstances. If we merely fail to open the new log we
+ * should stick with the old one. ergo this should only be
+ * reached when opening the logs for the first time: at
+ * startup or when the log level is increased from zero.
+ * -dwg 6 June 2000
+ */
+ dbf = x_fopen( "/dev/console", O_WRONLY, 0);
+ if(dbf) {
+ DEBUG(0,("check_log_size: open of debug file %s failed - using console.\n",
+ debugf ));
+ } else {
+ /*
+ * We cannot continue without a debug file handle.
+ */
+ abort();
+ }
+ }
+ debug_count = 0;
+}
+
+/*************************************************************************
+ Write an debug message on the debugfile.
+ This is called by dbghdr() and format_debug_text().
+************************************************************************/
+
+ int Debug1( const char *format_str, ... )
+{
+ va_list ap;
+ int old_errno = errno;
+
+ debug_count++;
+
+ if( stdout_logging ) {
+ va_start( ap, format_str );
+ if(dbf)
+ (void)x_vfprintf( dbf, format_str, ap );
+ va_end( ap );
+ errno = old_errno;
+ return( 0 );
+ }
+
+ /* prevent recursion by checking if reopen_logs() has temporaily
+ set the debugf string to "" */
+ if( debugf[0] == '\0')
+ return( 0 );
- if( stdout_logging )
- {
-#ifdef HAVE_STDARG_H
- va_start( ap, format_str );
-#else
- va_start( ap );
- format_str = va_arg( ap, char * );
-#endif
- (void)vfprintf( dbf, format_str, ap );
- va_end( ap );
- errno = old_errno;
- return( 0 );
- }
-
#ifdef WITH_SYSLOG
- if( !lp_syslog_only() )
+ if( !lp_syslog_only() )
#endif
- {
- if( !dbf )
- {
- mode_t oldumask = umask( 022 );
-
- if( append_log )
- dbf = sys_fopen( debugf, "a" );
- else
- dbf = sys_fopen( debugf, "w" );
- (void)umask( oldumask );
- if( dbf )
- {
- setbuf( dbf, NULL );
- }
- else
- {
- errno = old_errno;
- return(0);
- }
- }
- }
+ {
+ if( !dbf ) {
+ mode_t oldumask = umask( 022 );
+
+ dbf = x_fopen( debugf, O_WRONLY|O_APPEND|O_CREAT, 0644 );
+ (void)umask( oldumask );
+ if( dbf ) {
+ x_setbuf( dbf, NULL );
+ } else {
+ errno = old_errno;
+ return(0);
+ }
+ }
+ }
#ifdef WITH_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 HAVE_STDARG_H
- 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 );
- }
+ 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];
+
+ va_start( ap, format_str );
+ vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap );
+ va_end( ap );
+
+ msgbuf[255] = '\0';
+ syslog( priority, "%s", msgbuf );
+ }
#endif
+ check_log_size();
+
#ifdef WITH_SYSLOG
- if( !lp_syslog_only() )
+ if( !lp_syslog_only() )
#endif
- {
-#ifdef HAVE_STDARG_H
- va_start( ap, format_str );
-#else
- va_start( ap );
- format_str = va_arg( ap, char * );
-#endif
- (void)vfprintf( dbf, format_str, ap );
- va_end( ap );
- (void)fflush( dbf );
- }
+ {
+ va_start( ap, format_str );
+ if(dbf)
+ (void)x_vfprintf( dbf, format_str, ap );
+ va_end( ap );
+ if(dbf)
+ (void)x_fflush( dbf );
+ }
+
+ errno = old_errno;
+
+ return( 0 );
+}
- check_log_size();
- errno = old_errno;
+/**************************************************************************
+ Print the buffer content via Debug1(), then reset the buffer.
+ Input: none
+ Output: none
+****************************************************************************/
- return( 0 );
- } /* Debug1 */
+static void bufr_print( void )
+{
+ format_bufr[format_pos] = '\0';
+ (void)Debug1( "%s", format_bufr );
+ format_pos = 0;
+}
+/***************************************************************************
+ 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( const char *msg )
+{
+ size_t i;
+ BOOL timestamp = (!stdout_logging && (lp_timestamp_logs() || !(lp_loaded())));
+
+ for( i = 0; msg[i]; i++ ) {
+ /* Indent two spaces at each new line. */
+ if(timestamp && 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 < FORMAT_BUFR_MAX )
+ format_bufr[format_pos++] = msg[i];
+
+ /* If a newline is encountered, print & restart. */
+ if( '\n' == msg[i] )
+ bufr_print();
+
+ /* If the buffer is full dump it out, reset it, and put out a line
+ * continuation indicator.
+ */
+ if( format_pos >= FORMAT_BUFR_MAX ) {
+ bufr_print();
+ (void)Debug1( " +>\n" );
+ }
+ }
+
+ /* Just to be safe... */
+ format_bufr[format_pos] = '\0';
+}
+
+/***************************************************************************
+ Flush debug output, including the format buffer content.
+
+ Input: none
+ Output: none
+***************************************************************************/
-/* ************************************************************************** **
- * Print the buffer content via Debug1(), then reset the buffer.
- *
- * Input: none
- * Output: none
- *
- * ************************************************************************** **
- */
-static void bufr_print( void )
- {
- format_bufr[format_pos] = '\0';
- (void)Debug1( "%s", format_bufr );
- format_pos = 0;
- } /* bufr_print */
-
-/* ************************************************************************** **
- * 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 i;
- BOOL timestamp = (timestamp_log && !stdout_logging && (lp_timestamp_logs() ||
- !(lp_loaded())));
-
- for( i = 0; msg[i]; i++ )
- {
- /* Indent two spaces at each new line. */
- if(timestamp && 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 < FORMAT_BUFR_MAX )
- format_bufr[format_pos++] = msg[i];
-
- /* If a newline is encountered, print & restart. */
- if( '\n' == msg[i] )
- bufr_print();
-
- /* If the buffer is full dump it out, reset it, and put out a line
- * continuation indicator.
- */
- if( format_pos >= FORMAT_BUFR_MAX )
- {
- bufr_print();
- (void)Debug1( " +>\n" );
- }
- }
-
- /* Just to be safe... */
- format_bufr[format_pos] = '\0';
- } /* format_debug_text */
-
-/* ************************************************************************** **
- * Flush debug output, including the format buffer content.
- *
- * Input: none
- * Output: none
- *
- * ************************************************************************** **
- */
void dbgflush( void )
- {
- bufr_print();
- (void)fflush( dbf );
- } /* dbgflush */
+{
+ bufr_print();
+ if(dbf)
+ (void)x_fflush( dbf );
+}
-/* ************************************************************************** **
- * 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 );
- }
+/***************************************************************************
+ Print a Debug Header.
-#ifdef WITH_SYSLOG
- /* Set syslog_level. */
- syslog_level = level;
-#endif
+ 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.
- /* Don't print a header if we're logging to stdout. */
- if( stdout_logging )
- return( True );
+ 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)) )
- /* Print the header if timestamps are turned on. If parameters are
- * not yet loaded, then default to timestamps on.
- */
- if( timestamp_log && (lp_timestamp_logs() || !(lp_loaded()) ))
- {
- /* Print it all out at once to prevent split syslog output. */
- (void)Debug1( "[%s, %d] %s:%s(%d)\n",
- timestring(), level, file, func, line );
- }
+ Notes: This function takes care of setting syslog_level.
- 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 HAVE_STDARG_H
- BOOL dbgtext( char *format_str, ... )
- {
- va_list ap;
- pstring msgbuf;
+BOOL dbghdr( int level, const char *file, const char *func, int line )
+{
+ /* Ensure we don't lose any real errno value. */
+ int old_errno = errno;
+
+ 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 );
+ }
- va_start( ap, format_str );
- vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap );
- va_end( ap );
+#ifdef WITH_SYSLOG
+ /* Set syslog_level. */
+ syslog_level = level;
+#endif
- format_debug_text( msgbuf );
+ /* Don't print a header if we're logging to stdout. */
+ if( stdout_logging )
+ return( True );
+
+ /* Print the header if timestamps are turned on. If parameters are
+ * not yet loaded, then default to timestamps on.
+ */
+ if( lp_timestamp_logs() || !(lp_loaded()) ) {
+ char header_str[200];
+
+ header_str[0] = '\0';
+
+ if( lp_debug_pid())
+ slprintf(header_str,sizeof(header_str)-1,", pid=%u",(unsigned int)sys_getpid());
+
+ if( lp_debug_uid()) {
+ size_t hs_len = strlen(header_str);
+ slprintf(header_str + hs_len,
+ sizeof(header_str) - 1 - hs_len,
+ ", effective(%u, %u), real(%u, %u)",
+ (unsigned int)geteuid(), (unsigned int)getegid(),
+ (unsigned int)getuid(), (unsigned int)getgid());
+ }
+
+ /* Print it all out at once to prevent split syslog output. */
+ (void)Debug1( "[%s, %d%s] %s:%s(%d)\n",
+ timestring(lp_debug_hires_timestamp()), level,
+ header_str, file, func, line );
+ }
+
+ errno = old_errno;
+ return( True );
+}
- return( True );
- } /* dbgtext */
+/***************************************************************************
+ Add text to the body of the "current" debug message via the format buffer.
-#else
- BOOL dbgtext( va_alist )
- va_dcl
- {
- char *format_str;
- va_list ap;
- pstring msgbuf;
+ Input: format_str - Format string, as used in printf(), et. al.
+ ... - Variable argument list.
- va_start( ap );
- format_str = va_arg( ap, char * );
- vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap );
- va_end( ap );
+ ..or.. va_alist - Old style variable parameter list starting point.
- format_debug_text( msgbuf );
+ Output: Always True. See dbghdr() for more info, though this is not
+ likely to be used in the same way.
- return( True );
- } /* dbgtext */
+***************************************************************************/
-#endif
+ BOOL dbgtext( const 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 );
-dbg_Token dbg_char2token( dbg_Token *state, int c )
- /* ************************************************************************ **
- * Parse input one character at a time.
- *
- * Input: state - A pointer to a token variable. This is used to
- * maintain the parser state between calls. For
- * each input stream, you should set up a separate
- * state variable and initialize it to dbg_null.
- * Pass a pointer to it into this function with each
- * character in the input stream. See dbg_test()
- * for an example.
- * c - The "current" character in the input stream.
- *
- * Output: A token.
- * The token value will change when delimiters are found,
- * which indicate a transition between syntactical objects.
- * Possible return values are:
- *
- * dbg_null - The input character was an end-of-line.
- * This resets the parser to its initial state
- * in preparation for parsing the next line.
- * dbg_eof - Same as dbg_null, except that the character
- * was an end-of-file.
- * dbg_ignore - Returned for whitespace and delimiters.
- * These lexical tokens are only of interest
- * to the parser.
- * dbg_header - Indicates the start of a header line. The
- * input character was '[' and was the first on
- * the line.
- * dbg_timestamp - Indicates that the input character was part
- * of a header timestamp.
- * dbg_level - Indicates that the input character was part
- * of the debug-level value in the header.
- * dbg_sourcefile - Indicates that the input character was part
- * of the sourcefile name in the header.
- * dbg_function - Indicates that the input character was part
- * of the function name in the header.
- * dbg_lineno - Indicates that the input character was part
- * of the DEBUG call line number in the header.
- * dbg_message - Indicates that the input character was part
- * of the DEBUG message text.
- *
- * ************************************************************************ **
- */
- {
- /* The terminating characters that we see will greatly depend upon
- * how they are read. For example, if gets() is used instead of
- * fgets(), then we will not see newline characters. A lot also
- * depends on the calling function, which may handle terminators
- * itself.
- *
- * '\n', '\0', and EOF are all considered line terminators. The
- * dbg_eof token is sent back if an EOF is encountered.
- *
- * Warning: only allow the '\0' character to be sent if you are
- * using gets() to read whole lines (thus replacing '\n'
- * with '\0'). Sending '\0' at the wrong time will mess
- * up the parsing.
- */
- switch( c )
- {
- case EOF:
- *state = dbg_null; /* Set state to null (initial state) so */
- return( dbg_eof ); /* that we can restart with new input. */
- case '\n':
- case '\0':
- *state = dbg_null; /* A newline or eoln resets to the null state. */
- return( dbg_null );
- }
-
- /* When within the body of the message, only a line terminator
- * can cause a change of state. We've already checked for line
- * terminators, so if the current state is dbg_msgtxt, simply
- * return that as our current token.
- */
- if( dbg_message == *state )
- return( dbg_message );
-
- /* If we are at the start of a new line, and the input character
- * is an opening bracket, then the line is a header line, otherwise
- * it's a message body line.
- */
- if( dbg_null == *state )
- {
- if( '[' == c )
- {
- *state = dbg_timestamp;
- return( dbg_header );
- }
- *state = dbg_message;
- return( dbg_message );
- }
-
- /* We've taken care of terminators, text blocks and new lines.
- * The remaining possibilities are all within the header line
- * itself.
- */
-
- /* Within the header line, whitespace can be ignored *except*
- * within the timestamp.
- */
- if( isspace( c ) )
- {
- /* Fudge. The timestamp may contain space characters. */
- if( (' ' == c) && (dbg_timestamp == *state) )
- return( dbg_timestamp );
- /* Otherwise, ignore whitespace. */
- return( dbg_ignore );
- }
-
- /* Okay, at this point we know we're somewhere in the header.
- * Valid header *states* are: dbg_timestamp, dbg_level,
- * dbg_sourcefile, dbg_function, and dbg_lineno.
- */
- switch( c )
- {
- case ',':
- if( dbg_timestamp == *state )
- {
- *state = dbg_level;
- return( dbg_ignore );
- }
- break;
- case ']':
- if( dbg_level == *state )
- {
- *state = dbg_sourcefile;
- return( dbg_ignore );
- }
- break;
- case ':':
- if( dbg_sourcefile == *state )
- {
- *state = dbg_function;
- return( dbg_ignore );
- }
- break;
- case '(':
- if( dbg_function == *state )
- {
- *state = dbg_lineno;
- return( dbg_ignore );
- }
- break;
- case ')':
- if( dbg_lineno == *state )
- {
- *state = dbg_null;
- return( dbg_ignore );
- }
- break;
- }
-
- /* If the previous block did not result in a state change, then
- * return the current state as the current token.
- */
- return( *state );
- } /* dbg_char2token */
-
-/* ************************************************************************** */
+ return( True );
+}