debug: new debug class for kerberos
[kai/samba-autobuild/.git] / lib / util / debug.c
index d96fcc5322f13a1df108ee015a0e090614097d35..d30b1a9fae504b2e7d28c248a6861a2204ee12b8 100644 (file)
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include "includes.h"
+#include "replace.h"
+#include <talloc.h>
 #include "system/filesys.h"
 #include "system/syslog.h"
-#include "lib/util/time_basic.h"
+#include "system/locale.h"
+#include "time_basic.h"
+#include "close_low_fd.h"
+#include "memory.h"
+#include "util_strlist.h" /* LIST_SEP */
+#include "blocking.h"
+#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 +103,405 @@ static struct {
        .fd = 2 /* stderr by default */
 };
 
+#if defined(WITH_SYSLOG) || defined(HAVE_LIBSYSTEMD_JOURNAL) || defined(HAVE_LIBSYSTEMD)
+static int debug_level_to_priority(int level)
+{
+       /*
+        * map debug levels to syslog() priorities
+        */
+       static const int priority_map[] = {
+               LOG_ERR,     /* 0 */
+               LOG_WARNING, /* 1 */
+               LOG_NOTICE,  /* 2 */
+               LOG_NOTICE,  /* 3 */
+               LOG_NOTICE,  /* 4 */
+               LOG_NOTICE,  /* 5 */
+               LOG_INFO,    /* 6 */
+               LOG_INFO,    /* 7 */
+               LOG_INFO,    /* 8 */
+               LOG_INFO,    /* 9 */
+       };
+       int priority;
+
+       if( level >= ARRAY_SIZE(priority_map) || level < 0)
+               priority = LOG_DEBUG;
+       else
+               priority = priority_map[level];
+
+       return priority;
+}
+#endif
+
+/* -------------------------------------------------------------------------- **
+ * Debug backends. When logging to DEBUG_FILE, send the log entries to
+ * all active backends.
+ */
+
+static void debug_file_log(int msg_level,
+                          const char *msg, const char *msg_no_nl)
+{
+       ssize_t ret;
+
+       check_log_size();
+       do {
+               ret = write(state.fd, msg, strlen(msg));
+       } while (ret == -1 && errno == EINTR);
+}
+
+#ifdef WITH_SYSLOG
+static void debug_syslog_reload(bool enabled, bool previously_enabled,
+                               const char *prog_name, char *option)
+{
+       if (enabled && !previously_enabled) {
+#ifdef LOG_DAEMON
+               openlog(prog_name, LOG_PID, SYSLOG_FACILITY);
+#else
+               /* for old systems that have no facility codes. */
+               openlog(prog_name, LOG_PID );
+#endif
+               return;
+       }
+
+       if (!enabled && previously_enabled) {
+               closelog();
+       }
+}
+
+static void debug_syslog_log(int msg_level,
+                            const char *msg, const char *msg_no_nl)
+{
+       int priority;
+
+       priority = debug_level_to_priority(msg_level);
+
+       /*
+        * Specify the facility to interoperate with other syslog
+        * callers (vfs_full_audit for example).
+        */
+       priority |= SYSLOG_FACILITY;
+
+       syslog(priority, "%s", msg);
+}
+#endif /* WITH_SYSLOG */
+
+#if defined(HAVE_LIBSYSTEMD_JOURNAL) || defined(HAVE_LIBSYSTEMD)
+#include <systemd/sd-journal.h>
+static void debug_systemd_log(int msg_level,
+                             const char *msg, const char *msg_no_nl)
+{
+       sd_journal_send("MESSAGE=%s", msg_no_nl,
+                       "PRIORITY=%d", debug_level_to_priority(msg_level),
+                       "LEVEL=%d", msg_level,
+                       NULL);
+}
+#endif
+
+#ifdef HAVE_LTTNG_TRACEF
+#include <lttng/tracef.h>
+static void debug_lttng_log(int msg_level,
+                           const char *msg, const char *msg_no_nl)
+{
+       tracef(msg_no_nl);
+}
+#endif /* WITH_LTTNG_TRACEF */
+
+#ifdef HAVE_GPFS
+#include "gpfswrap.h"
+static void debug_gpfs_reload(bool enabled, bool previously_enabled,
+                             const char *prog_name, char *option)
+{
+       gpfswrap_init();
+
+       if (enabled && !previously_enabled) {
+               gpfswrap_init_trace();
+               return;
+       }
+
+       if (!enabled && previously_enabled) {
+               gpfswrap_fini_trace();
+               return;
+       }
+
+       if (enabled) {
+               /*
+                * Trigger GPFS library to adjust state if necessary.
+                */
+               gpfswrap_query_trace();
+       }
+}
+
+static void debug_gpfs_log(int msg_level,
+                          const char *msg, const char *msg_no_nl)
+{
+       gpfswrap_add_trace(msg_level, msg_no_nl);
+}
+#endif /* HAVE_GPFS */
+
+#define DEBUG_RINGBUF_SIZE (1024 * 1024)
+#define DEBUG_RINGBUF_SIZE_OPT "size="
+
+static char *debug_ringbuf;
+static size_t debug_ringbuf_size;
+static size_t debug_ringbuf_ofs;
+
+/* We ensure in debug_ringbuf_log() that this is always \0 terminated */
+char *debug_get_ringbuf(void)
+{
+       return debug_ringbuf;
+}
+
+/* Return the size of the ringbuf (including a \0 terminator) */
+size_t debug_get_ringbuf_size(void)
+{
+       return debug_ringbuf_size;
+}
+
+static void debug_ringbuf_reload(bool enabled, bool previously_enabled,
+                                const char *prog_name, char *option)
+{
+       bool cmp;
+       size_t optlen = strlen(DEBUG_RINGBUF_SIZE_OPT);
+
+       debug_ringbuf_size = DEBUG_RINGBUF_SIZE;
+       debug_ringbuf_ofs = 0;
+
+       SAFE_FREE(debug_ringbuf);
+
+       if (!enabled) {
+               return;
+       }
+
+       if (option != NULL) {
+               cmp = strncmp(option, DEBUG_RINGBUF_SIZE_OPT, optlen);
+               if (cmp == 0) {
+                       debug_ringbuf_size = (size_t)strtoull(
+                               option + optlen, NULL, 10);
+               }
+       }
+
+       debug_ringbuf = calloc(debug_ringbuf_size, sizeof(char));
+       if (debug_ringbuf == NULL) {
+               return;
+       }
+}
+
+static void debug_ringbuf_log(int msg_level,
+                             const char *msg,
+                             const char *msg_no_nl)
+{
+       size_t msglen = strlen(msg);
+       size_t allowed_size;
+
+       if (debug_ringbuf == NULL) {
+               return;
+       }
+
+       /* Ensure the buffer is always \0 terminated */
+       allowed_size = debug_ringbuf_size - 1;
+
+       if (msglen > allowed_size) {
+               return;
+       }
+
+       if ((debug_ringbuf_ofs + msglen) < debug_ringbuf_ofs) {
+               return;
+       }
+
+       if ((debug_ringbuf_ofs + msglen) > allowed_size) {
+               debug_ringbuf_ofs = 0;
+       }
+
+       memcpy(debug_ringbuf + debug_ringbuf_ofs, msg, msglen);
+       debug_ringbuf_ofs += msglen;
+}
+
+static struct debug_backend {
+       const char *name;
+       int log_level;
+       int new_log_level;
+       void (*reload)(bool enabled, bool prev_enabled,
+                      const char *prog_name, char *option);
+       void (*log)(int msg_level, const char *msg, const char *msg_no_nl);
+       char *option;
+} debug_backends[] = {
+       {
+               .name = "file",
+               .log = debug_file_log,
+       },
+#ifdef WITH_SYSLOG
+       {
+               .name = "syslog",
+               .reload = debug_syslog_reload,
+               .log = debug_syslog_log,
+       },
+#endif
+
+#if defined(HAVE_LIBSYSTEMD_JOURNAL) || defined(HAVE_LIBSYSTEMD)
+       {
+               .name = "systemd",
+               .log = debug_systemd_log,
+       },
+#endif
+
+#ifdef HAVE_LTTNG_TRACEF
+       {
+               .name = "lttng",
+               .log = debug_lttng_log,
+       },
+#endif
+
+#ifdef HAVE_GPFS
+       {
+               .name = "gpfs",
+               .reload = debug_gpfs_reload,
+               .log = debug_gpfs_log,
+       },
+#endif
+       {
+               .name = "ringbuf",
+               .log = debug_ringbuf_log,
+               .reload = debug_ringbuf_reload,
+       },
+};
+
+static struct debug_backend *debug_find_backend(const char *name)
+{
+       unsigned i;
+
+       for (i = 0; i < ARRAY_SIZE(debug_backends); i++) {
+               if (strcmp(name, debug_backends[i].name) == 0) {
+                       return &debug_backends[i];
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * parse "backend[:option][@loglevel]
+ */
+static void debug_backend_parse_token(char *tok)
+{
+       char *backend_name_option, *backend_name,*backend_level, *saveptr;
+       char *backend_option;
+       struct debug_backend *b;
+
+       /*
+        * First parse into backend[:option] and loglevel
+        */
+       backend_name_option = strtok_r(tok, "@\0", &saveptr);
+       if (backend_name_option == NULL) {
+               return;
+       }
+
+       backend_level = strtok_r(NULL, "\0", &saveptr);
+
+       /*
+        * Now parse backend[:option]
+        */
+       backend_name = strtok_r(backend_name_option, ":\0", &saveptr);
+       if (backend_name == NULL) {
+               return;
+       }
+
+       backend_option = strtok_r(NULL, "\0", &saveptr);
+
+       /*
+        * Find and update backend
+        */
+       b = debug_find_backend(backend_name);
+       if (b == NULL) {
+               return;
+       }
+
+       if (backend_level == NULL) {
+               b->new_log_level = MAX_DEBUG_LEVEL;
+       } else {
+               b->new_log_level = atoi(backend_level);
+       }
+
+       if (backend_option != NULL) {
+               b->option = strdup(backend_option);
+               if (b->option == NULL) {
+                       return;
+               }
+       }
+}
+
+/*
+ * parse "backend1[:option1][@loglevel1] backend2[option2][@loglevel2] ... "
+ * and enable/disable backends accordingly
+ */
+static void debug_set_backends(const char *param)
+{
+       size_t str_len = strlen(param);
+       char str[str_len+1];
+       char *tok, *saveptr;
+       unsigned i;
+
+       /*
+        * initialize new_log_level to detect backends that have been
+        * disabled
+        */
+       for (i = 0; i < ARRAY_SIZE(debug_backends); i++) {
+               SAFE_FREE(debug_backends[i].option);
+               debug_backends[i].new_log_level = -1;
+       }
+
+       memcpy(str, param, str_len + 1);
+
+       tok = strtok_r(str, LIST_SEP, &saveptr);
+       if (tok == NULL) {
+               return;
+       }
+
+       while (tok != NULL) {
+               debug_backend_parse_token(tok);
+               tok = strtok_r(NULL, LIST_SEP, &saveptr);
+       }
+
+       /*
+        * Let backends react to config changes
+        */
+       for (i = 0; i < ARRAY_SIZE(debug_backends); i++) {
+               struct debug_backend *b = &debug_backends[i];
+
+               if (b->reload) {
+                       bool enabled = b->new_log_level > -1;
+                       bool previously_enabled = b->log_level > -1;
+
+                       b->reload(enabled, previously_enabled, state.prog_name,
+                                 b->option);
+               }
+               b->log_level = b->new_log_level;
+       }
+}
+
+static void debug_backends_log(const char *msg, int msg_level)
+{
+       char msg_no_nl[FORMAT_BUFR_SIZE];
+       unsigned i;
+       int len;
+
+       /*
+        * Some backends already add an extra newline, so also provide
+        * a buffer without the newline character.
+        */
+       len = MIN(strlen(msg), FORMAT_BUFR_SIZE - 1);
+       if ((len > 0) && (msg[len - 1] == '\n')) {
+               len--;
+       }
+
+       memcpy(msg_no_nl, msg, len);
+       msg_no_nl[len] = '\0';
+
+       for (i = 0; i < ARRAY_SIZE(debug_backends); i++) {
+               if (msg_level <= debug_backends[i].log_level) {
+                       debug_backends[i].log(msg_level, msg, msg_no_nl);
+               }
+       }
+}
+
 /* -------------------------------------------------------------------------- **
  * External variables.
  */
@@ -107,11 +512,41 @@ static struct {
 */
 bool    override_logfile;
 
+static const char *default_classname_table[] = {
+       [DBGC_ALL] =            "all",
+       [DBGC_TDB] =            "tdb",
+       [DBGC_PRINTDRIVERS] =   "printdrivers",
+       [DBGC_LANMAN] =         "lanman",
+       [DBGC_SMB] =            "smb",
+       [DBGC_RPC_PARSE] =      "rpc_parse",
+       [DBGC_RPC_SRV] =        "rpc_srv",
+       [DBGC_RPC_CLI] =        "rpc_cli",
+       [DBGC_PASSDB] =         "passdb",
+       [DBGC_SAM] =            "sam",
+       [DBGC_AUTH] =           "auth",
+       [DBGC_WINBIND] =        "winbind",
+       [DBGC_VFS] =            "vfs",
+       [DBGC_IDMAP] =          "idmap",
+       [DBGC_QUOTA] =          "quota",
+       [DBGC_ACLS] =           "acls",
+       [DBGC_LOCKING] =        "locking",
+       [DBGC_MSDFS] =          "msdfs",
+       [DBGC_DMAPI] =          "dmapi",
+       [DBGC_REGISTRY] =       "registry",
+       [DBGC_SCAVENGER] =      "scavenger",
+       [DBGC_DNS] =            "dns",
+       [DBGC_LDB] =            "ldb",
+       [DBGC_TEVENT] =         "tevent",
+       [DBGC_AUTH_AUDIT] =     "auth_audit",
+       [DBGC_AUTH_AUDIT_JSON] = "auth_json_audit",
+       [DBGC_KERBEROS] =       "kerberos",
+};
+
 /*
  * This is to allow reading of DEBUGLEVEL_CLASS before the debug
  * system has been initialized.
  */
-static const int debug_class_list_initial[DBGC_MAX_FIXED + 1];
+static const int debug_class_list_initial[ARRAY_SIZE(default_classname_table)];
 
 static int debug_num_classes = 0;
 int     *DEBUGLEVEL_CLASS = discard_const_p(int, debug_class_list_initial);
@@ -151,32 +586,6 @@ static bool    log_overflow   = false;
  * 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        */
-       "dmapi",             /* DBGC_DMAPI        */
-       "registry",          /* DBGC_REGISTRY     */
-       "scavenger",         /* DBGC_SCAVENGER    */
-       "dns",               /* DBGC_DNS          */
-       "ldb",               /* DBGC_LDB          */
-       NULL
-};
 
 static char **classname_table = NULL;
 
@@ -193,6 +602,8 @@ static void debug_init(void);
 
 void gfree_debugsyms(void)
 {
+       unsigned i;
+
        TALLOC_FREE(classname_table);
 
        if ( DEBUGLEVEL_CLASS != debug_class_list_initial ) {
@@ -203,6 +614,10 @@ void gfree_debugsyms(void)
        debug_num_classes = 0;
 
        state.initialized = false;
+
+       for (i = 0; i < ARRAY_SIZE(debug_backends); i++) {
+               SAFE_FREE(debug_backends[i].option);
+       }
 }
 
 /****************************************************************************
@@ -215,7 +630,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],
@@ -300,7 +715,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;
 
@@ -338,7 +753,7 @@ static bool debug_parse_param(char *param)
 {
        char *class_name;
        char *class_level;
-       char *saveptr;
+       char *saveptr = NULL;
        int ndx;
 
        class_name = strtok_r(param, ":", &saveptr);
@@ -362,71 +777,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 (; 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 */
@@ -447,7 +850,7 @@ Init debugging (one time stuff)
 
 static void debug_init(void)
 {
-       const char **p;
+       size_t i;
 
        if (state.initialized)
                return;
@@ -456,17 +859,51 @@ static void debug_init(void)
 
        debug_setup_talloc_log();
 
-       for(p = default_classname_table; *p; p++) {
-               debug_add_class(*p);
+       for (i = 0; i < ARRAY_SIZE(default_classname_table); i++) {
+               debug_add_class(default_classname_table[i]);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(debug_backends); i++) {
+               debug_backends[i].log_level = -1;
+               debug_backends[i].new_log_level = -1;
        }
 }
 
-/* This forces in some smb.conf derived values into the debug system.
- * There are no pointers in this structure, so we can just
- * structure-assign it in */
-void debug_set_settings(struct debug_settings *settings)
+void debug_set_settings(struct debug_settings *settings,
+                       const char *logging_param,
+                       int syslog_level, bool syslog_only)
 {
+       char fake_param[256];
+       size_t len = 0;
+
+       /*
+        * This forces in some smb.conf derived values into the debug
+        * system. There are no pointers in this structure, so we can
+        * just structure-assign it in
+        */
        state.settings = *settings;
+
+       /*
+        * If 'logging' is not set, create backend settings from
+        * deprecated 'syslog' and 'syslog only' parameters
+        */
+       if (logging_param != NULL) {
+               len = strlen(logging_param);
+       }
+       if (len == 0) {
+               if (syslog_only) {
+                       snprintf(fake_param, sizeof(fake_param),
+                                "syslog@%d", syslog_level - 1);
+               } else {
+                       snprintf(fake_param, sizeof(fake_param),
+                                "syslog@%d file@%d", syslog_level -1,
+                                MAX_DEBUG_LEVEL);
+               }
+
+               logging_param = fake_param;
+       }
+
+       debug_set_backends(logging_param);
 }
 
 /**
@@ -486,23 +923,15 @@ void setup_logging(const char *prog_name, enum debug_logtype new_logtype)
                state.logtype = new_logtype;
        }
        if (prog_name) {
-               state.prog_name = prog_name;
-       }
-       reopen_logs_internal();
-
-       if (state.logtype == DEBUG_FILE) {
-#ifdef WITH_SYSLOG
                const char *p = strrchr(prog_name, '/');
-               if (p)
+
+               if (p) {
                        prog_name = p + 1;
-#ifdef LOG_DAEMON
-               openlog( prog_name, LOG_PID, SYSLOG_FACILITY );
-#else
-               /* for old systems that have no facility codes. */
-               openlog( prog_name, LOG_PID );
-#endif
-#endif
+               }
+
+               state.prog_name = prog_name;
        }
+       reopen_logs_internal();
 }
 
 /***************************************************************************
@@ -550,6 +979,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
@@ -610,6 +1053,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);
@@ -627,9 +1071,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);
                }
        }
 
@@ -714,18 +1158,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);
                }
        }
 
@@ -743,6 +1185,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 ));
@@ -761,109 +1204,35 @@ void check_log_size( void )
  This is called by dbghdr() and format_debug_text().
 ************************************************************************/
 
-static int Debug1( const char *format_str, ... )
+static void 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 );
-
-               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 );
-               goto done;
-       }
-
-#ifdef WITH_SYSLOG
-       if( !state.settings.syslog_only)
-#endif
-       {
-               if( state.fd <= 0 ) {
-                       mode_t oldumask = umask( 022 );
-                       int fd = open( state.debugf, O_WRONLY|O_APPEND|O_CREAT, 0644 );
-                       (void)umask( oldumask );
-                       if(fd == -1) {
-                               goto done;
-                       }
-                       state.fd = fd;
-               }
-       }
-
-
-#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];
-
-               /*
-                * Specify the facility to interoperate with other syslog
-                * callers (vfs_full_audit for example).
-                */
-               priority |= SYSLOG_FACILITY;
-
-               va_start(ap, format_str);
-               ret = vasprintf(&msgbuf, format_str, ap);
-               va_end(ap);
-
-               if (ret != -1) {
-                       syslog(priority, "%s", msgbuf);
+       switch(state.logtype) {
+       case DEBUG_CALLBACK:
+               debug_callback_log(msg, current_msg_level);
+               break;
+       case DEBUG_STDOUT:
+       case DEBUG_STDERR:
+       case DEBUG_DEFAULT_STDOUT:
+       case DEBUG_DEFAULT_STDERR:
+               if (state.fd > 0) {
+                       ssize_t ret;
+                       do {
+                               ret = write(state.fd, msg, strlen(msg));
+                       } while (ret == -1 && errno == EINTR);
                }
-               SAFE_FREE(msgbuf);
-       }
-#endif
-
-       check_log_size();
-
-#ifdef WITH_SYSLOG
-       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 );
-       }
+               break;
+       case DEBUG_FILE:
+               debug_backends_log(msg, current_msg_level);
+               break;
+       };
 
