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