along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <talloc.h>
#include "replace.h"
+#include <talloc.h>
#include "system/filesys.h"
#include "system/syslog.h"
#include "system/locale.h"
#include "time_basic.h"
#include "close_low_fd.h"
#include "memory.h"
-#include "samba_util.h" /* LIST_SEP */
+#include "util_strlist.h" /* LIST_SEP */
+#include "blocking.h"
#include "debug.h"
/* define what facility to use for syslog */
/* -------------------------------------------------------------------------- **
* 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.
.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.
*/
*/
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);
* 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;
void gfree_debugsyms(void)
{
+ unsigned i;
+
TALLOC_FREE(classname_table);
if ( DEBUGLEVEL_CLASS != debug_class_list_initial ) {
debug_num_classes = 0;
state.initialized = false;
+
+ for (i = 0; i < ARRAY_SIZE(debug_backends); i++) {
+ SAFE_FREE(debug_backends[i].option);
+ }
}
/****************************************************************************
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;
{
char *class_name;
char *class_level;
- char *saveptr;
+ char *saveptr = NULL;
int ndx;
class_name = strtok_r(param, ":", &saveptr);
static void debug_init(void)
{
- const char **p;
+ size_t i;
if (state.initialized)
return;
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);
}
/**
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();
}
/***************************************************************************
}
}
+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
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);
*/
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 ));
This is called by dbghdr() and format_debug_text().
************************************************************************/
-static int Debug1(const char *msg)
+static void Debug1(const char *msg)
{
int old_errno = errno;
debug_count++;
- if (state.logtype == DEBUG_CALLBACK) {
- 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, current_msg_level, msg);
- goto done;
- }
-
- if ( state.logtype != DEBUG_FILE ) {
- if (state.fd > 0) {
- write(state.fd, msg, strlen(msg));
- }
- 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;
-
- 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;
-
- syslog(priority, "%s", msg);
- }
-#endif
-
- check_log_size();
-
-#ifdef WITH_SYSLOG
- if( !state.settings.syslog_only)
-#endif
- {
+ 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) {
- write(state.fd, msg, strlen(msg));
+ ssize_t ret;
+ do {
+ ret = write(state.fd, msg, strlen(msg));
+ } while (ret == -1 && errno == EINTR);
}
- }
+ 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
}
/* 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. */
/* 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" );
}
/***************************************************************************
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.
-
- 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.
+ 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)) )
+
+ Notes: This function takes care of setting current_msg_level.
****************************************************************************/