- done:
        errno = old_errno;
-
-       return( 0 );
 }
 
-
 /**************************************************************************
  Print the buffer content via Debug1(), then reset the buffer.
  Input:  none
@@ -873,7 +1242,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;
 }
 
@@ -908,7 +1277,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. */
@@ -918,7 +1287,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" );
                }
@@ -943,23 +1312,21 @@ void dbgflush( void )
 /***************************************************************************
  Print a Debug Header.
 
- Input:  level - Debug level of the message (not the system-wide debug
-                  level. )
-         cls   - Debuglevel class of the calling module.
-          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.
+ Input:  level    - Debug level of the message (not the system-wide debug
+                    level. )
+         cls      - Debuglevel class of the calling module.
+         location - 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.
 
 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)) )
+ 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 current_msg_level.
+ Notes:  This function takes care of setting current_msg_level.
 
 ****************************************************************************/
 
@@ -968,7 +1335,8 @@ 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[200];
+       char header_str[300];
+       size_t hs_len;
        struct timeval tv;
        struct timeval_buf tvbuf;
 
@@ -1002,48 +1370,68 @@ bool dbghdrclass(int level, int cls, const char *location, const char *func)
        }
 
        GetTimeOfDay(&tv);
-       timeval_str_buf(&tv, state.settings.debug_hires_timestamp, &tvbuf);
+       timeval_str_buf(&tv, false, state.settings.debug_hires_timestamp,
+                       &tvbuf);
 
