03cc5f65858078c4d107ccfa10df48de2395eeac
[debian/amanda] / server-src / logfile.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: logfile.c,v 1.31 2006/06/01 14:54:39 martinea Exp $
29  *
30  * common log file writing routine
31  */
32 #include "amanda.h"
33 #include "arglist.h"
34 #include "util.h"
35 #include "conffile.h"
36
37 #include "logfile.h"
38
39 char *logtype_str[] = {
40     "BOGUS",
41     "FATAL",            /* program died for some reason, used by error() */
42     "ERROR", "WARNING", "INFO", "SUMMARY",       /* information messages */
43     "START", "FINISH",                             /* start/end of a run */
44     "DISK",                                                      /* disk */
45     /* the end of a dump */
46     "DONE", "PART", "PARTPARTIAL", "SUCCESS", "PARTIAL", "FAIL", "STRANGE",
47     "CHUNK", "CHUNKSUCCESS",                            /* ... continued */
48     "STATS",                                               /* statistics */
49     "MARKER",                                     /* marker for reporter */
50     "CONT"                                 /* continuation line; special */
51 };
52
53 char *program_str[] = {
54     "UNKNOWN", "planner", "driver", "amreport", "dumper", "chunker",
55     "taper", "amflush"
56 };
57
58 int curlinenum;
59 logtype_t curlog;
60 program_t curprog;
61 char *curstr;
62
63 int multiline = -1;
64 static char *logfile;
65 static int logfd = -1;
66
67  /*
68   * Note that technically we could use two locks, a read lock
69   * from 0-EOF and a write-lock from EOF-EOF, thus leaving the
70   * beginning of the file open for read-only access.  Doing so
71   * would open us up to some race conditions unless we're pretty
72   * careful, and on top of that the functions here are so far
73   * the only accesses to the logfile, so keep things simple.
74   */
75
76 /* local functions */
77 static void open_log(void);
78 static void close_log(void);
79
80 void
81 logerror(
82     char *      msg)
83 {
84     log_add(L_FATAL, "%s", msg);
85 }
86
87
88 printf_arglist_function2(char *log_genstring, logtype_t, typ, char *, pname, char *, format)
89 {
90     va_list argp;
91     char *leader = NULL;
92     char linebuf[STR_SIZE];
93     char *xlated_fmt = dgettext("C", format);
94
95     /* format error message */
96
97     if((int)typ <= (int)L_BOGUS || (int)typ > (int)L_MARKER) typ = L_BOGUS;
98
99     if(multiline > 0) {
100         leader = stralloc("  ");                /* continuation line */
101     } else {
102         leader = vstralloc(logtype_str[(int)typ], " ", pname, " ", NULL);
103     }
104
105     arglist_start(argp, format);
106     g_vsnprintf(linebuf, SIZEOF(linebuf)-1, xlated_fmt, argp);
107                                                 /* -1 to allow for '\n' */
108     arglist_end(argp);
109     return(vstralloc(leader, linebuf, "\n", NULL));
110 }
111
112 printf_arglist_function1(void log_add, logtype_t, typ, char *, format)
113 {
114     va_list argp;
115     int saved_errout;
116     char *leader = NULL;
117     char *xlated_fmt = gettext(format);
118     char linebuf[STR_SIZE];
119     size_t n;
120
121     /* format error message */
122
123     if((int)typ <= (int)L_BOGUS || (int)typ > (int)L_MARKER) typ = L_BOGUS;
124
125     if(multiline > 0) {
126         leader = stralloc("  ");                /* continuation line */
127     } else {
128         leader = vstralloc(logtype_str[(int)typ], " ", get_pname(), " ", NULL);
129     }
130
131     arglist_start(argp, format);
132     g_vsnprintf(linebuf, SIZEOF(linebuf)-1, xlated_fmt, argp);
133                                                 /* -1 to allow for '\n' */
134     arglist_end(argp);
135
136     /* avoid recursive call from error() */
137
138     saved_errout = erroutput_type;
139     erroutput_type &= ~ERR_AMANDALOG;
140
141     /* append message to the log file */
142
143     if(multiline == -1) open_log();
144
145     if (fullwrite(logfd, leader, strlen(leader)) < 0) {
146         error(_("log file write error: %s"), strerror(errno));
147         /*NOTREACHED*/
148     }
149
150     amfree(leader);
151
152     n = strlen(linebuf);
153     if(n == 0 || linebuf[n-1] != '\n') linebuf[n++] = '\n';
154     linebuf[n] = '\0';
155
156     if (fullwrite(logfd, linebuf, n) < 0) {
157         error(_("log file write error: %s"), strerror(errno));
158         /*NOTREACHED*/
159     }
160
161     if(multiline != -1) multiline++;
162     else close_log();
163
164     erroutput_type = saved_errout;
165 }
166
167 void
168 log_start_multiline(void)
169 {
170     assert(multiline == -1);
171
172     multiline = 0;
173     open_log();
174 }
175
176
177 void
178 log_end_multiline(void)
179 {
180     assert(multiline != -1);
181     multiline = -1;
182     close_log();
183 }
184
185
186 void
187 log_rename(
188     char *      datestamp)
189 {
190     char *conf_logdir;
191     char *logfile;
192     char *fname = NULL;
193     char seq_str[NUM_STR_SIZE];
194     unsigned int seq;
195     struct stat statbuf;
196
197     if(datestamp == NULL) datestamp = "error";
198
199     conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
200     logfile = vstralloc(conf_logdir, "/log", NULL);
201
202     for(seq = 0; 1; seq++) {    /* if you've got MAXINT files in your dir... */
203         g_snprintf(seq_str, SIZEOF(seq_str), "%u", seq);
204         fname = newvstralloc(fname,
205                              logfile,
206                              ".", datestamp,
207                              ".", seq_str,
208                              NULL);
209         if(stat(fname, &statbuf) == -1 && errno == ENOENT) break;
210     }
211
212     if(rename(logfile, fname) == -1) {
213         error(_("could not rename \"%s\" to \"%s\": %s"),
214               logfile, fname, strerror(errno));
215         /*NOTREACHED*/
216     }
217
218     amfree(fname);
219     amfree(logfile);
220     amfree(conf_logdir);
221 }
222
223
224 static void
225 open_log(void)
226 {
227     char *conf_logdir;
228
229     /* now that we have a logfile, let the debug module know how to write
230      * error messages to it.  This is due to some rather obscure linking 
231      * problems. */
232     set_logerror(logerror);
233
234     conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
235     logfile = vstralloc(conf_logdir, "/log", NULL);
236     amfree(conf_logdir);
237
238     logfd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600);
239
240     if(logfd == -1) {
241         error(_("could not open log file %s: %s"), logfile, strerror(errno));
242         /*NOTREACHED*/
243     }
244
245     if(amflock(logfd, "log") == -1) {
246         error(_("could not lock log file %s: %s"), logfile, strerror(errno));
247         /*NOTREACHED*/
248     }
249 }
250
251
252 static void
253 close_log(void)
254 {
255     if(amfunlock(logfd, "log") == -1) {
256         error(_("could not unlock log file %s: %s"), logfile, strerror(errno));
257         /*NOTREACHED*/
258     }
259
260     if(close(logfd) == -1) {
261         error(_("close log file: %s"), strerror(errno));
262         /*NOTREACHED*/
263     }
264
265     logfd = -1;
266     amfree(logfile);
267 }
268
269 /* WARNING: Function accesses globals curstr, curlog, and curprog
270  * WARNING: Function has static member logline, returned via globals */
271 int
272 get_logline(
273     FILE *      logf)
274 {
275     static char *logline = NULL;
276     char *logstr, *progstr;
277     char *s;
278     int ch;
279
280     amfree(logline);
281     while ((logline = agets(logf)) != NULL) {
282         if (logline[0] != '\0')
283             break;
284         amfree(logline);
285     }
286     if (logline == NULL) return 0;
287     curlinenum++;
288     s = logline;
289     ch = *s++;
290
291     /* continuation lines are special */
292
293     if(logline[0] == ' ' && logline[1] == ' ') {
294         curlog = L_CONT;
295         /* curprog stays the same */
296         skip_whitespace(s, ch);
297         curstr = s-1;
298         return 1;
299     }
300
301     /* isolate logtype field */
302
303     skip_whitespace(s, ch);
304     logstr = s - 1;
305     skip_non_whitespace(s, ch);
306     s[-1] = '\0';
307
308     /* isolate program name field */
309
310     skip_whitespace(s, ch);
311     progstr = s - 1;
312     skip_non_whitespace(s, ch);
313     s[-1] = '\0';
314
315     /* rest of line is logtype dependent string */
316
317     skip_whitespace(s, ch);
318     curstr = s - 1;
319
320     /* lookup strings */
321
322     for(curlog = L_MARKER; curlog != L_BOGUS; curlog--)
323         if(strcmp(logtype_str[curlog], logstr) == 0) break;
324
325     for(curprog = P_LAST; curprog != P_UNKNOWN; curprog--)
326         if(strcmp(program_str[curprog], progstr) == 0) break;
327
328     return 1;
329 }