Imported Upstream version 3.2.0
[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", "amdump", "amidxtaped", "amfetchdump", "amcheckdump",
56     "amvault",
57 };
58
59 int curlinenum;
60 logtype_t curlog;
61 program_t curprog;
62 char *curstr;
63
64 int multiline = -1;
65 static char *logfile;
66 static int logfd = -1;
67
68  /*
69   * Note that technically we could use two locks, a read lock
70   * from 0-EOF and a write-lock from EOF-EOF, thus leaving the
71   * beginning of the file open for read-only access.  Doing so
72   * would open us up to some race conditions unless we're pretty
73   * careful, and on top of that the functions here are so far
74   * the only accesses to the logfile, so keep things simple.
75   */
76
77 /* local functions */
78 static void open_log(void);
79 static void close_log(void);
80
81 void
82 amanda_log_trace_log(
83     GLogLevelFlags log_level,
84     const gchar *message)
85 {
86     logtype_t logtype = L_ERROR;
87
88     switch (log_level) {
89         case G_LOG_LEVEL_ERROR:
90         case G_LOG_LEVEL_CRITICAL:
91             logtype = L_FATAL;
92             break;
93
94         default:
95             return;
96     }
97     log_add(logtype, "%s", message);
98 }
99
100 static void log_add_full_v(logtype_t typ, char *pname, char *format, va_list argp)
101 {
102     char *leader = NULL;
103     char *xlated_fmt = gettext(format);
104     char linebuf[STR_SIZE];
105     size_t n;
106     static gboolean in_log_add = 0;
107
108     /* avoid recursion */
109     if (in_log_add)
110         return;
111
112     /* format error message */
113
114     if((int)typ <= (int)L_BOGUS || (int)typ > (int)L_MARKER) typ = L_BOGUS;
115
116     if(multiline > 0) {
117         leader = stralloc("  ");                /* continuation line */
118     } else {
119         leader = vstralloc(logtype_str[(int)typ], " ", pname, " ", NULL);
120     }
121
122     /* use sizeof(linebuf)-2 to save space for a trailing newline */
123     g_vsnprintf(linebuf, SIZEOF(linebuf)-2, xlated_fmt, argp);
124                                                 /* -1 to allow for '\n' */
125
126     /* avoid recursive call from error() */
127
128     in_log_add = 1;
129
130     /* append message to the log file */
131
132     if(multiline == -1) open_log();
133
134     if (full_write(logfd, leader, strlen(leader)) < strlen(leader)) {
135         error(_("log file write error: %s"), strerror(errno));
136         /*NOTREACHED*/
137     }
138
139     amfree(leader);
140
141     /* add a newline if necessary */
142     n = strlen(linebuf);
143     if(n == 0 || linebuf[n-1] != '\n') linebuf[n++] = '\n';
144     linebuf[n] = '\0';
145
146     if (full_write(logfd, linebuf, n) < n) {
147         error(_("log file write error: %s"), strerror(errno));
148         /*NOTREACHED*/
149     }
150
151     if(multiline != -1) multiline++;
152     else close_log();
153
154     in_log_add = 0;
155 }
156
157 void log_add(logtype_t typ, char *format, ...)
158 {
159     va_list argp;
160
161     arglist_start(argp, format);
162     log_add_full_v(typ, get_pname(), format, argp);
163     arglist_end(argp);
164 }
165
166 void log_add_full(logtype_t typ, char *pname, char *format, ...)
167 {
168     va_list argp;
169
170     arglist_start(argp, format);
171     log_add_full_v(typ, pname, format, argp);
172     arglist_end(argp);
173 }
174
175 void
176 log_start_multiline(void)
177 {
178     assert(multiline == -1);
179
180     multiline = 0;
181     open_log();
182 }
183
184
185 void
186 log_end_multiline(void)
187 {
188     assert(multiline != -1);
189     multiline = -1;
190     close_log();
191 }
192
193
194 void
195 log_rename(
196     char *      datestamp)
197 {
198     char *conf_logdir;
199     char *logfile;
200     char *fname = NULL;
201     char seq_str[NUM_STR_SIZE];
202     unsigned int seq;
203     struct stat statbuf;
204
205     if(datestamp == NULL) datestamp = "error";
206
207     conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
208     logfile = vstralloc(conf_logdir, "/log", NULL);
209
210     for(seq = 0; 1; seq++) {    /* if you've got MAXINT files in your dir... */
211         g_snprintf(seq_str, SIZEOF(seq_str), "%u", seq);
212         fname = newvstralloc(fname,
213                              logfile,
214                              ".", datestamp,
215                              ".", seq_str,
216                              NULL);
217         if(stat(fname, &statbuf) == -1 && errno == ENOENT) break;
218     }
219
220     if(rename(logfile, fname) == -1) {
221         error(_("could not rename \"%s\" to \"%s\": %s"),
222               logfile, fname, strerror(errno));
223         /*NOTREACHED*/
224     }
225
226     amfree(fname);
227     amfree(logfile);
228     amfree(conf_logdir);
229 }
230
231
232 static void
233 open_log(void)
234 {
235     char *conf_logdir;
236
237     conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
238     logfile = vstralloc(conf_logdir, "/log", NULL);
239     amfree(conf_logdir);
240
241     logfd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600);
242
243     if(logfd == -1) {
244         error(_("could not open log file %s: %s"), logfile, strerror(errno));
245         /*NOTREACHED*/
246     }
247
248     if(amflock(logfd, "log") == -1) {
249         error(_("could not lock log file %s: %s"), logfile, strerror(errno));
250         /*NOTREACHED*/
251     }
252 }
253
254
255 static void
256 close_log(void)
257 {
258     if(amfunlock(logfd, "log") == -1) {
259         error(_("could not unlock log file %s: %s"), logfile, strerror(errno));
260         /*NOTREACHED*/
261     }
262
263     if(close(logfd) == -1) {
264         error(_("close log file: %s"), strerror(errno));
265         /*NOTREACHED*/
266     }
267
268     logfd = -1;
269     amfree(logfile);
270 }
271
272 /* WARNING: Function accesses globals curstr, curlog, and curprog
273  * WARNING: Function has static member logline, returned via globals */
274 int
275 get_logline(
276     FILE *      logf)
277 {
278     static char *logline = NULL;
279     char *logstr, *progstr;
280     char *s;
281     int ch;
282
283     amfree(logline);
284     while ((logline = agets(logf)) != NULL) {
285         if (logline[0] != '\0')
286             break;
287         amfree(logline);
288     }
289     if (logline == NULL) return 0;
290     curlinenum++;
291     s = logline;
292     ch = *s++;
293
294     /* continuation lines are special */
295
296     if(logline[0] == ' ' && logline[1] == ' ') {
297         curlog = L_CONT;
298         /* curprog stays the same */
299         skip_whitespace(s, ch);
300         curstr = s-1;
301         return 1;
302     }
303
304     /* isolate logtype field */
305
306     skip_whitespace(s, ch);
307     logstr = s - 1;
308     skip_non_whitespace(s, ch);
309     s[-1] = '\0';
310
311     /* isolate program name field */
312
313     skip_whitespace(s, ch);
314     progstr = s - 1;
315     skip_non_whitespace(s, ch);
316     s[-1] = '\0';
317
318     /* rest of line is logtype dependent string */
319
320     skip_whitespace(s, ch);
321     curstr = s - 1;
322
323     /* lookup strings */
324
325     for(curlog = L_MARKER; curlog != L_BOGUS; curlog--)
326         if(strcmp(logtype_str[curlog], logstr) == 0) break;
327
328     for(curprog = P_LAST; curprog != P_UNKNOWN; curprog--)
329         if(strcmp(program_str[curprog], progstr) == 0) break;
330
331     return 1;
332 }