-       snprintf(header_str, sizeof(header_str), "[%s, %2d",
-                tvbuf.buf, level);
+       hs_len = snprintf(header_str, sizeof(header_str), "[%s, %2d",
+                         tvbuf.buf, level);
+       if (hs_len >= sizeof(header_str)) {
+               goto full;
+       }
 
        if (unlikely(DEBUGLEVEL_CLASS[ cls ] >= 10)) {
                verbose = true;
        }
 
        if (verbose || state.settings.debug_pid) {
-               size_t hs_len = strlen(header_str);
-               snprintf(header_str + hs_len, sizeof(header_str) - hs_len,
-                        ", pid=%u", (unsigned int)getpid());
+               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_uid) {
-               size_t hs_len = strlen(header_str);
-               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());
+               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;
+               }
        }
 
        if ((verbose || state.settings.debug_class)
            && (cls != DBGC_ALL)) {
-               size_t hs_len = strlen(header_str);
-               snprintf(header_str + hs_len,
-                        sizeof(header_str) - hs_len,
-                        ", class=%s",
-                        classname_table[cls]);
+               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;
+               }
        }
 
-       strlcat(header_str, "] ", sizeof(header_str));
+       /*
+        * 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) {
-               size_t hs_len = strlen(header_str);
-               snprintf(header_str + hs_len,
-                        sizeof(header_str) - hs_len,
-                        "%s(%s)\n", location, func);
+               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;
+               }
        }
 
-       (void)Debug1("%s", header_str);
+full:
+       (void)Debug1(header_str);
 
        errno = old_errno;
        return( true );
@@ -1062,17 +1450,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 {
@@ -1081,3 +1466,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;
+}