* University of Maryland at College Park
*/
/*
- * $Id: debug.c,v 1.36 2006/01/12 01:57:05 paddy_s Exp $
+ * $Id: debug.c,v 1.40 2006/07/26 11:49:32 martinea Exp $
*
- * debug log subroutines
+ * Logging support
*/
#include "amanda.h"
#include "util.h"
#include "arglist.h"
#include "clock.h"
+#include "timestamp.h"
+#include "conffile.h"
-#ifndef AMANDA_DBGDIR
-# define AMANDA_DBGDIR AMANDA_TMPDIR
+#ifdef HAVE_GLIBC_BACKTRACE
+#include <execinfo.h>
#endif
-#ifdef DEBUG_CODE
+/* Minimum file descriptor on which to keep the debug file. This is intended
+ * to keep the descriptor "out of the way" of other processing. It's not clear
+ * that this is required any longer, but it doesn't hurt anything.
+ */
+#define MIN_DB_FD 10
-int debug = 1;
+/* information on the current debug file */
+static int db_fd = 2; /* file descriptor (default stderr) */
+static FILE *db_file = NULL; /* stdio stream */
+static char *db_name = NULL; /* unqualified filename */
+static char *db_filename = NULL; /* fully qualified pathname */
-#define MIN_DB_FD 10
+/* directory containing debug file, including trailing slash */
+static char *dbgdir = NULL;
-static int db_fd = 2; /* default is stderr */
-static FILE *db_file = NULL; /* stderr may not be a constant */
-static char *db_filename = NULL;
+/* time debug log was opened (timestamp of the file) */
+static time_t open_time;
-static pid_t debug_prefix_pid = 0;
+/* storage for global variables */
+int error_exit_status = 1;
-/*
- * Format and write a debug message to the process debug file.
- */
-printf_arglist_function(void debug_printf, const char *, format)
-{
- va_list argp;
- int save_errno;
+/* static function prototypes */
+static char *get_debug_name(time_t t, int n);
+static void debug_unlink_old(void);
+static void debug_setup_1(char *config, char *subdir);
+static void debug_setup_2(char *s, int fd, char *annotation);
+static char *msg_timestamp(void);
+static char *msg_thread(void);
- /*
- * It is common in the code to call dbprintf to write out
- * syserrno(errno) and then turn around and try to do something else
- * with errno (e.g. printf() or log()), so we make sure errno goes
- * back out with the same value it came in with.
- */
- save_errno = errno;
+static void debug_logging_handler(const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data);
+static void debug_setup_logging(void);
- if(db_file == NULL && db_fd == 2) {
- db_file = stderr;
- }
- if(db_file != NULL) {
- arglist_start(argp, format);
- vfprintf(db_file, format, argp);
- fflush(db_file);
- arglist_end(argp);
- }
+/* By default, do not suppress tracebacks */
+static gboolean do_suppress_error_traceback = FALSE;
- errno = save_errno;
-}
+/* configured amanda_log_handlers */
+static GSList *amanda_log_handlers = NULL;
/*
* Generate a debug file name. The name is based on the program name,
* followed by a timestamp, an optional sequence number, and ".debug".
+ *
+ * @param t: timestamp
+ * @param n: sequence number between 1 and 1000; if zero, no sequence number
+ * is included.
*/
static char *
-get_debug_name(t, n)
- time_t t;
- int n;
+get_debug_name(
+ time_t t,
+ int n)
{
char number[NUM_STR_SIZE];
char *ts;
if(n < 0 || n > 1000) {
return NULL;
}
- ts = construct_timestamp(&t);
+ ts = get_timestamp_from_time(t);
if(n == 0) {
number[0] = '\0';
} else {
- snprintf(number, sizeof(number), "%03d", n - 1);
+ g_snprintf(number, SIZEOF(number), "%03d", n - 1);
}
result = vstralloc(get_pname(), ".", ts, number, ".debug", NULL);
amfree(ts);
return result;
}
-static char *dbgdir = NULL;
-static time_t curtime;
+/* Call this to suppress tracebacks on error() or g_critical(). This is used
+ * when a critical error is indicated in perl, and the traceback will not be
+ * useful. */
+void
+suppress_error_traceback(void)
+{
+ do_suppress_error_traceback = 1;
+}
+
+/* A GLogFunc to handle g_log calls. This function assumes that user_data
+ * is either NULL or a pointer to one of the debug_* configuration variables
+ * in conffile.c, indicating whether logging for this log domain is enabled.
+ *
+ * @param log_domain: the log domain, or NULL for general logging
+ * @param log_level: level, fatality, and recursion flags
+ * @param message: the message to log
+ * @param user_pointer: unused
+ */
+static void
+debug_logging_handler(const gchar *log_domain G_GNUC_UNUSED,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data G_GNUC_UNUSED)
+{
+ GLogLevelFlags maxlevel;
+ char *levprefix = NULL;
+ pcontext_t context = get_pcontext();
+
+ /* glib allows a message to have multiple levels, so calculate the "worst"
+ * level */
+ if (log_level & G_LOG_LEVEL_ERROR) {
+ maxlevel = G_LOG_LEVEL_ERROR;
+ levprefix = _("error (fatal): ");
+ } else if (log_level & G_LOG_LEVEL_CRITICAL) {
+ maxlevel = G_LOG_LEVEL_CRITICAL;
+ levprefix = _("critical (fatal): ");
+ } else if (log_level & G_LOG_LEVEL_WARNING) {
+ maxlevel = G_LOG_LEVEL_WARNING;
+ levprefix = _("warning: ");
+ } else if (log_level & G_LOG_LEVEL_MESSAGE) {
+ maxlevel = G_LOG_LEVEL_MESSAGE;
+ levprefix = _("message: ");
+ } else if (log_level & G_LOG_LEVEL_INFO) {
+ maxlevel = G_LOG_LEVEL_INFO;
+ levprefix = _("info: ");
+ } else {
+ maxlevel = G_LOG_LEVEL_DEBUG;
+ levprefix = ""; /* no level displayed for debugging */
+ }
-static void debug_setup_1()
+ /* scriptutil context doesn't do any logging except for critical
+ * and error levels */
+ if (context != CONTEXT_SCRIPTUTIL) {
+ /* convert the highest level to a string and dbprintf it */
+ debug_printf("%s%s\n", levprefix, message);
+ }
+
+ if (amanda_log_handlers) {
+ GSList *iter = amanda_log_handlers;
+ while (iter) {
+ amanda_log_handler_t *hdlr = (amanda_log_handler_t *)iter->data;
+ hdlr(maxlevel, message);
+ iter = g_slist_next(iter);
+ }
+ } else {
+ /* call the appropriate handlers, based on the context */
+ amanda_log_stderr(maxlevel, message);
+ if (context == CONTEXT_DAEMON)
+ amanda_log_syslog(maxlevel, message);
+ }
+
+ /* error and critical levels have special handling */
+ if (log_level & (G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL)) {
+#ifdef HAVE_GLIBC_BACKTRACE
+ /* try logging a traceback to the debug log */
+ if (!do_suppress_error_traceback && db_fd != -1) {
+ void *stack[32];
+ int naddrs;
+ naddrs = backtrace(stack, sizeof(stack)/sizeof(*stack));
+ backtrace_symbols_fd(stack, naddrs, db_fd);
+ }
+#endif
+
+ /* we're done */
+ if (log_level & G_LOG_LEVEL_CRITICAL)
+ exit(error_exit_status);
+ else
+ abort();
+ g_assert_not_reached();
+ }
+}
+
+/* Install our handler into the glib log handling system.
+ */
+static void
+debug_setup_logging(void)
+{
+ /* g_error and g_critical should be fatal, although the log handler
+ * takes care of this anyway */
+ g_log_set_always_fatal(G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
+
+ /* set up handler (g_log_set_default_handler is new in glib-2.6, and
+ * hence not useable here) */
+ g_log_set_handler(NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
+ debug_logging_handler, NULL);
+}
+
+void
+add_amanda_log_handler(amanda_log_handler_t *hdlr)
+{
+ amanda_log_handlers = g_slist_append(amanda_log_handlers, (gpointer)hdlr);
+}
+
+void
+amanda_log_syslog(GLogLevelFlags log_level, const gchar *message)
+{
+ int priority = LOG_ERR;
+ switch (log_level) {
+ case G_LOG_LEVEL_ERROR:
+ case G_LOG_LEVEL_CRITICAL:
+ priority = LOG_ERR;
+ break;
+
+ case G_LOG_LEVEL_WARNING:
+#ifdef LOG_WARNING
+ priority = LOG_WARNING;
+#endif
+ break;
+
+ default:
+ return;
+ }
+
+#ifdef LOG_DAEMON
+ openlog(get_pname(), LOG_PID, LOG_DAEMON);
+#else
+ openlog(get_pname(), LOG_PID, 0);
+#endif
+ syslog(priority, "%s", message);
+ closelog();
+
+}
+
+void
+amanda_log_stderr(GLogLevelFlags log_level, const gchar *message)
+{
+ switch (log_level) {
+ case G_LOG_LEVEL_ERROR:
+ case G_LOG_LEVEL_CRITICAL:
+ g_fprintf(stderr, "%s: %s\n", get_pname(), message);
+ break;
+
+ default:
+ return;
+ }
+}
+
+void
+amanda_log_null(GLogLevelFlags log_level G_GNUC_UNUSED, const gchar *message G_GNUC_UNUSED)
+{
+}
+
+/* Set the global dbgdir according to 'config' and 'subdir'
+ *
+ * The global open_time is set to the current time, and used to delete
+ * old files.
+ *
+ * @param config: configuration or NULL
+ * @param subdir: subdirectory (server, client, etc.) or NULL
+ */
+static void
+debug_setup_1(char *config, char *subdir)
+{
+ char *sane_config = NULL;
+
+ /*
+ * Create the debug directory if it does not yet exist.
+ */
+ amfree(dbgdir);
+ if (config)
+ sane_config = sanitise_filename(config);
+ if (sane_config && subdir)
+ dbgdir = vstralloc(AMANDA_DBGDIR, "/", subdir, "/", sane_config,
+ "/", NULL);
+ else if (sane_config)
+ dbgdir = vstralloc(AMANDA_DBGDIR, "/", sane_config, "/", NULL);
+ else if (subdir)
+ dbgdir = vstralloc(AMANDA_DBGDIR, "/", subdir, "/", NULL);
+ else
+ dbgdir = stralloc2(AMANDA_DBGDIR, "/");
+ if(mkpdir(dbgdir, 0700, get_client_uid(), get_client_gid()) == -1) {
+ error(_("create debug directory \"%s\": %s"),
+ dbgdir, strerror(errno));
+ /*NOTREACHED*/
+ }
+ amfree(sane_config);
+
+ time(&open_time);
+}
+
+/*
+ * Clean out old debug files. We also rename files with old style
+ * names (XXX.debug or XXX.$PID.debug) into the new name format.
+ * We assume no system has 17 digit PID-s :-) and that there will
+ * not be a conflict between an old and new name.
+ */
+static void
+debug_unlink_old(void)
{
- struct passwd *pwent;
char *pname;
size_t pname_len;
char *e = NULL;
char *s = NULL;
- DIR *d;
struct dirent *entry;
int do_rename;
- char *test_name = NULL;
+ char *test_name;
size_t test_name_len;
size_t d_name_len;
- struct stat sbuf;
char *dbfilename = NULL;
int i;
+ DIR *d;
+ struct stat sbuf;
- if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
- client_uid = pwent->pw_uid;
- client_gid = pwent->pw_gid;
- endpwent();
- }
+ assert(dbgdir != NULL);
+
+ memset(&sbuf, 0, SIZEOF(sbuf));
pname = get_pname();
pname_len = strlen(pname);
- /*
- * Create the debug directory if it does not yet exist.
- */
- amfree(dbgdir);
- dbgdir = stralloc2(AMANDA_DBGDIR, "/");
- if(mkpdir(dbgdir, 02700, client_uid, client_gid) == -1) {
- error("create debug directory \"%s\": %s",
- AMANDA_DBGDIR, strerror(errno));
+ if((d = opendir(dbgdir)) == NULL) {
+ error(_("open debug directory \"%s\": %s"),
+ dbgdir, strerror(errno));
+ /*NOTREACHED*/
}
-
- /*
- * Clean out old debug files. We also rename files with old style
- * names (XXX.debug or XXX.$PID.debug) into the new name format.
- * We assume no system has 17 digit PID-s :-) and that there will
- * not be a conflict between an old and new name.
- */
- if((d = opendir(AMANDA_DBGDIR)) == NULL) {
- error("open debug directory \"%s\": %s",
- AMANDA_DBGDIR, strerror(errno));
- }
- time(&curtime);
- test_name = get_debug_name(curtime - (AMANDA_DEBUG_DAYS * 24 * 60 * 60), 0);
+ test_name = get_debug_name(open_time - (getconf_int(CNF_DEBUG_DAYS) * 24 * 60 * 60), 0);
test_name_len = strlen(test_name);
while((entry = readdir(d)) != NULL) {
if(is_dot_or_dotdot(entry->d_name)) {
dbfilename = get_debug_name((time_t)sbuf.st_mtime, ++i);
}
if(dbfilename == NULL) {
- error("cannot rename old debug file \"%s\"", entry->d_name);
+ error(_("cannot rename old debug file \"%s\""), entry->d_name);
+ /*NOTREACHED*/
}
}
}
closedir(d);
}
-static void debug_setup_2(s, fd, notation)
- char *s;
- int fd;
- char *notation;
+/* Given an already-opened debug file, set the file's ownership
+ * appropriately, move its file descriptor above MIN_DB_FD, and
+ * add an initial log entry to the file.
+ *
+ * This function records the file's identity in the globals
+ * db_filename, db_fd, and db_file. It does *not* set db_name.
+ * db_file is not set if fd is -1
+ *
+ * This function uses the global 'open_time', which is set by
+ * debug_setup_1.
+ *
+ * @param s: the filename of the debug file; string should be malloc'd,
+ * and should *not* be freed by the caller.
+ * @param fd: the descriptor connected to the debug file, or -1 if
+ * no decriptor moving should take place.
+ * @param annotation: an extra string to include in the initial
+ * log entry.
+ */
+static void
+debug_setup_2(
+ char * s,
+ int fd,
+ char * annotation)
{
- int saved_debug;
int i;
int fd_close[MIN_DB_FD+1];
amfree(db_filename);
db_filename = s;
s = NULL;
- (void) chown(db_filename, client_uid, client_gid);
- amfree(dbgdir);
+
+ /* If we're root, change the ownership of the debug files. If we're not root,
+ * this would either be redundant or an error. */
+ if (geteuid() == 0) {
+ if (chown(db_filename, get_client_uid(), get_client_gid()) < 0) {
+ dbprintf(_("chown(%s, %d, %d) failed: %s"),
+ db_filename, (int)get_client_uid(), (int)get_client_gid(), strerror(errno));
+ }
+ }
+
/*
* Move the file descriptor up high so it stays out of the way
* of other processing, e.g. sendbackup.
*/
- i = 0;
- fd_close[i++] = fd;
- while((db_fd = dup(fd)) < MIN_DB_FD) {
- fd_close[i++] = db_fd;
- }
- while(--i >= 0) {
- close(fd_close[i]);
+ if (fd >= 0) {
+ i = 0;
+ fd_close[i++] = fd;
+ while((db_fd = dup(fd)) < MIN_DB_FD) {
+ fd_close[i++] = db_fd;
+ }
+ while(--i >= 0) {
+ close(fd_close[i]);
+ }
+ db_file = fdopen(db_fd, "a");
}
- db_file = fdopen(db_fd, "a");
- if (notation) {
+ if (annotation) {
/*
* Make the first debug log file entry.
*/
- saved_debug = debug; debug = 1;
- debug_printf("%s: debug %d pid %ld ruid %ld euid %ld: %s at %s",
- get_pname(), saved_debug, (long)getpid(),
+ debug_printf(_("pid %ld ruid %ld euid %ld version %s: %s at %s"),
+ (long)getpid(),
(long)getuid(), (long)geteuid(),
- notation,
- ctime(&curtime));
- debug = saved_debug;
+ VERSION,
+ annotation,
+ ctime(&open_time));
}
}
-void debug_open()
+/* Get current GMT time and return a message timestamp.
+ * Used for g_printf calls to logs and such. The return value
+ * is to a static buffer, so it should be used immediately.
+ *
+ * @returns: timestamp
+ */
+static char *
+msg_timestamp(void)
+{
+ static char timestamp[128];
+ char *r;
+ time_t curtime;
+
+ time(&curtime);
+ ctime_r(&curtime, timestamp);
+ r = strchr(timestamp, '\n');
+ if (r)
+ *r = '\0';
+
+ return timestamp;
+}
+
+/* Get current GMT time and return a message timestamp.
+ * Used for g_printf calls to logs and such. The return value
+ * is to a static buffer, so it should be used immediately.
+ *
+ * @returns: timestamp
+ */
+static char *
+msg_thread(void)
+{
+ static char thread[128];
+
+ sprintf(thread, "thd-%p", g_thread_self());
+
+ return thread;
+}
+
+/*
+ * ---- public functions
+ */
+
+void
+debug_init(void)
+{
+ debug_setup_logging();
+
+ /* the scriptutil context does not create a debug log, since such
+ * processes are invoked many times.
+ */
+ if (get_pcontext() != CONTEXT_SCRIPTUTIL) {
+ debug_open(get_ptype());
+ }
+}
+
+void
+debug_open(char *subdir)
{
- char *dbfilename = NULL;
int fd = -1;
int i;
char *s = NULL;
mode_t mask;
- /*
- * Do initial setup.
- */
- debug_setup_1();
+ /* set up logging while we're here */
+ debug_setup_logging();
+
+ /* set 'dbgdir' and clean out old debug files */
+ debug_setup_1(NULL, subdir);
/*
* Create the new file with a unique sequence number.
*/
- mask = umask(0037); /* Allow the group read bit through */
+ mask = (mode_t)umask((mode_t)0037); /* Allow the group read bit through */
+
+ /* iteratate through sequence numbers until we find one that
+ * is not already in use */
for(i = 0; fd < 0; i++) {
- if ((dbfilename = get_debug_name(curtime, i)) == NULL) {
- error("Cannot create %s debug file", get_pname());
- /* NOTREACHED */
- }
-
- if ((s = newvstralloc(s, dbgdir, dbfilename, NULL)) == NULL) {
- error("Cannot allocate %s debug file name memory", get_pname());
- /* NOTREACHED */
- }
- amfree(dbfilename);
-
- if ((fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0640)) < 0) {
- if (errno != EEXIST) {
- error("Cannot create %s debug file: %s",
- get_pname(), strerror(errno));
- /* NOTREACHED */
- }
- amfree(s);
- }
+ amfree(db_name);
+ if ((db_name = get_debug_name(open_time, i)) == NULL) {
+ error(_("Cannot create debug file name in %d tries."), i);
+ /*NOTREACHED*/
+ }
+
+ if ((s = newvstralloc(s, dbgdir, db_name, NULL)) == NULL) {
+ error(_("Cannot allocate debug file name memory"));
+ /*NOTREACHED*/
+ }
+
+ if ((fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0640)) < 0) {
+ if (errno != EEXIST) {
+ error(_("Cannot create debug file \"%s\": %s"),
+ s, strerror(errno));
+ /*NOTREACHED*/
+ }
+ amfree(s);
+ }
}
(void)umask(mask); /* Restore mask */
debug_setup_2(s, fd, "start");
}
-void debug_reopen(dbfilename, notation)
- char *dbfilename;
- char *notation;
+void
+debug_reopen(
+ char * dbfilename,
+ char * annotation)
{
char *s = NULL;
- int fd = -1;
+ int fd;
if (dbfilename == NULL) {
return;
}
- /*
- * Do initial setup.
- */
- debug_setup_1();
+ /* set 'dbgdir' and clean out old debug files */
+ debug_setup_1(NULL, NULL);
/*
* Reopen the file.
s = newvstralloc(s, dbgdir, dbfilename, NULL);
}
if ((fd = open(s, O_RDWR|O_APPEND)) < 0) {
- error("cannot reopen %s debug file %s", get_pname(), dbfilename);
+ error(_("cannot reopen debug file %s"), dbfilename);
+ /*NOTREACHED*/
}
/*
*
* Note: we release control of the string 's' points to.
*/
- debug_setup_2(s, fd, notation);
+ debug_setup_2(s, fd, annotation);
}
-void debug_close()
+void
+debug_rename(
+ char *config,
+ char *subdir)
+{
+ int fd = -1;
+ int i;
+ char *s = NULL;
+ mode_t mask;
+
+ if (!db_filename)
+ return;
+
+ if (get_pcontext() == CONTEXT_SCRIPTUTIL) {
+ return;
+ }
+
+ /* Remove old log from source directory */
+ debug_unlink_old();
+ /* set 'dbgdir' and clean out old debug files */
+ debug_setup_1(config, subdir);
+ /* Remove old log from destination directory */
+ debug_unlink_old();
+
+ s = newvstralloc(s, dbgdir, db_name, NULL);
+
+ if (strcmp(db_filename, s) == 0) {
+ amfree(s);
+ return;
+ }
+
+ mask = (mode_t)umask((mode_t)0037);
+
+#if defined(__CYGWIN__)
+ /*
+ * On cygwin, rename will not overwrite an existing file nor
+ * will it rename a file that is open for writing...
+ *
+ * Rename file directly. Expect failure if file already exists
+ * or is open by another user.
+ */
+
+ i = 0;
+ while (rename(db_filename, s) < 0) {
+ if (errno != EEXIST) {
+ /*
+ * If the failure was not due to the target file name already
+ * existing then we have bigger issues at hand so we keep
+ * the existing file.
+ */
+ dbprintf(_("Cannot rename \"%s\" to \"%s\": %s\n"),
+ db_filename, s, strerror(errno));
+ s = newvstralloc(s, db_filename, NULL);
+ i = -1;
+ break;
+ }
+
+ /*
+ * Files already exists:
+ * Continue searching for a unique file name that will work.
+ */
+ amfree(db_name);
+ if ((db_name = get_debug_name(open_time, i++)) == NULL) {
+ dbprintf(_("Cannot create unique debug file name"));
+ break;
+ }
+ s = newvstralloc(s, dbgdir, db_name, NULL);
+ }
+ if (i >= 0) {
+ /*
+ * We need to close and reopen the original file handle to
+ * release control of the original debug file name.
+ */
+ if ((fd = open(s, O_WRONLY|O_APPEND, 0640)) >= 0) {
+ /*
+ * We can safely close the the original log file
+ * since we now have a new working handle.
+ */
+ db_fd = 2;
+ fclose(db_file);
+ db_file = NULL;
+ }
+ }
+#else
+ /* check if a file with the same name already exists. */
+ if ((fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0640)) < 0) {
+ for(i = 0; fd < 0; i++) {
+ amfree(db_name);
+ if ((db_name = get_debug_name(open_time, i)) == NULL) {
+ dbprintf(_("Cannot create debug file"));
+ break;
+ }
+
+ s = newvstralloc(s, dbgdir, db_name, NULL);
+ if ((fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0640)) < 0) {
+ if (errno != EEXIST) {
+ dbprintf(_("Cannot create debug file: %s"),
+ strerror(errno));
+ break;
+ }
+ }
+ }
+ }
+
+ if (fd >= 0) {
+ close(fd);
+ if (rename(db_filename, s) == -1) {
+ dbprintf(_("Cannot rename \"%s\" to \"%s\": %s\n"),
+ db_filename, s, strerror(errno));
+ }
+ fd = -1;
+ }
+#endif
+
+ (void)umask(mask); /* Restore mask */
+ /*
+ * Finish setup.
+ *
+ * Note: we release control of the string 's' points to.
+ */
+ debug_setup_2(s, fd, "rename");
+}
+
+void
+debug_close(void)
{
time_t curtime;
- int save_debug;
- pid_t save_pid;
+
+ if (get_pcontext() == CONTEXT_SCRIPTUTIL) {
+ return;
+ }
+
+ debug_unlink_old();
time(&curtime);
- save_debug = debug;
- debug = 1;
- save_pid = debug_prefix_pid;
- debug_prefix_pid = 0;
- debug_printf("%s: pid %ld finish time %s",
- debug_prefix_time(NULL),
- (long)getpid(),
- ctime(&curtime));
- debug_prefix_pid = save_pid;
- debug = save_debug;
+ debug_printf(_("pid %ld finish time %s"), (long)getpid(), ctime(&curtime));
if(db_file && fclose(db_file) == EOF) {
int save_errno = errno;
db_file = NULL; /* prevent recursion */
- error("close debug file: %s", strerror(save_errno));
+ g_fprintf(stderr, _("close debug file: %s"), strerror(save_errno));
+ /*NOTREACHED*/
}
- db_fd = -1;
+ db_fd = 2;
db_file = NULL;
amfree(db_filename);
+ amfree(db_name);
}
-int debug_fd()
+/*
+ * Format and write a debug message to the process debug file.
+ */
+printf_arglist_function(void debug_printf, const char *, format)
{
- return db_fd;
-}
+ va_list argp;
+ int save_errno;
-FILE *debug_fp()
-{
- return db_file;
+ /*
+ * It is common in the code to call dbprintf to write out
+ * syserrno(errno) and then turn around and try to do something else
+ * with errno (e.g. g_printf() or log()), so we make sure errno goes
+ * back out with the same value it came in with.
+ */
+
+ save_errno = errno;
+
+ /* handle the default (stderr) if debug_open hasn't been called yet */
+ if(db_file == NULL && db_fd == 2) {
+ db_file = stderr;
+ }
+ if(db_file != NULL) {
+ char *prefix;
+ char *text;
+
+ if (db_file != stderr)
+ prefix = g_strdup_printf("%s: %s: %s:", msg_timestamp(), msg_thread(), get_pname());
+ else
+ prefix = g_strdup_printf("%s:", get_pname());
+ arglist_start(argp, format);
+ text = g_strdup_vprintf(format, argp);
+ arglist_end(argp);
+ fprintf(db_file, "%s %s", prefix, text);
+ amfree(prefix);
+ amfree(text);
+ fflush(db_file);
+ }
+ errno = save_errno;
}
-char *debug_fn()
+int
+debug_fd(void)
{
- return db_filename;
+ return db_fd;
}
-/*
- * Routines for returning a common debug file line prefix. Always starts
- * with the current program name, possibly with an optional suffix.
- * May then be followed by a PID. May then be followed by an elapsed
- * time indicator.
- */
-
-void set_debug_prefix_pid(p)
- pid_t p;
+FILE *
+debug_fp(void)
{
- debug_prefix_pid = p;
+ return db_file;
}
-char *debug_prefix(suffix)
- char *suffix;
+char *
+debug_fn(void)
{
- int save_errno;
- static char *s = NULL;
- char debug_pid[NUM_STR_SIZE];
-
- save_errno = errno;
- s = newvstralloc(s, get_pname(), suffix, NULL);
- if (debug_prefix_pid != (pid_t) 0) {
- snprintf(debug_pid, sizeof(debug_pid),
- "%ld",
- (long) debug_prefix_pid);
- s = newvstralloc(s, s, "[", debug_pid, "]", NULL);
- }
- errno = save_errno;
- return s;
+ return db_filename;
}
-char *debug_prefix_time(suffix)
- char *suffix;
+void
+debug_dup_stderr_to_debug(void)
{
- int save_errno;
- static char *s = NULL;
- char *t1;
- char *t2;
-
- save_errno = errno;
- if (clock_is_running()) {
- t1 = ": time ";
- t2 = walltime_str(curclock());
- } else {
- t1 = t2 = NULL;
+ if(db_fd != -1 && db_fd != STDERR_FILENO)
+ {
+ if(dup2(db_fd, STDERR_FILENO) != STDERR_FILENO)
+ {
+ error(_("can't redirect stderr to the debug file: %d, %s"), db_fd, strerror(errno));
+ g_assert_not_reached();
+ }
}
-
- s = newvstralloc(s, debug_prefix(suffix), t1, t2, NULL);
-
- errno = save_errno;
- return s;
}
-#endif
+