debug: Move mapping from level to syslog priority to helper function
[sharpe/samba-autobuild/.git] / lib / util / debug.c
index 73e5c5b485c7dcdad8cb6426c51b85724d78c66e..351a88c5c478e12262b6513e468f9977ca062d87 100644 (file)
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include "includes.h"
+#include <talloc.h>
+#include "replace.h"
 #include "system/filesys.h"
 #include "system/syslog.h"
-#include "lib/util/time.h"
+#include "system/locale.h"
+#include "time_basic.h"
+#include "close_low_fd.h"
+#include "memory.h"
+#include "samba_util.h" /* LIST_SEP */
+#include "debug.h"
 
 /* define what facility to use for syslog */
 #ifndef SYSLOG_FACILITY
 
 /* -------------------------------------------------------------------------- **
  * Defines...
- *
- *  FORMAT_BUFR_MAX - Index of the last byte of the format buffer;
- *                    format_bufr[FORMAT_BUFR_MAX] should always be reserved
- *                    for a terminating null byte.
  */
 
+/*
+ * format_bufr[FORMAT_BUFR_SIZE - 1] should always be reserved
+ * for a terminating null byte.
+ */
 #define FORMAT_BUFR_SIZE 1024
-#define FORMAT_BUFR_MAX (FORMAT_BUFR_SIZE - 1)
 
 /* -------------------------------------------------------------------------- **
  * This module implements Samba's debugging utility.
@@ -97,6 +102,30 @@ static struct {
        .fd = 2 /* stderr by default */
 };
 
+#ifdef WITH_SYSLOG
+static int debug_level_to_priority(int level)
+{
+       /*
+        * map debug levels to syslog() priorities note that not all
+        * DEBUG(0, ...) calls are necessarily errors
+        */
+       static const int priority_map[4] = {
+               LOG_ERR,     /* 0 */
+               LOG_WARNING, /* 1 */
+               LOG_NOTICE,  /* 2 */
+               LOG_INFO,    /* 3 */
+       };
+       int priority;
+
+       if( level >= ARRAY_SIZE(priority_map) || level < 0)
+               priority = LOG_DEBUG;
+       else
+               priority = priority_map[level];
+
+       return priority;
+}
+#endif
+
 /* -------------------------------------------------------------------------- **
  * External variables.
  */
@@ -142,7 +171,7 @@ int     *DEBUGLEVEL_CLASS = discard_const_p(int, debug_class_list_initial);
 
 static int     debug_count    = 0;
 static int     current_msg_level   = 0;
-static char *format_bufr = NULL;
+static char format_bufr[FORMAT_BUFR_SIZE];
 static size_t     format_pos     = 0;
 static bool    log_overflow   = false;
 
@@ -200,8 +229,6 @@ void gfree_debugsyms(void)
                DEBUGLEVEL_CLASS = discard_const_p(int, debug_class_list_initial);
        }
 
-       TALLOC_FREE(format_bufr);
-
        debug_num_classes = 0;
 
        state.initialized = false;
