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