Imported Upstream version 2.4.4p3
[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 2003/01/04 17:46:09 martinea 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 void debug_open()
111 {
112     time_t curtime;
113     int saved_debug;
114     char *dbgdir = NULL;
115     char *e = NULL;
116     char *s = NULL;
117     char *dbfilename = NULL;
118     DIR *d;
119     struct dirent *entry;
120     char *pname;
121     size_t pname_len;
122     int do_rename;
123     char *test_name = NULL;
124     size_t test_name_len;
125     int fd = -1;
126     int i;
127     size_t d_name_len;
128     int fd_close[MIN_DB_FD+1];
129     struct passwd *pwent;
130     struct stat sbuf;
131
132     pname = get_pname();
133     pname_len = strlen(pname);
134
135     if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
136         client_uid = pwent->pw_uid;
137         client_gid = pwent->pw_gid;
138         endpwent();
139     }
140
141     /*
142      * Create the debug directory if it does not yet exist.
143      */
144     dbgdir = stralloc2(AMANDA_DBGDIR, "/");
145     if(mkpdir(dbgdir, 02700, client_uid, client_gid) == -1) {
146         error("create debug directory \"%s\": %s",
147               AMANDA_DBGDIR, strerror(errno));
148     }
149
150     /*
151      * Clean out old debug files.  We also rename files with old style
152      * names (XXX.debug or XXX.$PID.debug) into the new name format.
153      * We assume no system has 17 digit PID-s :-) and that there will
154      * not be a conflict between an old and new name.
155      */
156     if((d = opendir(AMANDA_DBGDIR)) == NULL) {
157         error("open debug directory \"%s\": %s",
158               AMANDA_DBGDIR, strerror(errno));
159     }
160     time(&curtime);
161     test_name = get_debug_name(curtime - (AMANDA_DEBUG_DAYS * 24 * 60 * 60), 0);
162     test_name_len = strlen(test_name);
163     while((entry = readdir(d)) != NULL) {
164         if(is_dot_or_dotdot(entry->d_name)) {
165             continue;
166         }
167         d_name_len = strlen(entry->d_name);
168         if(strncmp(entry->d_name, pname, pname_len) != 0
169            || entry->d_name[pname_len] != '.'
170            || d_name_len < 6
171            || strcmp(entry->d_name + d_name_len - 6, ".debug") != 0) {
172             continue;                           /* not one of our debug files */
173         }
174         e = newvstralloc(e, dbgdir, entry->d_name, NULL);
175         if(d_name_len < test_name_len) {
176             /*
177              * Create a "pretend" name based on the last modification
178              * time.  This name will be used to decide if the real name
179              * should be removed.  If not, it will be used to rename the
180              * real name.
181              */
182             if(stat(e, &sbuf) != 0) {
183                 continue;                       /* ignore errors */
184             }
185             amfree(dbfilename);
186             dbfilename = get_debug_name((time_t)sbuf.st_mtime, 0);
187             do_rename = 1;
188         } else {
189             dbfilename = newstralloc(dbfilename, entry->d_name);
190             do_rename = 0;
191         }
192         if(strcmp(dbfilename, test_name) < 0) {
193             (void) unlink(e);                   /* get rid of old file */
194             continue;
195         }
196         if(do_rename) {
197             i = 0;
198             while(dbfilename != NULL
199                   && (s = newvstralloc(s, dbgdir, dbfilename, NULL)) != NULL
200                   && rename(e, s) != 0 && errno != ENOENT) {
201                 amfree(dbfilename);
202                 dbfilename = get_debug_name((time_t)sbuf.st_mtime, ++i);
203             }
204             if(dbfilename == NULL) {
205                 error("cannot rename old debug file \"%s\"", entry->d_name);
206             }
207         }
208     }
209     amfree(dbfilename);
210     amfree(e);
211     amfree(s);
212     amfree(test_name);
213     closedir(d);
214
215     /*
216      * Create the new file.
217      */
218     for(i = 0;
219         (dbfilename = get_debug_name(curtime, i)) != NULL
220         && (s = newvstralloc(s, dbgdir, dbfilename, NULL)) != NULL
221         && (fd = open(s, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0600)) < 0;
222         i++, free(dbfilename)) {}
223     if(dbfilename == NULL) {
224         error("cannot create %s debug file", get_pname());
225     }
226     amfree(dbfilename);
227     amfree(db_filename);
228     db_filename = s;
229     s = NULL;
230     (void) chown(db_filename, client_uid, client_gid);
231     amfree(dbgdir);
232
233     /*
234      * Move the file descriptor up high so it stays out of the way
235      * of other processing, e.g. sendbackup.
236      */
237     i = 0;
238     fd_close[i++] = fd;
239     while((db_fd = dup(fd)) < MIN_DB_FD) {
240         fd_close[i++] = db_fd;
241     }
242     while(--i >= 0) {
243         close(fd_close[i]);
244     }
245     db_file = fdopen(db_fd, "a");
246
247     /*
248      * Make the first debug log file entry.
249      */
250     saved_debug = debug; debug = 1;
251     debug_printf("%s: debug %d pid %ld ruid %ld euid %ld: start at %s",
252                  pname, saved_debug, (long)getpid(),
253                  (long)getuid(), (long)geteuid(),
254                  ctime(&curtime));
255     debug = saved_debug;
256 }
257
258 void debug_close()
259 {
260     time_t curtime;
261     int save_debug;
262     pid_t save_pid;
263
264     time(&curtime);
265     save_debug = debug;
266     debug = 1;
267     save_pid = debug_prefix_pid;
268     debug_prefix_pid = 0;
269     debug_printf("%s: pid %ld finish time %s",
270                  debug_prefix_time(NULL),
271                  (long)getpid(),
272                  ctime(&curtime));
273     debug_prefix_pid = save_pid;
274     debug = save_debug;
275
276     if(db_file && fclose(db_file) == EOF) {
277         int save_errno = errno;
278
279         db_file = NULL;                         /* prevent recursion */
280         error("close debug file: %s", strerror(save_errno));
281     }
282     db_fd = -1;
283     db_file = NULL;
284     amfree(db_filename);
285 }
286
287 int debug_fd()
288 {
289     return db_fd;
290 }
291
292 FILE *debug_fp()
293 {
294     return db_file;
295 }
296
297 char *debug_fn()
298 {
299     return db_filename;
300 }
301
302 /*
303  * Routines for returning a common debug file line prefix.  Always starts
304  * with the current program name, possibly with an optional suffix.
305  * May then be followed by a PID.  May then be followed by an elapsed
306  * time indicator.
307  */ 
308
309 void set_debug_prefix_pid(p)
310     pid_t p;
311 {
312     debug_prefix_pid = p;
313 }
314
315 char *debug_prefix(suffix)
316     char *suffix;
317 {
318     static char *s = NULL;
319     char debug_pid[NUM_STR_SIZE];
320
321     s = newvstralloc(s, get_pname(), suffix, NULL);
322     if (debug_prefix_pid != (pid_t) 0) {
323         ap_snprintf(debug_pid, sizeof(debug_pid),
324                     "%ld",
325                     (long) debug_prefix_pid);
326         s = newvstralloc(s, s, "[", debug_pid, "]", NULL);
327     }
328     return s;
329 }
330
331 char *debug_prefix_time(suffix)
332     char *suffix;
333 {
334     static char *s = NULL;
335     char *t1;
336     char *t2;
337
338     if (clock_is_running()) {
339         t1 = ": time ";
340         t2 = walltime_str(curclock());
341     } else {
342         t1 = t2 = NULL;
343     }
344
345     s = newvstralloc(s, debug_prefix(suffix), t1, t2, NULL);
346
347     return s;
348 }
349 #endif