@@ -217,7 +244,7 @@ char *debug_list_class_names_and_levels(void)
        unsigned int i;
        /* prepare strings */
        for (i = 0; i < debug_num_classes; i++) {
-               buf = talloc_asprintf_append(buf, 
+               buf = talloc_asprintf_append(buf,
                                             "%s:%d%s",
                                             classname_table[i],
                                             DEBUGLEVEL_CLASS[i],
@@ -302,7 +329,7 @@ int debug_add_class(const char *classname)
  Utility to translate names to debug class index's (public version).
 ****************************************************************************/
 
-int debug_lookup_classname(const char *classname)
+static int debug_lookup_classname(const char *classname)
 {
        int ndx;
 
@@ -364,71 +391,59 @@ static bool debug_parse_param(char *param)
 }
 
 /****************************************************************************
- parse the debug levels from smbcontrol. Example debug level parameter:
- printdrivers:7
+ 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.
 ****************************************************************************/
 
-static bool debug_parse_params(char **params)
+bool debug_parse_levels(const char *params_str)
 {
-       int   i, ndx;
+       size_t str_len = strlen(params_str);
+       char str[str_len+1];
+       char *tok, *saveptr;
+       int i;
 
-       if (!params)
-               return false;
+       /* Just in case */
+       debug_init();
+
+       memcpy(str, params_str, str_len+1);
+
+       tok = strtok_r(str, LIST_SEP, &saveptr);
+       if (tok == NULL) {
+               return true;
+       }
 
        /* 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]);
-               i = 1; /* start processing at the next params */
+       if (isdigit(tok[0])) {
+               DEBUGLEVEL_CLASS[DBGC_ALL] = atoi(tok);
+               tok = strtok_r(NULL, LIST_SEP, &saveptr);
        } else {
                DEBUGLEVEL_CLASS[DBGC_ALL] = 0;
-               i = 0; /* DBGC_ALL not specified OR class name was included */
        }
 
        /* Array is debug_num_classes long */
-       for (ndx = DBGC_ALL; ndx < debug_num_classes; ndx++) {
-               DEBUGLEVEL_CLASS[ndx] = DEBUGLEVEL_CLASS[DBGC_ALL];
+       for (i = DBGC_ALL+1; i < debug_num_classes; i++) {
+               DEBUGLEVEL_CLASS[i] = DEBUGLEVEL_CLASS[DBGC_ALL];
        }
 
-       /* Fill in new debug class levels */
-       for (; i < debug_num_classes && params[i]; i++) {
+       while (tok != NULL) {
                bool ok;
 
-               ok = debug_parse_param(params[i]);
+               ok = debug_parse_param(tok);
                if (!ok) {
                        DEBUG(0,("debug_parse_params: unrecognized debug "
-                                "class name or format [%s]\n", params[i]));
+                                "class name or format [%s]\n", tok));
                        return false;
                }
-       }
-
-       return true;
-}
 
-/****************************************************************************
- 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();
+               tok = strtok_r(NULL, LIST_SEP, &saveptr);
+       }
 
-       params = str_list_make(NULL, params_str, NULL);
+       debug_dump_status(5);
 
-       if (debug_parse_params(params)) {
-               debug_dump_status(5);
-               TALLOC_FREE(params);
-               return true;
-       } else {
-               TALLOC_FREE(params);
-               return false;
-       }
+       return true;
 }
 
 /* setup for logging of talloc warnings */
@@ -461,10 +476,6 @@ static void debug_init(void)
        for(p = default_classname_table; *p; p++) {
                debug_add_class(*p);
        }
-       format_bufr = talloc_array(NULL, char, FORMAT_BUFR_SIZE);
-       if (!format_bufr) {
-               smb_panic("debug_init: unable to create buffer");
-       }
 }
 
 /* This forces in some smb.conf derived values into the debug system.
@@ -492,20 +503,23 @@ void setup_logging(const char *prog_name, enum debug_logtype new_logtype)
                state.logtype = new_logtype;
        }
        if (prog_name) {
+               const char *p = strrchr(prog_name, '/');
+
+               if (p) {
+                       prog_name = p + 1;
+               }
+
                state.prog_name = prog_name;
        }
        reopen_logs_internal();
 
        if (state.logtype == DEBUG_FILE) {
 #ifdef WITH_SYSLOG
-               const char *p = strrchr(prog_name, '/');
-               if (p)
-                       prog_name = p + 1;
 #ifdef LOG_DAEMON
-               openlog( prog_name, LOG_PID, SYSLOG_FACILITY );
+               openlog(state.prog_name, LOG_PID, SYSLOG_FACILITY );
 #else
                /* for old systems that have no facility codes. */
-               openlog( prog_name, LOG_PID );
+               openlog(state.prog_name, LOG_PID );
 #endif
 #endif
        }
@@ -556,6 +570,20 @@ void debug_set_callback(void *private_ptr, debug_callback_fn fn)
        }
 }
 
