1 /*
2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
4 * Copyright (c) 2007-2013 Zmanda, Inc. All Rights Reserved.
5 * All Rights Reserved.
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of U.M. not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission. U.M. makes no representations about the
14 * suitability of this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
16 *
17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 * Author: James da Silva, Systems Design and Analysis Group
25 * Computer Science Department
26 * University of Maryland at College Park
27 */
28 /*
29 * $Id: logfile.c,v 1.31 2006/06/01 14:54:39 martinea Exp $
30 *
31 * common log file writing routine
32 */
33 #include "amanda.h"
34 #include "arglist.h"
35 #include "util.h"
36 #include "conffile.h"
37
38 #include "logfile.h"
39
40 char *logtype_str[] = {
41 "BOGUS",
42 "FATAL", /* program died for some reason, used by error() */
43 "ERROR", "WARNING", "INFO", "SUMMARY", /* information messages */
44 "START", "FINISH", /* start/end of a run */
45 "DISK", /* disk */
46 /* the end of a dump */
47 "DONE", "PART", "PARTPARTIAL", "SUCCESS", "PARTIAL", "FAIL", "STRANGE",
48 "CHUNK", "CHUNKSUCCESS", /* ... continued */
49 "STATS", /* statistics */
50 "MARKER", /* marker for reporter */
51 "CONT" /* continuation line; special */
52 };
53
54 char *program_str[] = {
55 "UNKNOWN", "planner", "driver", "amreport", "dumper", "chunker",
56 "taper", "amflush", "amdump", "amidxtaped", "amfetchdump", "amcheckdump",
57 "amvault",
58 };
59
60 int curlinenum;
61 logtype_t curlog;
62 program_t curprog;
63 char *curstr;
64
65 int multiline = -1;
66 static char *logfile;
67 static int logfd = -1;
68
69 /*
70 * Note that technically we could use two locks, a read lock
71 * from 0-EOF and a write-lock from EOF-EOF, thus leaving the
72 * beginning of the file open for read-only access. Doing so
73 * would open us up to some race conditions unless we're pretty
74 * careful, and on top of that the functions here are so far
75 * the only accesses to the logfile, so keep things simple.
76 */
77
78 /* local functions */
79 static void open_log(void);
80 static void close_log(void);
81
82 void
amanda_log_trace_log(GLogLevelFlags log_level,const gchar * message)83 amanda_log_trace_log(
84 GLogLevelFlags log_level,
85 const gchar *message)
86 {
87 logtype_t logtype = L_ERROR;
88
89 switch (log_level) {
90 case G_LOG_LEVEL_ERROR:
91 case G_LOG_LEVEL_CRITICAL:
92 logtype = L_FATAL;
93 break;
94
95 default:
96 return;
97 }
98 log_add(logtype, "%s", message);
99 }
100
log_add_full_v(logtype_t typ,char * pname,char * format,va_list argp)101 static void log_add_full_v(logtype_t typ, char *pname, char *format, va_list argp)
102 {
103 char *leader = NULL;
104 char *xlated_fmt = gettext(format);
105 char linebuf[STR_SIZE];
106 size_t n;
107 static gboolean in_log_add = 0;
108
109 /* avoid recursion */
110 if (in_log_add)
111 return;
112
113 /* format error message */
114
115 if((int)typ <= (int)L_BOGUS || (int)typ > (int)L_MARKER) typ = L_BOGUS;
116
117 if(multiline > 0) {
118 leader = stralloc(" "); /* continuation line */
119 } else {
120 leader = vstralloc(logtype_str[(int)typ], " ", pname, " ", NULL);
121 }
122
123 /* use sizeof(linebuf)-2 to save space for a trailing newline */
124 g_vsnprintf(linebuf, SIZEOF(linebuf)-2, xlated_fmt, argp);
125 /* -1 to allow for '\n' */
126
127 /* avoid recursive call from error() */
128
129 in_log_add = 1;
130
131 /* append message to the log file */
132
133 if(multiline == -1) open_log();
134
135 if (full_write(logfd, leader, strlen(leader)) < strlen(leader)) {
136 error(_("log file write error: %s"), strerror(errno));
137 /*NOTREACHED*/
138 }
139
140 amfree(leader);
141
142 /* add a newline if necessary */
143 n = strlen(linebuf);
144 if(n == 0 || linebuf[n-1] != '\n') linebuf[n++] = '\n';
145 linebuf[n] = '\0';
146
147 if (full_write(logfd, linebuf, n) < n) {
148 error(_("log file write error: %s"), strerror(errno));
149 /*NOTREACHED*/
150 }
151
152 if(multiline != -1) multiline++;
153 else close_log();
154
155 in_log_add = 0;
156 }
157
log_add(logtype_t typ,char * format,...)158 void log_add(logtype_t typ, char *format, ...)
159 {
160 va_list argp;
161
162 arglist_start(argp, format);
163 log_add_full_v(typ, get_pname(), format, argp);
164 arglist_end(argp);
165 }
166
log_add_full(logtype_t typ,char * pname,char * format,...)167 void log_add_full(logtype_t typ, char *pname, char *format, ...)
168 {
169 va_list argp;
170
171 arglist_start(argp, format);
172 log_add_full_v(typ, pname, format, argp);
173 arglist_end(argp);
174 }
175
176 void
log_start_multiline(void)177 log_start_multiline(void)
178 {
179 assert(multiline == -1);
180
181 multiline = 0;
182 open_log();
183 }
184
185
186 void
log_end_multiline(void)187 log_end_multiline(void)
188 {
189 assert(multiline != -1);
190 multiline = -1;
191 close_log();
192 }
193
194
195 void
log_rename(char * datestamp)196 log_rename(
197 char * datestamp)
198 {
199 char *conf_logdir;
200 char *logfile;
201 char *fname = NULL;
202 char seq_str[NUM_STR_SIZE];
203 unsigned int seq;
204 struct stat statbuf;
205
206 if(datestamp == NULL) datestamp = "error";
207
208 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
209 logfile = vstralloc(conf_logdir, "/log", NULL);
210
211 for(seq = 0; 1; seq++) { /* if you've got MAXINT files in your dir... */
212 g_snprintf(seq_str, SIZEOF(seq_str), "%u", seq);
213 fname = newvstralloc(fname,
214 logfile,
215 ".", datestamp,
216 ".", seq_str,
217 NULL);
218 if(stat(fname, &statbuf) == -1 && errno == ENOENT) break;
219 }
220
221 if(rename(logfile, fname) == -1) {
222 g_debug(_("could not rename \"%s\" to \"%s\": %s"),
223 logfile, fname, strerror(errno));
224 }
225
226 amfree(fname);
227 amfree(logfile);
228 amfree(conf_logdir);
229 }
230
231
232 static void
open_log(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
close_log(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
get_logline(FILE * logf)275 get_logline(
276 FILE * logf)
277 {
278 static char *logline = NULL;
279 static size_t line_size = 0;
280 char *lline;
281 size_t loffset = 0;
282 char *logstr, *progstr;
283 char *s;
284 int ch;
285 int n;
286
287 if (!logline) {
288 line_size = 256;
289 logline = g_malloc(line_size);
290 }
291
292 logline[0] = '\0';
293 while(1) {
294 lline = fgets(logline + loffset, line_size - loffset, logf);
295 if (lline == NULL) {
296 break; /* EOF */
297 }
298 if (strlen(logline) == line_size -1 &&
299 logline[strlen(logline)-1] != '\n') {
300 line_size *= 2;
301 logline = g_realloc(logline, line_size);
302 loffset = strlen(logline);
303 } else if (strlen(logline) == 0 ||
304 (strlen(logline) == 1 && logline[0] == '\n')) {
305 } else {
306 break; /* good line */
307 }
308 logline[loffset] = '\0';
309 }
310 if (logline[0] == '\0')
311 return 0;
312
313 /* remove \n */
314 n = strlen(logline);
315 if (logline[n-1] == '\n') logline[n-1] = '\0';
316
317 curlinenum++;
318 s = logline;
319 ch = *s++;
320
321 /* continuation lines are special */
322
323 if(logline[0] == ' ' && logline[1] == ' ') {
324 curlog = L_CONT;
325 /* curprog stays the same */
326 skip_whitespace(s, ch);
327 curstr = s-1;
328 return 1;
329 }
330
331 /* isolate logtype field */
332
333 skip_whitespace(s, ch);
334 logstr = s - 1;
335 skip_non_whitespace(s, ch);
336 s[-1] = '\0';
337
338 /* isolate program name field */
339
340 skip_whitespace(s, ch);
341 progstr = s - 1;
342 skip_non_whitespace(s, ch);
343 s[-1] = '\0';
344
345 /* rest of line is logtype dependent string */
346
347 skip_whitespace(s, ch);
348 curstr = s - 1;
349
350 /* lookup strings */
351
352 for(curlog = L_MARKER; curlog != L_BOGUS; curlog--)
353 if(strcmp(logtype_str[curlog], logstr) == 0) break;
354
355 for(curprog = P_LAST; curprog != P_UNKNOWN; curprog--)
356 if(strcmp(program_str[curprog], progstr) == 0) break;
357
358 return 1;
359 }
360