Imported Upstream version 3.1.0
[debian/amanda] / common-src / debug.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27 /*
28  * $Id: debug.c,v 1.40 2006/07/26 11:49:32 martinea Exp $
29  *
30  * Logging support
31  */
32
33 #include "amanda.h"
34 #include "util.h"
35 #include "arglist.h"
36 #include "clock.h"
37 #include "timestamp.h"
38 #include "conffile.h"
39
40 #ifdef HAVE_GLIBC_BACKTRACE
41 #include <execinfo.h>
42 #endif
43
44 /* Minimum file descriptor on which to keep the debug file.  This is intended
45  * to keep the descriptor "out of the way" of other processing.  It's not clear
46  * that this is required any longer, but it doesn't hurt anything.
47  */
48 #define MIN_DB_FD                       10
49
50 /* information on the current debug file */
51 static int db_fd = 2;                   /* file descriptor (default stderr) */
52 static FILE *db_file = NULL;            /* stdio stream */
53 static char *db_name  = NULL;           /* unqualified filename */
54 static char *db_filename = NULL;        /* fully qualified pathname */
55
56 /* directory containing debug file, including trailing slash */
57 static char *dbgdir = NULL;
58
59 /* time debug log was opened (timestamp of the file) */
60 static time_t open_time;
61
62 /* storage for global variables */
63 int error_exit_status = 1;
64
65 /* static function prototypes */
66 static char *get_debug_name(time_t t, int n);
67 static void debug_unlink_old(void);
68 static void debug_setup_1(char *config, char *subdir);
69 static void debug_setup_2(char *s, int fd, char *annotation);
70 static char *msg_timestamp(void);
71
72 static void debug_logging_handler(const gchar *log_domain,
73         GLogLevelFlags log_level,
74         const gchar *message,
75         gpointer user_data);
76 static void debug_setup_logging(void);
77
78 /* By default, do not suppress tracebacks */
79 static gboolean do_suppress_error_traceback = FALSE;
80
81 /* configured amanda_log_handlers */
82 static GSList *amanda_log_handlers = NULL;
83
84 /*
85  * Generate a debug file name.  The name is based on the program name,
86  * followed by a timestamp, an optional sequence number, and ".debug".
87  *
88  * @param t: timestamp
89  * @param n: sequence number between 1 and 1000; if zero, no sequence number
90  * is included.
91  */
92 static char *
93 get_debug_name(
94     time_t      t,
95     int         n)
96 {
97     char number[NUM_STR_SIZE];
98     char *ts;
99     char *result;
100
101     if(n < 0 || n > 1000) {
102         return NULL;
103     }
104     ts = get_timestamp_from_time(t);
105     if(n == 0) {
106         number[0] = '\0';
107     } else {
108         g_snprintf(number, SIZEOF(number), "%03d", n - 1);
109     }
110     result = vstralloc(get_pname(), ".", ts, number, ".debug", NULL);
111     amfree(ts);
112     return result;
113 }
114
115 /* Call this to suppress tracebacks on error() or g_critical().  This is used
116  * when a critical error is indicated in perl, and the traceback will not be
117  * useful. */
118 void
119 suppress_error_traceback(void)
120 {
121     do_suppress_error_traceback = 1;
122 }
123
124 /* A GLogFunc to handle g_log calls.  This function assumes that user_data
125  * is either NULL or a pointer to one of the debug_* configuration variables
126  * in conffile.c, indicating whether logging for this log domain is enabled.
127  *
128  * @param log_domain: the log domain, or NULL for general logging
129  * @param log_level: level, fatality, and recursion flags
130  * @param message: the message to log
131  * @param user_pointer: unused
132  */
133 static void
134 debug_logging_handler(const gchar *log_domain G_GNUC_UNUSED,
135             GLogLevelFlags log_level,
136             const gchar *message,
137             gpointer user_data G_GNUC_UNUSED)
138 {
139     GLogLevelFlags maxlevel;
140     char *levprefix = NULL;
141     pcontext_t context = get_pcontext();
142
143     /* glib allows a message to have multiple levels, so calculate the "worst"
144      * level */
145     if (log_level & G_LOG_LEVEL_ERROR) {
146         maxlevel = G_LOG_LEVEL_ERROR;
147         levprefix = _("error (fatal): ");
148     } else if (log_level & G_LOG_LEVEL_CRITICAL) {
149         maxlevel = G_LOG_LEVEL_CRITICAL;
150         levprefix = _("critical (fatal): ");
151     } else if (log_level & G_LOG_LEVEL_WARNING) {
152         maxlevel = G_LOG_LEVEL_WARNING;
153         levprefix = _("warning: ");
154     } else if (log_level & G_LOG_LEVEL_MESSAGE) {
155         maxlevel = G_LOG_LEVEL_MESSAGE;
156         levprefix = _("message: ");
157     } else if (log_level & G_LOG_LEVEL_INFO) {
158         maxlevel = G_LOG_LEVEL_INFO;
159         levprefix = _("info: ");
160     } else {
161         maxlevel = G_LOG_LEVEL_DEBUG;
162         levprefix = ""; /* no level displayed for debugging */
163     }
164
165     /* scriptutil context doesn't do any logging except for critical
166      * and error levels */
167     if (context != CONTEXT_SCRIPTUTIL) {
168         /* convert the highest level to a string and dbprintf it */
169         debug_printf("%s%s\n", levprefix, message);
170     }
171
172     if (amanda_log_handlers) {
173         GSList *iter = amanda_log_handlers;
174         while (iter) {
175             amanda_log_handler_t *hdlr = (amanda_log_handler_t *)iter->data;
176             hdlr(maxlevel, message);
177             iter = g_slist_next(iter);
178         }
179     } else {
180         /* call the appropriate handlers, based on the context */
181         amanda_log_stderr(maxlevel, message);
182         if (context == CONTEXT_DAEMON)
183             amanda_log_syslog(maxlevel, message);
184     }
185
186     /* error and critical levels have special handling */
187     if (log_level & (G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL)) {
188 #ifdef HAVE_GLIBC_BACKTRACE
189         /* try logging a traceback to the debug log */
190         if (!do_suppress_error_traceback && db_fd != -1) {
191             void *stack[32];
192             int naddrs;
193             naddrs = backtrace(stack, sizeof(stack)/sizeof(*stack));
194             backtrace_symbols_fd(stack, naddrs, db_fd);
195         }
196 #endif
197
198         /* we're done */
199         if (log_level & G_LOG_LEVEL_CRITICAL)
200             exit(error_exit_status);
201         else
202             abort();
203         g_assert_not_reached();
204     }
205 }
206
207 /* Install our handler into the glib log handling system.
208  */
209 static void
210 debug_setup_logging(void)
211 {
212     /* g_error and g_critical should be fatal, although the log handler
213      * takes care of this anyway */
214     g_log_set_always_fatal(G_LOG_LEVEL_ERROR |  G_LOG_LEVEL_CRITICAL);
215
216     /* set up handler (g_log_set_default_handler is new in glib-2.6, and
217      * hence not useable here) */
218     g_log_set_handler(NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
219                       debug_logging_handler, NULL);
220 }
221
222 void
223 add_amanda_log_handler(amanda_log_handler_t *hdlr)
224 {
225     amanda_log_handlers = g_slist_append(amanda_log_handlers, (gpointer)hdlr);
226 }
227
228 void
229 amanda_log_syslog(GLogLevelFlags log_level, const gchar *message)
230 {
231     int priority = LOG_ERR;
232     switch (log_level) {
233         case G_LOG_LEVEL_ERROR:
234         case G_LOG_LEVEL_CRITICAL:
235             priority = LOG_ERR;
236             break;
237
238         case G_LOG_LEVEL_WARNING:
239 #ifdef LOG_WARNING
240             priority = LOG_WARNING;
241 #endif
242             break;
243
244         default:
245             return;
246     }
247
248 #ifdef LOG_DAEMON
249     openlog(get_pname(), LOG_PID, LOG_DAEMON);
250 #else
251     openlog(get_pname(), LOG_PID, 0);
252 #endif
253     syslog(priority, "%s", message);
254     closelog();
255
256 }
257
258 void
259 amanda_log_stderr(GLogLevelFlags log_level, const gchar *message)
260 {
261     switch (log_level) {
262         case G_LOG_LEVEL_ERROR:
263         case G_LOG_LEVEL_CRITICAL:
264             g_fprintf(stderr, "%s: %s\n", get_pname(), message);
265             break;
266
267         default:
268             return;
269     }
270 }
271
272 void
273 amanda_log_null(GLogLevelFlags log_level G_GNUC_UNUSED, const gchar *message G_GNUC_UNUSED)
274 {
275 }
276
277 /* Set the global dbgdir according to 'config' and 'subdir'
278  *
279  * The global open_time is set to the current time, and used to delete
280  * old files.
281  *
282  * @param config: configuration or NULL
283  * @param subdir: subdirectory (server, client, etc.) or NULL
284  */
285 static void
286 debug_setup_1(char *config, char *subdir)
287 {
288     char *sane_config = NULL;
289
290     /*
291      * Create the debug directory if it does not yet exist.
292      */
293     amfree(dbgdir);
294     if (config)
295         sane_config = sanitise_filename(config);
296     if (sane_config && subdir)
297         dbgdir = vstralloc(AMANDA_DBGDIR, "/", subdir, "/", sane_config,
298                            "/", NULL);
299     else if (sane_config)
300         dbgdir = vstralloc(AMANDA_DBGDIR, "/", sane_config, "/", NULL);
301     else if (subdir)
302         dbgdir = vstralloc(AMANDA_DBGDIR, "/", subdir, "/", NULL);
303     else
304         dbgdir = stralloc2(AMANDA_DBGDIR, "/");
305     if(mkpdir(dbgdir, 0700, get_client_uid(), get_client_gid()) == -1) {
306         error(_("create debug directory \"%s\": %s"),
307               dbgdir, strerror(errno));
308         /*NOTREACHED*/
309     }
310     amfree(sane_config);
311
312     time(&open_time);
313 }
314
315 /*
316  * Clean out old debug files.  We also rename files with old style
317  * names (XXX.debug or XXX.$PID.debug) into the new name format.
318  * We assume no system has 17 digit PID-s :-) and that there will
319  * not be a conflict between an old and new name.
320  */
321 static void
322 debug_unlink_old(void)
323 {
324     char *pname;
325     size_t pname_len;
326     char *e = NULL;
327     char *s = NULL;
328     struct dirent *entry;
329     int do_rename;
330     char *test_name;
331     size_t test_name_len;
332     size_t d_name_len;
333     char *dbfilename = NULL;
334     int i;
335     DIR *d;
336     struct stat sbuf;
337
338     assert(dbgdir != NULL);
339
340     memset(&sbuf, 0, SIZEOF(sbuf));
341
342     pname = get_pname();
343     pname_len = strlen(pname);
344
345     if((d = opendir(dbgdir)) == NULL) {
346         error(_("open debug directory \"%s\": %s"),
347               dbgdir, strerror(errno));
348         /*NOTREACHED*/
349     }
350     test_name = get_debug_name(open_time - (getconf_int(CNF_DEBUG_DAYS) * 24 * 60 * 60), 0);
351     test_name_len = strlen(test_name);
352     while((entry = readdir(d)) != NULL) {
353         if(is_dot_or_dotdot(entry->d_name)) {
354             continue;
355         }
356         d_name_len = strlen(entry->d_name);
357         if(strncmp(entry->d_name, pname, pname_len) != 0
358            || entry->d_name[pname_len] != '.'
359            || d_name_len < 6
360            || strcmp(entry->d_name + d_name_len - 6, ".debug") != 0) {
361             continue;                           /* not one of our debug files */
362         }
363         e = newvstralloc(e, dbgdir, entry->d_name, NULL);
364         if(d_name_len < test_name_len) {
365             /*
366              * Create a "pretend" name based on the last modification
367              * time.  This name will be used to decide if the real name
368              * should be removed.  If not, it will be used to rename the
369              * real name.
370              */
371             if(stat(e, &sbuf) != 0) {
372                 continue;                       /* ignore errors */
373             }
374             amfree(dbfilename);
375             dbfilename = get_debug_name((time_t)sbuf.st_mtime, 0);
376             do_rename = 1;
377         } else {
378             dbfilename = newstralloc(dbfilename, entry->d_name);
379             do_rename = 0;
380         }
381         if(strcmp(dbfilename, test_name) < 0) {
382             (void) unlink(e);                   /* get rid of old file */
383             continue;
384         }
385         if(do_rename) {
386             i = 0;
387             while(dbfilename != NULL
388                   && (s = newvstralloc(s, dbgdir, dbfilename, NULL)) != NULL
389                   && rename(e, s) != 0 && errno != ENOENT) {
390                 amfree(dbfilename);
391                 dbfilename = get_debug_name((time_t)sbuf.st_mtime, ++i);
392             }
393             if(dbfilename == NULL) {
394                 error(_("cannot rename old debug file \"%s\""), entry->d_name);
395                 /*NOTREACHED*/
396             }
397         }
398     }
399     amfree(dbfilename);
400     amfree(e);
401     amfree(s);
402     amfree(test_name);
403     closedir(d);
404 }
405
406 /* Given an already-opened debug file, set the file's ownership
407  * appropriately, move its file descriptor above MIN_DB_FD, and
408  * add an initial log entry to the file.
409  *
410  * This function records the file's identity in the globals
411  * db_filename, db_fd, and db_file.  It does *not* set db_name.
412  * db_file is not set if fd is -1
413  *
414  * This function uses the global 'open_time', which is set by
415  * debug_setup_1.
416  *
417  * @param s: the filename of the debug file; string should be malloc'd,
418  * and should *not* be freed by the caller.
419  * @param fd: the descriptor connected to the debug file, or -1 if
420  * no decriptor moving should take place.
421  * @param annotation: an extra string to include in the initial
422  * log entry.
423  */
424 static void
425 debug_setup_2(
426     char *      s,
427     int         fd,
428     char *      annotation)
429 {
430     int i;
431     int fd_close[MIN_DB_FD+1];
432
433     amfree(db_filename);
434     db_filename = s;
435     s = NULL;
436
437     /* If we're root, change the ownership of the debug files.  If we're not root,
438      * this would either be redundant or an error. */
439     if (geteuid() == 0) {
440         if (chown(db_filename, get_client_uid(), get_client_gid()) < 0) {
441             dbprintf(_("chown(%s, %d, %d) failed: %s"),
442                      db_filename, (int)get_client_uid(), (int)get_client_gid(), strerror(errno));
443         }
444     }
445
446     /*
447      * Move the file descriptor up high so it stays out of the way
448      * of other processing, e.g. sendbackup.
449      */
450     if (fd >= 0) {
451         i = 0;
452         fd_close[i++] = fd;
453         while((db_fd = dup(fd)) < MIN_DB_FD) {
454             fd_close[i++] = db_fd;
455         }
456         while(--i >= 0) {
457             close(fd_close[i]);
458         }
459         db_file = fdopen(db_fd, "a");
460     }
461
462     if (annotation) {
463         /*
464          * Make the first debug log file entry.
465          */
466         debug_printf(_("pid %ld ruid %ld euid %ld version %s: %s at %s"),
467                      (long)getpid(),
468                      (long)getuid(), (long)geteuid(),
469                      VERSION,
470                      annotation,
471                      ctime(&open_time));
472     }
473 }
474
475 /* Get current GMT time and return a message timestamp.
476  * Used for g_printf calls to logs and such.  The return value
477  * is to a static buffer, so it should be used immediately.
478  *
479  * @returns: timestamp
480  */
481 static char *
482 msg_timestamp(void)
483 {
484     static char  timestamp[128];
485     char        *r;
486     time_t       curtime;
487
488     time(&curtime);
489     ctime_r(&curtime, timestamp);
490     r = strchr(timestamp, '\n');
491     if (r)
492         *r = '\0';
493
494     return timestamp;
495 }
496
497 /*
498  * ---- public functions
499  */
500
501 void
502 debug_init(void)
503 {
504     debug_setup_logging();
505
506     /* the scriptutil context does not create a debug log, since such
507      * processes are invoked many times.
508      */
509     if (get_pcontext() != CONTEXT_SCRIPTUTIL) {
510         debug_open(get_ptype());
511     }
512 }
513
514 void
515 debug_open(char *subdir)
516 {
517     int fd = -1;
518     int i;
519     char *s = NULL;
520     mode_t mask;
521
522     /* set up logging while we're here */
523     debug_setup_logging();
524
525     /* set 'dbgdir' and clean out old debug files */
526     debug_setup_1(NULL, subdir);
527
528     /*
529      * Create the new file with a unique sequence number.
530      */
531     mask = (mode_t)umask((mode_t)0037); /* Allow the group read bit through */
532
533     /* iteratate through sequence numbers until we find one that
534      * is not already in use */
535     for(i = 0; fd < 0; i++) {
536         amfree(db_name);
537         if ((db_name = get_debug_name(open_time, i)) == NULL) {
538             error(_("Cannot create debug file name in %d tries."), i);
539             /*NOTREACHED*/
540         }
541
542         if ((s = newvstralloc(s, dbgdir, db_name, NULL)) == NULL) {
543             error(_("Cannot allocate debug file name memory"));
544             /*NOTREACHED*/
545         }
546
547         if ((fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0640)) < 0) {
548             if (errno != EEXIST) {
549                 error(_("Cannot create debug file \"%s\": %s"),
550                         s, strerror(errno));
551                 /*NOTREACHED*/
552             }
553             amfree(s);
554         }
555     }
556     (void)umask(mask); /* Restore mask */
557
558     /*
559      * Finish setup.
560      *
561      * Note: we release control of the string 's' points to.
562      */
563     debug_setup_2(s, fd, "start");
564 }
565
566 void
567 debug_reopen(
568     char *      dbfilename,
569     char *      annotation)
570 {
571     char *s = NULL;
572     int fd;
573
574     if (dbfilename == NULL) {
575         return;
576     }
577
578     /* set 'dbgdir' and clean out old debug files */
579     debug_setup_1(NULL, NULL);
580
581     /*
582      * Reopen the file.
583      */
584     if (*dbfilename == '/') {
585         s = stralloc(dbfilename);
586     } else {
587         s = newvstralloc(s, dbgdir, dbfilename, NULL);
588     }
589     if ((fd = open(s, O_RDWR|O_APPEND)) < 0) {
590         error(_("cannot reopen debug file %s"), dbfilename);
591         /*NOTREACHED*/
592     }
593
594     /*
595      * Finish setup.
596      *
597      * Note: we release control of the string 's' points to.
598      */
599     debug_setup_2(s, fd, annotation);
600 }
601
602 void
603 debug_rename(
604     char *config,
605     char *subdir)
606 {
607     int fd = -1;
608     int i;
609     char *s = NULL;
610     mode_t mask;
611
612     if (!db_filename)
613         return;
614
615     if (get_pcontext() == CONTEXT_SCRIPTUTIL) {
616         return;
617     }
618
619     /* Remove old log from source directory */
620     debug_unlink_old();
621     /* set 'dbgdir' and clean out old debug files */
622     debug_setup_1(config, subdir);
623     /* Remove old log from destination directory */
624     debug_unlink_old();
625
626     s = newvstralloc(s, dbgdir, db_name, NULL);
627
628     if (strcmp(db_filename, s) == 0) {
629         amfree(s);
630         return;
631     }
632
633     mask = (mode_t)umask((mode_t)0037);
634
635 #if defined(__CYGWIN__)
636     /*
637      * On cygwin, rename will not overwrite an existing file nor
638      * will it rename a file that is open for writing...
639      *
640      * Rename file directly.  Expect failure if file already exists
641      * or is open by another user.
642      */
643
644     i = 0;
645     while (rename(db_filename, s) < 0) {
646         if (errno != EEXIST) {
647             /*
648              * If the failure was not due to the target file name already
649              * existing then we have bigger issues at hand so we keep 
650              * the existing file.
651              */
652             dbprintf(_("Cannot rename \"%s\" to \"%s\": %s\n"),
653                      db_filename, s, strerror(errno));
654             s = newvstralloc(s, db_filename, NULL);
655             i = -1;
656             break;
657         }
658
659         /*
660          * Files already exists:
661          * Continue searching for a unique file name that will work.
662          */
663         amfree(db_name);
664         if ((db_name = get_debug_name(open_time, i++)) == NULL) {
665             dbprintf(_("Cannot create unique debug file name"));
666             break;
667         }
668         s = newvstralloc(s, dbgdir, db_name, NULL);
669     }
670     if (i >= 0) {
671         /*
672          * We need to close and reopen the original file handle to
673          * release control of the original debug file name.
674          */
675         if ((fd = open(s, O_WRONLY|O_APPEND, 0640)) >= 0) {
676             /*
677              * We can safely close the the original log file
678              * since we now have a new working handle.
679              */
680             db_fd = 2;
681             fclose(db_file);
682             db_file = NULL;
683         }
684     }
685 #else
686     /* check if a file with the same name already exists. */
687     if ((fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0640)) < 0) {
688         for(i = 0; fd < 0; i++) {
689             amfree(db_name);
690             if ((db_name = get_debug_name(open_time, i)) == NULL) {
691                 dbprintf(_("Cannot create debug file"));
692                 break;
693             }
694
695             s = newvstralloc(s, dbgdir, db_name, NULL);
696             if ((fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0640)) < 0) {
697                 if (errno != EEXIST) {
698                     dbprintf(_("Cannot create debug file: %s"),
699                               strerror(errno));
700                     break;
701                 }
702             }
703         }
704     }
705
706     if (fd >= 0) {
707         close(fd);
708         if (rename(db_filename, s) == -1) {
709             dbprintf(_("Cannot rename \"%s\" to \"%s\": %s\n"),
710                      db_filename, s, strerror(errno));
711         }
712         fd = -1;
713     }
714 #endif
715
716     (void)umask(mask); /* Restore mask */
717     /*
718      * Finish setup.
719      *
720      * Note: we release control of the string 's' points to.
721      */
722     debug_setup_2(s, fd, "rename");
723 }
724
725 void
726 debug_close(void)
727 {
728     time_t curtime;
729
730     if (get_pcontext() == CONTEXT_SCRIPTUTIL) {
731         return;
732     }
733
734     debug_unlink_old();
735
736     time(&curtime);
737     debug_printf(_("pid %ld finish time %s"), (long)getpid(), ctime(&curtime));
738
739     if(db_file && fclose(db_file) == EOF) {
740         int save_errno = errno;
741
742         db_file = NULL;                         /* prevent recursion */
743         g_fprintf(stderr, _("close debug file: %s"), strerror(save_errno));
744         /*NOTREACHED*/
745     }
746     db_fd = 2;
747     db_file = NULL;
748     amfree(db_filename);
749     amfree(db_name);
750 }
751
752 /*
753  * Format and write a debug message to the process debug file.
754  */
755 printf_arglist_function(void debug_printf, const char *, format)
756 {
757     va_list argp;
758     int save_errno;
759
760     /*
761      * It is common in the code to call dbprintf to write out
762      * syserrno(errno) and then turn around and try to do something else
763      * with errno (e.g. g_printf() or log()), so we make sure errno goes
764      * back out with the same value it came in with.
765      */
766
767     save_errno = errno;
768
769     /* handle the default (stderr) if debug_open hasn't been called yet */
770     if(db_file == NULL && db_fd == 2) {
771         db_file = stderr;
772     }
773     if(db_file != NULL) {
774         char *prefix;
775         char *text;
776
777         if (db_file != stderr)
778             prefix = g_strdup_printf("%s: %s:", msg_timestamp(), get_pname());
779         else 
780             prefix = g_strdup_printf("%s:", get_pname());
781         arglist_start(argp, format);
782         text = g_strdup_vprintf(format, argp);
783         arglist_end(argp);
784         fprintf(db_file, "%s %s", prefix, text);
785         amfree(prefix);
786         amfree(text);
787         fflush(db_file);
788     }
789     errno = save_errno;
790 }
791
792 int
793 debug_fd(void)
794 {
795     return db_fd;
796 }
797
798 FILE *
799 debug_fp(void)
800 {
801     return db_file;
802 }
803
804 char *
805 debug_fn(void)
806 {
807     return db_filename;
808 }
809
810 void
811 debug_dup_stderr_to_debug(void)
812 {
813     if(db_fd != -1 && db_fd != STDERR_FILENO)
814     {
815        if(dup2(db_fd, STDERR_FILENO) != STDERR_FILENO)
816        {
817            error(_("can't redirect stderr to the debug file: %d, %s"), db_fd, strerror(errno));
818            g_assert_not_reached();
819        }
820     }
821 }
822