+static void debug_callback_log(const char *msg, int msg_level)
+{
+       size_t msg_len = strlen(msg);
+       char msg_copy[msg_len];
+
+       if ((msg_len > 0) && (msg[msg_len-1] == '\n')) {
+               memcpy(msg_copy, msg, msg_len-1);
+               msg_copy[msg_len-1] = '\0';
+               msg = msg_copy;
+       }
+
+       state.callback(state.callback_private, msg_level, msg);
+}
+
 /**************************************************************************
  reopen the log files
  note that we now do this unconditionally
@@ -616,6 +644,7 @@ bool reopen_logs_internal(void)
                log_overflow = false;
                ret = false;
        } else {
+               smb_set_close_on_exec(new_fd);
                old_fd = state.fd;
                state.fd = new_fd;
                debug_close_fd(old_fd);
@@ -633,9 +662,9 @@ bool reopen_logs_internal(void)
                if (dup2(state.fd, 2) == -1) {
                        /* Close stderr too, if dup2 can't point it -
                           at the logfile.  There really isn't much
-                          that can be done on such a fundemental
+                          that can be done on such a fundamental
                           failure... */
-                       close_low_fds(false, false, true);
+                       close_low_fd(2);
                }
        }
 
@@ -720,18 +749,16 @@ void check_log_size( void )
                (void)reopen_logs_internal();
                if (state.fd > 2 && (fstat(state.fd, &st) == 0
                                     && st.st_size > maxlog)) {
-                       char *name = NULL;
-                       
-                       if (asprintf(&name, "%s.old", state.debugf ) < 0) {
-                               return;
-                       }
+                       char name[strlen(state.debugf) + 5];
+
+                       snprintf(name, sizeof(name), "%s.old", state.debugf);
+
                        (void)rename(state.debugf, name);
-                       
+
                        if (!reopen_logs_internal()) {
                                /* We failed to reopen a log - continue using the old name. */
                                (void)rename(name, state.debugf);
                        }
-                       SAFE_FREE(name);
                }
        }
 
@@ -749,6 +776,7 @@ void check_log_size( void )
                 */
                int fd = open( "/dev/console", O_WRONLY, 0);
                if (fd != -1) {
+                       smb_set_close_on_exec(fd);
                        state.fd = fd;
                        DEBUG(0,("check_log_size: open of debug file %s failed - using console.\n",
                                        state.debugf ));
@@ -767,35 +795,21 @@ void check_log_size( void )
  This is called by dbghdr() and format_debug_text().
 ************************************************************************/
 
-static int Debug1( const char *format_str, ... )
+static int Debug1(const char *msg)
 {
-       va_list ap;
        int old_errno = errno;
 
        debug_count++;
 
        if (state.logtype == DEBUG_CALLBACK) {
-               char *msg;
-               int ret;
-               va_start( ap, format_str );
-               ret = vasprintf( &msg, format_str, ap );
-               if (ret != -1) {
-                       if (msg[ret - 1] == '\n') {
-                               msg[ret - 1] = '\0';
-                       }
-                       state.callback(state.callback_private, current_msg_level, msg);
-                       free(msg);
-               }
-               va_end( ap );
-
+               debug_callback_log(msg, current_msg_level);
                goto done;
+       }
 
-       } else if ( state.logtype != DEBUG_FILE ) {
-               va_start( ap, format_str );
-               if (state.fd > 0)
-                       (void)vdprintf( state.fd, format_str, ap );
-               va_end( ap );
-               errno = old_errno;
+       if ( state.logtype != DEBUG_FILE ) {
+               if (state.fd > 0) {
+                       write(state.fd, msg, strlen(msg));
+               }
                goto done;
        }
 
@@ -808,9 +822,9 @@ static int Debug1( const char *format_str, ... )
                        int fd = open( state.debugf, O_WRONLY|O_APPEND|O_CREAT, 0644 );
                        (void)umask( oldumask );
                        if(fd == -1) {
-                               errno = old_errno;
                                goto done;
                        }
+                       smb_set_close_on_exec(fd);
                        state.fd = fd;
                }
        }
