X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Fdebug.c;h=cb3bdd558a8c9a8527578ea33defa06e8d02544f;hb=HEAD;hp=ce546bc37801dd89a44c482bf2d30a47cfee2696;hpb=bde83ad58d800ae004caccab6531234272181da2;p=debian%2Famanda diff --git a/common-src/debug.c b/common-src/debug.c index ce546bc..cb3bdd5 100644 --- a/common-src/debug.c +++ b/common-src/debug.c @@ -1,6 +1,7 @@ /* * Amanda, The Advanced Maryland Automatic Network Disk Archiver * Copyright (c) 1991-1998 University of Maryland at College Park + * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved. * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its @@ -25,69 +26,75 @@ * University of Maryland at College Park */ /* - * $Id: debug.c,v 1.17.4.3.4.3.2.9.2.1 2005/09/20 19:06:37 jrjackson Exp $ + * $Id: debug.c,v 1.40 2006/07/26 11:49:32 martinea Exp $ * - * debug log subroutines + * Logging support */ #include "amanda.h" -#include "clock.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 #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, 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; @@ -96,68 +103,253 @@ get_debug_name(t, n) if(n < 0 || n > 1000) { return NULL; } - ts = construct_timestamp(&t); + ts = get_timestamp_from_time(t); if(n == 0) { number[0] = '\0'; } else { - ap_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; +} -static void debug_setup_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 */ + } + + /* 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)); - } - - /* - * 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)); + if((d = opendir(dbgdir)) == NULL) { + error(_("open debug directory \"%s\": %s"), + dbgdir, strerror(errno)); + /*NOTREACHED*/ } - 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)) { @@ -201,7 +393,8 @@ static void debug_setup_1() 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*/ } } } @@ -212,72 +405,176 @@ static void debug_setup_1() 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(); + /* create AMANDA_TMPDIR */ + make_amanda_tmpdir(); + + /* 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. + * Create the new file with a unique sequence number. */ - for(i = 0; - (dbfilename = get_debug_name(curtime, i)) != NULL - && (s = newvstralloc(s, dbgdir, dbfilename, NULL)) != NULL - && (fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0600)) < 0; - i++, free(dbfilename)) {} - if(dbfilename == NULL) { - error("cannot create %s debug file", get_pname()); + 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++) { + 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); + } } - amfree(dbfilename); + (void)umask(mask); /* Restore mask */ /* * Finish setup. @@ -287,21 +584,20 @@ void debug_open() 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. @@ -311,8 +607,9 @@ void debug_reopen(dbfilename, notation) } else { s = newvstralloc(s, dbgdir, dbfilename, NULL); } - if ((fd = open(s, O_RDWR|O_APPEND, 0600)) < 0) { - error("cannot reopen %s debug file %s", get_pname(), dbfilename); + if ((fd = open(s, O_RDWR|O_APPEND)) < 0) { + error(_("cannot reopen debug file %s"), dbfilename); + /*NOTREACHED*/ } /* @@ -320,98 +617,227 @@ void debug_reopen(dbfilename, notation) * * 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) { - static char *s = NULL; - char debug_pid[NUM_STR_SIZE]; - - s = newvstralloc(s, get_pname(), suffix, NULL); - if (debug_prefix_pid != (pid_t) 0) { - ap_snprintf(debug_pid, sizeof(debug_pid), - "%ld", - (long) debug_prefix_pid); - s = newvstralloc(s, s, "[", debug_pid, "]", NULL); - } - return s; + return db_filename; } -char *debug_prefix_time(suffix) - char *suffix; +void +debug_dup_stderr_to_debug(void) { - static char *s = NULL; - char *t1; - char *t2; - - 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); - - return s; } -#endif +