ce546bc37801dd89a44c482bf2d30a47cfee2696
[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.17.4.3.4.3.2.9.2.1 2005/09/20 19:06:37 jrjackson Exp $
29  *
30  * debug log subroutines
31  */
32
33 #include "amanda.h"
34 #include "clock.h"
35 #include "util.h"
36 #include "arglist.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_filename = NULL;
51
52 static pid_t debug_prefix_pid = 0;
53
54 /*
55  * Format and write a debug message to the process debug file.
56  */
57 printf_arglist_function(void debug_printf, char *, format)
58 {
59     va_list argp;
60     int save_errno;
61
62     /*
63      * It is common in the code to call dbprintf to write out
64      * syserrno(errno) and then turn around and try to do something else
65      * with errno (e.g. printf() or log()), so we make sure errno goes
66      * back out with the same value it came in with.
67      */
68     save_errno = errno;
69
70     if(db_file == NULL && db_fd == 2) {
71         db_file = stderr;
72     }
73     if(db_file != NULL) {
74         arglist_start(argp, format);
75         vfprintf(db_file, format, argp);
76         fflush(db_file);
77         arglist_end(argp);
78     }
79
80     errno = save_errno;
81 }
82
83 /*
84  * Generate a debug file name.  The name is based on the program name,
85  * followed by a timestamp, an optional sequence number, and ".debug".
86  */
87 static char *
88 get_debug_name(t, n)
89     time_t t;
90     int n;
91 {
92     char number[NUM_STR_SIZE];
93     char *ts;
94     char *result;
95
96     if(n < 0 || n > 1000) {
97         return NULL;
98     }
99     ts = construct_timestamp(&t);
100     if(n == 0) {
101         number[0] = '\0';
102     } else {
103         ap_snprintf(number, sizeof(number), "%03d", n - 1);
104     }
105     result = vstralloc(get_pname(), ".", ts, number, ".debug", NULL);
106     amfree(ts);
107     return result;
108 }
109
110 static char *dbgdir = NULL;
111 static time_t curtime;
112
113 static void debug_setup_1()
114 {
115     struct passwd *pwent;
116     char *pname;
117     size_t pname_len;
118     char *e = NULL;
119     char *s = NULL;
120     DIR *d;
121     struct dirent *entry;
122     int do_rename;
123     char *test_name = NULL;
124     size_t test_name_len;
125     size_t d_name_len;
126     struct stat sbuf;
127     char *dbfilename = NULL;
128     int i;
129
130     if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
131         client_uid = pwent->pw_uid;
132         client_gid = pwent->pw_gid;
133         endpwent();
134     }
135
136     pname = get_pname();
137     pname_len = strlen(pname);
138
139     /*
140      * Create the debug directory if it does not yet exist.
141      */
142     amfree(dbgdir);
143     dbgdir = stralloc2(AMANDA_DBGDIR, "/");
144     if(mkpdir(dbgdir, 02700, client_uid, client_gid) == -1) {
145         error("create debug directory \"%s\": %s",
146               AMANDA_DBGDIR, strerror(errno));
147     }
148
149     /*
150      * Clean out old debug files.  We also rename files with old style
151      * names (XXX.debug or XXX.$PID.debug) into the new name format.
152      * We assume no system has 17 digit PID-s :-) and that there will
153      * not be a conflict between an old and new name.
154      */
155     if((d = opendir(AMANDA_DBGDIR)) == NULL) {
156         error("open debug directory \"%s\": %s",
157               AMANDA_DBGDIR, strerror(errno));
158     }
159     time(&curtime);
160     test_name = get_debug_name(curtime - (AMANDA_DEBUG_DAYS * 24 * 60 * 60), 0);
161     test_name_len = strlen(test_name);
162     while((entry = readdir(d)) != NULL) {
163         if(is_dot_or_dotdot(entry->d_name)) {
164             continue;
165         }
166         d_name_len = strlen(entry->d_name);
167         if(strncmp(entry->d_name, pname, pname_len) != 0
168            || entry->d_name[pname_len] != '.'
169            || d_name_len < 6
170            || strcmp(entry->d_name + d_name_len - 6, ".debug") != 0) {
171             continue;                           /* not one of our debug files */
172         }
173         e = newvstralloc(e, dbgdir, entry->d_name, NULL);
174         if(d_name_len < test_name_len) {
175             /*
176              * Create a "pretend" name based on the last modification
177              * time.  This name will be used to decide if the real name
178              * should be removed.  If not, it will be used to rename the
179              * real name.
180              */
181             if(stat(e, &sbuf) != 0) {
182                 continue;                       /* ignore errors */
183             }
184             amfree(dbfilename);
185             dbfilename = get_debug_name((time_t)sbuf.st_mtime, 0);
186             do_rename = 1;
187         } else {
188             dbfilename = newstralloc(dbfilename, entry->d_name);
189             do_rename = 0;
190         }
191         if(strcmp(dbfilename, test_name) < 0) {
192             (void) unlink(e);                   /* get rid of old file */
193             continue;
194         }
195         if(do_rename) {
196             i = 0;
197             while(dbfilename != NULL
198                   && (s = newvstralloc(s, dbgdir, dbfilename, NULL)) != NULL
199                   && rename(e, s) != 0 && errno != ENOENT) {
200                 amfree(dbfilename);
201                 dbfilename = get_debug_name((time_t)sbuf.st_mtime, ++i);
202             }
203             if(dbfilename == NULL) {
204                 error("cannot rename old debug file \"%s\"", entry->d_name);
205             }
206         }
207     }
208     amfree(dbfilename);
209     amfree(e);
210     amfree(s);
211     amfree(test_name);
212     closedir(d);
213 }
214
215 static void debug_setup_2(s, fd, notation)
216     char *s;
217     int fd;
218     char *notation;
219 {
220     int saved_debug;
221     int i;
222     int fd_close[MIN_DB_FD+1];
223
224     amfree(db_filename);
225     db_filename = s;
226     s = NULL;
227     (void) chown(db_filename, client_uid, client_gid);
228     amfree(dbgdir);
229     /*
230      * Move the file descriptor up high so it stays out of the way
231      * of other processing, e.g. sendbackup.
232      */
233     i = 0;
234     fd_close[i++] = fd;
235     while((db_fd = dup(fd)) < MIN_DB_FD) {
236         fd_close[i++] = db_fd;
237     }
238     while(--i >= 0) {
239         close(fd_close[i]);
240     }
241     db_file = fdopen(db_fd, "a");
242
243     if (notation) {
244         /*
245          * Make the first debug log file entry.
246          */
247         saved_debug = debug; debug = 1;
248         debug_printf("%s: debug %d pid %ld ruid %ld euid %ld: %s at %s",
249                      get_pname(), saved_debug, (long)getpid(),
250                      (long)getuid(), (long)geteuid(),
251                      notation,
252                      ctime(&curtime));
253         debug = saved_debug;
254     }
255 }
256
257 void debug_open()
258 {
259     char *dbfilename = NULL;
260     int fd = -1;
261     int i;
262     char *s = NULL;
263
264     /*
265      * Do initial setup.
266      */
267     debug_setup_1();
268
269     /*
270      * Create the new file.
271      */
272     for(i = 0;
273         (dbfilename = get_debug_name(curtime, i)) != NULL
274         && (s = newvstralloc(s, dbgdir, dbfilename, NULL)) != NULL
275         && (fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0600)) < 0;
276         i++, free(dbfilename)) {}
277     if(dbfilename == NULL) {
278         error("cannot create %s debug file", get_pname());
279     }
280     amfree(dbfilename);
281
282     /*
283      * Finish setup.
284      *
285      * Note: we release control of the string 's' points to.
286      */
287     debug_setup_2(s, fd, "start");
288 }
289
290 void debug_reopen(dbfilename, notation)
291     char *dbfilename;
292     char *notation;
293 {
294     char *s = NULL;
295     int fd = -1;
296
297     if (dbfilename == NULL) {
298         return;
299     }
300
301     /*
302      * Do initial setup.
303      */
304     debug_setup_1();
305
306     /*
307      * Reopen the file.
308      */
309     if (*dbfilename == '/') {
310         s = stralloc(dbfilename);
311     } else {
312         s = newvstralloc(s, dbgdir, dbfilename, NULL);
313     }
314     if ((fd = open(s, O_RDWR|O_APPEND, 0600)) < 0) {
315         error("cannot reopen %s debug file %s", get_pname(), dbfilename);
316     }
317
318     /*
319      * Finish setup.
320      *
321      * Note: we release control of the string 's' points to.
322      */
323     debug_setup_2(s, fd, notation);
324 }
325
326 void debug_close()
327 {
328     time_t curtime;
329     int save_debug;
330     pid_t save_pid;
331
332     time(&curtime);
333     save_debug = debug;
334     debug = 1;
335     save_pid = debug_prefix_pid;
336     debug_prefix_pid = 0;
337     debug_printf("%s: pid %ld finish time %s",
338                  debug_prefix_time(NULL),
339                  (long)getpid(),
340                  ctime(&curtime));
341     debug_prefix_pid = save_pid;
342     debug = save_debug;
343
344     if(db_file && fclose(db_file) == EOF) {
345         int save_errno = errno;
346
347         db_file = NULL;                         /* prevent recursion */
348         error("close debug file: %s", strerror(save_errno));
349     }
350     db_fd = -1;
351     db_file = NULL;
352     amfree(db_filename);
353 }
354
355 int debug_fd()
356 {
357     return db_fd;
358 }
359
360 FILE *debug_fp()
361 {
362     return db_file;
363 }
364
365 char *debug_fn()
366 {
367     return db_filename;
368 }
369
370 /*
371  * Routines for returning a common debug file line prefix.  Always starts
372  * with the current program name, possibly with an optional suffix.
373  * May then be followed by a PID.  May then be followed by an elapsed
374  * time indicator.
375  */ 
376
377 void set_debug_prefix_pid(p)
378     pid_t p;
379 {
380     debug_prefix_pid = p;
381 }
382
383 char *debug_prefix(suffix)
384     char *suffix;
385 {
386     static char *s = NULL;
387     char debug_pid[NUM_STR_SIZE];
388
389     s = newvstralloc(s, get_pname(), suffix, NULL);
390     if (debug_prefix_pid != (pid_t) 0) {
391         ap_snprintf(debug_pid, sizeof(debug_pid),
392                     "%ld",
393                     (long) debug_prefix_pid);
394         s = newvstralloc(s, s, "[", debug_pid, "]", NULL);
395     }
396     return s;
397 }
398
399 char *debug_prefix_time(suffix)
400     char *suffix;
401 {
402     static char *s = NULL;
403     char *t1;
404     char *t2;
405
406     if (clock_is_running()) {
407         t1 = ": time ";
408         t2 = walltime_str(curclock());
409     } else {
410         t1 = t2 = NULL;
411     }
412
413     s = newvstralloc(s, debug_prefix(suffix), t1, t2, NULL);
414
415     return s;
416 }
417 #endif