@@ -818,23 +832,7 @@ static int Debug1( const char *format_str, ... )
 
 #ifdef WITH_SYSLOG
        if( current_msg_level < state.settings.syslog ) {
-               /* map debug levels to syslog() priorities
-                * note that not all DEBUG(0, ...) calls are
-                * necessarily errors */
-               static const int priority_map[4] = {
-                       LOG_ERR,     /* 0 */
-                       LOG_WARNING, /* 1 */
-                       LOG_NOTICE,  /* 2 */
-                       LOG_INFO,    /* 3 */
-               };
-               int     priority;
-               char *msgbuf = NULL;
-               int ret;
-
-               if( current_msg_level >= ARRAY_SIZE(priority_map) || current_msg_level < 0)
-                       priority = LOG_DEBUG;
-               else
-                       priority = priority_map[current_msg_level];
+               int priority = debug_level_to_priority(current_msg_level);
 
                /*
                 * Specify the facility to interoperate with other syslog
@@ -842,14 +840,7 @@ static int Debug1( const char *format_str, ... )
                 */
                priority |= SYSLOG_FACILITY;
 
-               va_start(ap, format_str);
-               ret = vasprintf(&msgbuf, format_str, ap);
-               va_end(ap);
-
-               if (ret != -1) {
-                       syslog(priority, "%s", msgbuf);
-               }
-               SAFE_FREE(msgbuf);
+               syslog(priority, "%s", msg);
        }
 #endif
 
@@ -859,10 +850,9 @@ static int Debug1( const char *format_str, ... )
        if( !state.settings.syslog_only)
 #endif
        {
-               va_start( ap, format_str );
-               if (state.fd > 0)
-                       (void)vdprintf( state.fd, format_str, ap );
-               va_end( ap );
+               if (state.fd > 0) {
+                       write(state.fd, msg, strlen(msg));
+               }
        }
 
  done:
@@ -881,7 +871,7 @@ static int Debug1( const char *format_str, ... )
 static void bufr_print( void )
 {
        format_bufr[format_pos] = '\0';
-       (void)Debug1( "%s", format_bufr );
+       (void)Debug1(format_bufr);
        format_pos = 0;
 }
 
@@ -906,9 +896,7 @@ static void format_debug_text( const char *msg )
        size_t i;
        bool timestamp = (state.logtype == DEBUG_FILE && (state.settings.timestamp_logs));
 
-       if (!format_bufr) {
-               debug_init();
-       }
+       debug_init();
 
        for( i = 0; msg[i]; i++ ) {
                /* Indent two spaces at each new line. */
@@ -918,7 +906,7 @@ static void format_debug_text( const char *msg )
                }
 
                /* If there's room, copy the character to the format buffer. */
-               if( format_pos < FORMAT_BUFR_MAX )
+               if (format_pos < FORMAT_BUFR_SIZE - 1)
                        format_bufr[format_pos++] = msg[i];
 
                /* If a newline is encountered, print & restart. */
@@ -928,7 +916,7 @@ static void format_debug_text( const char *msg )
                /* If the buffer is full dump it out, reset it, and put out a line
                 * continuation indicator.
                 */
