Imported Upstream version 2.5.2p1
[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  * debug log subroutines
31  */
32
33 #include "amanda.h"
34 #include "util.h"
35 #include "arglist.h"
36 #include "clock.h"
37
38 #ifndef AMANDA_DBGDIR
39 #  define AMANDA_DBGDIR         AMANDA_TMPDIR
40 #endif
41
42 #ifdef DEBUG_CODE
43
44 int debug = 1;
45
46 #define MIN_DB_FD                       10
47
48 static int db_fd = 2;                   /* default is stderr */
49 static FILE *db_file = NULL;            /* stderr may not be a constant */
50 static char *db_name  = NULL;           /* filename */
51 static char *db_filename = NULL;        /* /path/to/filename */
52
53 static pid_t debug_prefix_pid = 0;
54 static char *get_debug_name(time_t t, int n);
55 static void debug_setup_1(char *config, char *subdir);
56 static void debug_setup_2(char *s, int fd, char *notation);
57 static times_t debug_start_time;
58 static int debug_clock_is_running = 0;
59
60 /*
61  * Format and write a debug message to the process debug file.
62  */
63 printf_arglist_function(void debug_printf, const char *, format)
64 {
65     va_list argp;
66
67     /*
68      * It is common in the code to call dbprintf to write out
69      * syserrno(errno) and then turn around and try to do something else
70      * with errno (e.g. printf() or log()), so we make sure errno goes
71      * back out with the same value it came in with.
72      */
73     if (debug != 0) {
74         int save_errno;
75
76         save_errno = errno;
77         if(db_file == NULL && db_fd == 2) {
78             db_file = stderr;
79         }
80         if(db_file != NULL) {
81             arglist_start(argp, format);
82             vfprintf(db_file, format, argp);
83             fflush(db_file);
84             arglist_end(argp);
85         }
86         errno = save_errno;
87     }
88 }
89
90 /*
91  * Generate a debug file name.  The name is based on the program name,
92  * followed by a timestamp, an optional sequence number, and ".debug".
93  */
94 static char *
95 get_debug_name(
96     time_t      t,
97     int         n)
98 {
99     char number[NUM_STR_SIZE];
100     char *ts;
101     char *result;
102
103     if(n < 0 || n > 1000) {
104         return NULL;
105     }
106     ts = construct_timestamp(&t);
107     if(n == 0) {
108         number[0] = '\0';
109     } else {
110         snprintf(number, SIZEOF(number), "%03d", n - 1);
111     }
112     result = vstralloc(get_pname(), ".", ts, number, ".debug", NULL);
113     amfree(ts);
114     return result;
115 }
116
117 static char *dbgdir = NULL;
118 static time_t curtime;
119
120 static void
121 debug_setup_1(char *config, char *subdir)
122 {
123     struct passwd *pwent;
124     char *pname;
125     size_t pname_len;
126     char *e = NULL;
127     char *s = NULL;
128     DIR *d;
129     struct dirent *entry;
130     int do_rename;
131     char *test_name;
132     size_t test_name_len;
133     size_t d_name_len;
134     struct stat sbuf;
135     char *dbfilename = NULL;
136     char *sane_config = NULL;
137     int i;
138
139     memset(&sbuf, 0, SIZEOF(sbuf));
140     if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
141         client_uid = pwent->pw_uid;
142         client_gid = pwent->pw_gid;
143         endpwent();
144     }
145
146     pname = get_pname();
147     pname_len = strlen(pname);
148
149     /*
150      * Create the debug directory if it does not yet exist.
151      */
152     amfree(dbgdir);
153     if (config)
154         sane_config = sanitise_filename(config);
155     if (sane_config && subdir)
156         dbgdir = vstralloc(AMANDA_DBGDIR, "/", subdir, "/", sane_config,
157                            "/", NULL);
158     else if (sane_config)
159         dbgdir = vstralloc(AMANDA_DBGDIR, "/", sane_config, "/", NULL);
160     else if (subdir)
161         dbgdir = vstralloc(AMANDA_DBGDIR, "/", subdir, "/", NULL);
162     else
163         dbgdir = stralloc2(AMANDA_DBGDIR, "/");
164     if(mkpdir(dbgdir, 02700, client_uid, client_gid) == -1) {
165         error("create debug directory \"%s\": %s",
166               dbgdir, strerror(errno));
167         /*NOTREACHED*/
168     }
169     amfree(sane_config);
170
171     /*
172      * Clean out old debug files.  We also rename files with old style
173      * names (XXX.debug or XXX.$PID.debug) into the new name format.
174      * We assume no system has 17 digit PID-s :-) and that there will
175      * not be a conflict between an old and new name.
176      */
177     if((d = opendir(dbgdir)) == NULL) {
178         error("open debug directory \"%s\": %s",
179               dbgdir, strerror(errno));
180         /*NOTREACHED*/
181     }
182     time(&curtime);
183     test_name = get_debug_name(curtime - (AMANDA_DEBUG_DAYS * 24 * 60 * 60), 0);
184     test_name_len = strlen(test_name);
185     while((entry = readdir(d)) != NULL) {
186         if(is_dot_or_dotdot(entry->d_name)) {
187             continue;
188         }
189         d_name_len = strlen(entry->d_name);
190         if(strncmp(entry->d_name, pname, pname_len) != 0
191            || entry->d_name[pname_len] != '.'
192            || d_name_len < 6
193            || strcmp(entry->d_name + d_name_len - 6, ".debug") != 0) {
194             continue;                           /* not one of our debug files */
195         }
196         e = newvstralloc(e, dbgdir, entry->d_name, NULL);
197         if(d_name_len < test_name_len) {
198             /*
199              * Create a "pretend" name based on the last modification
200              * time.  This name will be used to decide if the real name
201              * should be removed.  If not, it will be used to rename the
202              * real name.
203              */
204             if(stat(e, &sbuf) != 0) {
205                 continue;                       /* ignore errors */
206             }
207             amfree(dbfilename);
208             dbfilename = get_debug_name((time_t)sbuf.st_mtime, 0);
209             do_rename = 1;
210         } else {
211             dbfilename = newstralloc(dbfilename, entry->d_name);
212             do_rename = 0;
213         }
214         if(strcmp(dbfilename, test_name) < 0) {
215             (void) unlink(e);                   /* get rid of old file */
216             continue;
217         }
218         if(do_rename) {
219             i = 0;
220             while(dbfilename != NULL
221                   && (s = newvstralloc(s, dbgdir, dbfilename, NULL)) != NULL
222                   && rename(e, s) != 0 && errno != ENOENT) {
223                 amfree(dbfilename);
224                 dbfilename = get_debug_name((time_t)sbuf.st_mtime, ++i);
225             }
226             if(dbfilename == NULL) {
227                 error("cannot rename old debug file \"%s\"", entry->d_name);
228                 /*NOTREACHED*/
229             }
230         }
231     }
232     amfree(dbfilename);
233     amfree(e);
234     amfree(s);
235     amfree(test_name);
236     closedir(d);
237 }
238
239 static void
240 debug_setup_2(
241     char *      s,
242     int         fd,
243     char *      notation)
244 {
245     int saved_debug;
246     int i, rc;
247     int fd_close[MIN_DB_FD+1];
248
249     amfree(db_filename);
250     db_filename = s;
251     s = NULL;
252     if ((rc = chown(db_filename, client_uid, client_gid)) < 0) {
253         dbprintf(("chown(%s, %d, %d) failed. <%s>",
254                   db_filename, client_uid, client_gid, strerror(errno)));
255         (void)rc;
256     }
257     amfree(dbgdir);
258     /*
259      * Move the file descriptor up high so it stays out of the way
260      * of other processing, e.g. sendbackup.
261      */
262     if (fd >= 0) {
263         i = 0;
264         fd_close[i++] = fd;
265         while((db_fd = dup(fd)) < MIN_DB_FD) {
266             fd_close[i++] = db_fd;
267         }
268         while(--i >= 0) {
269             close(fd_close[i]);
270         }
271         db_file = fdopen(db_fd, "a");
272     }
273
274     if (notation) {
275         /*
276          * Make the first debug log file entry.
277          */
278         saved_debug = debug; debug = 1;
279         debug_printf("%s: debug %d pid %ld ruid %ld euid %ld: %s at %s",
280                      get_pname(), saved_debug, (long)getpid(),
281                      (long)getuid(), (long)geteuid(),
282                      notation,
283                      ctime(&curtime));
284         debug = saved_debug;
285     }
286 }
287
288 void
289 debug_open(char *subdir)
290 {
291     int fd = -1;
292     int i;
293     char *s = NULL;
294     mode_t mask;
295     amanda_timezone dontcare;
296
297     amanda_gettimeofday(&debug_start_time.r, &dontcare);
298     debug_clock_is_running = 1;
299
300     /*
301      * Do initial setup.
302      */
303     debug_setup_1(NULL, subdir);
304
305     /*
306      * Create the new file with a unique sequence number.
307      */
308     mask = (mode_t)umask((mode_t)0037); /* Allow the group read bit through */
309     for(i = 0; fd < 0; i++) {
310         amfree(db_name);
311         if ((db_name = get_debug_name(curtime, i)) == NULL) {
312             error("Cannot create %s debug file", get_pname());
313             /*NOTREACHED*/
314         }
315
316         if ((s = newvstralloc(s, dbgdir, db_name, NULL)) == NULL) {
317             error("Cannot allocate %s debug file name memory", get_pname());
318             /*NOTREACHED*/
319         }
320
321         if ((fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0640)) < 0) {
322             if (errno != EEXIST) {
323                 error("Cannot create %s debug file: %s",
324                        get_pname(), strerror(errno));
325                 /*NOTREACHED*/
326             }
327             amfree(s);
328         }
329     }
330     (void)umask(mask); /* Restore mask */
331
332     /*
333      * Finish setup.
334      *
335      * Note: we release control of the string 's' points to.
336      */
337     debug_setup_2(s, fd, "start");
338 }
339
340 void
341 debug_reopen(
342     char *      dbfilename,
343     char *      notation)
344 {
345     char *s = NULL;
346     int fd;
347
348     if (dbfilename == NULL) {
349         return;
350     }
351
352     /*
353      * Do initial setup.
354      */
355     debug_setup_1(NULL, NULL);
356
357     /*
358      * Reopen the file.
359      */
360     if (*dbfilename == '/') {
361         s = stralloc(dbfilename);
362     } else {
363         s = newvstralloc(s, dbgdir, dbfilename, NULL);
364     }
365     if ((fd = open(s, O_RDWR|O_APPEND)) < 0) {
366         error("cannot reopen %s debug file %s", get_pname(), dbfilename);
367         /*NOTREACHED*/
368     }
369
370     /*
371      * Finish setup.
372      *
373      * Note: we release control of the string 's' points to.
374      */
375     debug_setup_2(s, fd, notation);
376 }
377
378 void
379 debug_rename(
380     char *config,
381     char *subdir)
382 {
383     int fd = -1;
384     int i;
385     char *s = NULL;
386     mode_t mask;
387
388     if (!db_filename)
389         return;
390
391     /*
392      * Do initial setup.
393      */
394     debug_setup_1(config, subdir);
395
396     s = newvstralloc(s, dbgdir, db_name, NULL);
397
398     if (strcmp(db_filename, s) == 0) {
399         amfree(s);
400         return;
401     }
402
403     mask = (mode_t)umask((mode_t)0037);
404     /* check if a file with the same name already exist */
405     if ((fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0640)) < 0) {
406         for(i = 0; fd < 0; i++) {
407             amfree(db_name);
408             if ((db_name = get_debug_name(curtime, i)) == NULL) {
409                 dbprintf(("Cannot create %s debug file", get_pname()));
410                 break;
411             }
412
413             s = newvstralloc(s, dbgdir, db_name, NULL);
414             if ((fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0640)) < 0) {
415                 if (errno != EEXIST) {
416                     dbprintf(("Cannot create %s debug file: %s", get_pname(),
417                               strerror(errno)));
418                     break;
419                 }
420             }
421         }
422     }
423
424     if (fd >= 0) {
425         rename(db_filename, s);
426     }
427     (void)umask(mask); /* Restore mask */
428     close(fd);
429     /*
430      * Finish setup.
431      *
432      * Note: we release control of the string 's' points to.
433      */
434     debug_setup_2(s, -1, "rename");
435 }
436
437 void
438 debug_close(void)
439 {
440     time_t curtime;
441     int save_debug;
442     pid_t save_pid;
443
444     time(&curtime);
445     save_debug = debug;
446     debug = 1;
447     save_pid = debug_prefix_pid;
448     debug_prefix_pid = 0;
449     debug_printf("%s: pid %ld finish time %s",
450                  debug_prefix_time(NULL),
451                  (long)getpid(),
452                  ctime(&curtime));
453     debug_prefix_pid = save_pid;
454     debug = save_debug;
455
456     if(db_file && fclose(db_file) == EOF) {
457         int save_errno = errno;
458
459         db_file = NULL;                         /* prevent recursion */
460         fprintf(stderr, "close debug file: %s", strerror(save_errno));
461         /*NOTREACHED*/
462     }
463     db_fd = -1;
464     db_file = NULL;
465     amfree(db_filename);
466 }
467
468 int
469 debug_fd(void)
470 {
471     return db_fd;
472 }
473
474 FILE *
475 debug_fp(void)
476 {
477     return db_file;
478 }
479
480 char *
481 debug_fn(void)
482 {
483     return db_filename;
484 }
485
486 /*
487  * Routines for returning a common debug file line prefix.  Always starts
488  * with the current program name, possibly with an optional suffix.
489  * May then be followed by a PID.  May then be followed by an elapsed
490  * time indicator.
491  */ 
492
493 void
494 set_debug_prefix_pid(
495     pid_t       p)
496 {
497     debug_prefix_pid = p;
498 }
499
500 char *
501 debug_prefix(
502     char *      suffix)
503 {
504     int save_errno;
505     static char *s = NULL;
506     char debug_pid[NUM_STR_SIZE];
507
508     save_errno = errno;
509     s = newvstralloc(s, get_pname(), suffix, NULL);
510     if (debug_prefix_pid != (pid_t) 0) {
511         snprintf(debug_pid, SIZEOF(debug_pid),
512                  "%ld",
513                  (long) debug_prefix_pid);
514         s = newvstralloc(s, s, "[", debug_pid, "]", NULL);
515     }
516     errno = save_errno;
517     return s;
518 }
519
520 char *
521 debug_prefix_time(
522     char *      suffix)
523 {
524     int save_errno;
525     static char *s = NULL;
526     char *t1;
527     char *t2;
528     times_t diff;
529     times_t debug_end_time;
530     amanda_timezone dontcare;
531
532     save_errno = errno;
533     if (debug_clock_is_running == 1) {
534         amanda_gettimeofday(&debug_end_time.r, &dontcare);
535         diff = timessub(debug_end_time,debug_start_time);
536         t1 = ": time ";
537         t2 = walltime_str(diff);
538     } else {
539         t1 = t2 = NULL;
540     }
541
542     s = newvstralloc(s, debug_prefix(suffix), t1, t2, NULL);
543
544     errno = save_errno;
545     return s;
546 }
547 #endif