-               if( format_pos >= FORMAT_BUFR_MAX ) {
+               if (format_pos >= FORMAT_BUFR_SIZE - 1) {
                        bufr_print();
                        (void)Debug1( " +>\n" );
                }
@@ -977,6 +965,11 @@ bool dbghdrclass(int level, int cls, const char *location, const char *func)
 {
        /* Ensure we don't lose any real errno value. */
        int old_errno = errno;
+       bool verbose = false;
+       char header_str[300];
+       size_t hs_len;
+       struct timeval tv;
+       struct timeval_buf tvbuf;
 
        if( format_pos ) {
                /* This is a fudge.  If there is stuff sitting in the format_bufr, then
@@ -1002,55 +995,75 @@ bool dbghdrclass(int level, int cls, const char *location, const char *func)
        /* Print the header if timestamps are turned on.  If parameters are
         * not yet loaded, then default to timestamps on.
         */
-       if( state.settings.timestamp_logs || state.settings.debug_prefix_timestamp) {
-               bool verbose = false;
-               char header_str[200];
+       if (!(state.settings.timestamp_logs ||
+             state.settings.debug_prefix_timestamp)) {
+               return true;
+       }
 
-               header_str[0] = '\0';
+       GetTimeOfDay(&tv);
+       timeval_str_buf(&tv, false, state.settings.debug_hires_timestamp,
+                       &tvbuf);
 
-               if (unlikely(DEBUGLEVEL_CLASS[ cls ] >= 10)) {
-                       verbose = true;
-               }
+       hs_len = snprintf(header_str, sizeof(header_str), "[%s, %2d",
+                         tvbuf.buf, level);
+       if (hs_len >= sizeof(header_str)) {
+               goto full;
+       }
 
-               if (verbose || state.settings.debug_pid)
-                       slprintf(header_str,sizeof(header_str)-1,", pid=%u",(unsigned int)getpid());
+       if (unlikely(DEBUGLEVEL_CLASS[ cls ] >= 10)) {
+               verbose = true;
+       }
 
-               if (verbose || state.settings.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());
+       if (verbose || state.settings.debug_pid) {
+               hs_len += snprintf(
+                       header_str + hs_len, sizeof(header_str) - hs_len,
+                       ", pid=%u", (unsigned int)getpid());
+               if (hs_len >= sizeof(header_str)) {
+                       goto full;
                }
+       }
 
-               if ((verbose || state.settings.debug_class)
-                   && (cls != DBGC_ALL)) {
-                       size_t hs_len = strlen(header_str);
-                       slprintf(header_str + hs_len,
-                                sizeof(header_str) -1 - hs_len,
-                                ", class=%s",
-                                classname_table[cls]);
+       if (verbose || state.settings.debug_uid) {
+               hs_len += snprintf(
+                       header_str + hs_len, sizeof(header_str) - hs_len,
+                       ", effective(%u, %u), real(%u, %u)",
+                       (unsigned int)geteuid(), (unsigned int)getegid(),
+                       (unsigned int)getuid(), (unsigned int)getgid());
+               if (hs_len >= sizeof(header_str)) {
+                       goto full;
                }
+       }
 
-               /* Print it all out at once to prevent split syslog output. */
-               if( state.settings.debug_prefix_timestamp ) {
-                       char *time_str = current_timestring(NULL,
-                                                           state.settings.debug_hires_timestamp);
-                       (void)Debug1( "[%s, %2d%s] ",
-                                     time_str,
-                                     level, header_str);
-                       talloc_free(time_str);
-               } else {
-                       char *time_str = current_timestring(NULL,
-                                                           state.settings.debug_hires_timestamp);
-                       (void)Debug1( "[%s, %2d%s] %s(%s)\n",
-                                     time_str,
-                                     level, header_str, location, func );
-                       talloc_free(time_str);
+       if ((verbose || state.settings.debug_class)
+           && (cls != DBGC_ALL)) {
+               hs_len += snprintf(
+                       header_str + hs_len, sizeof(header_str) - hs_len,
+                       ", class=%s", classname_table[cls]);
+               if (hs_len >= sizeof(header_str)) {
+                       goto full;
                }
        }
 
+       /*
+        * No +=, see man man strlcat
+        */
+       hs_len = strlcat(header_str, "] ", sizeof(header_str));
+       if (hs_len >= sizeof(header_str)) {
+               goto full;
+       }
+
+       if (!state.settings.debug_prefix_timestamp) {
+               hs_len += snprintf(
+                       header_str + hs_len, sizeof(header_str) - hs_len,
+                       "%s(%s)\n", location, func);
+               if (hs_len >= sizeof(header_str)) {
+                       goto full;
+               }
+       }
+
+full:
+       (void)Debug1(header_str);
+
        errno = old_errno;
        return( true );
 }
@@ -1068,17 +1081,14 @@ bool dbghdrclass(int level, int cls, const char *location, const char *func)
 
 ***************************************************************************/
 
- bool dbgtext( const char *format_str, ... )
+static inline bool __dbgtext_va(const char *format_str, va_list ap) PRINTF_ATTRIBUTE(1,0);
+static inline bool __dbgtext_va(const char *format_str, va_list ap)
 {
-       va_list ap;
        char *msgbuf = NULL;
        bool ret = true;
        int res;
 
-       va_start(ap, format_str);
        res = vasprintf(&msgbuf, format_str, ap);
-       va_end(ap);
-
        if (res != -1) {
                format_debug_text(msgbuf);
        } else {
@@ -1087,3 +1097,20 @@ bool dbghdrclass(int level, int cls, const char *location, const char *func)
        SAFE_FREE(msgbuf);
        return ret;
 }
+
+bool dbgtext_va(const char *format_str, va_list ap)
+{
+       return __dbgtext_va(format_str, ap);
+}
+
+bool dbgtext(const char *format_str, ... )
+{
+       va_list ap;
+       bool ret;
+
+       va_start(ap, format_str);
+       ret = __dbgtext_va(format_str, ap);
+       va_end(ap);
+
+       return ret;
+}