1/*****************************************************************
2**
3**	@(#) log.c -- The ZKT error logging module
4**
5**	Copyright (c) June 2008, Holger Zuleger HZnet. All rights reserved.
6**
7**	This software is open source.
8**
9**	Redistribution and use in source and binary forms, with or without
10**	modification, are permitted provided that the following conditions
11**	are met:
12**
13**	Redistributions of source code must retain the above copyright notice,
14**	this list of conditions and the following disclaimer.
15**
16**	Redistributions in binary form must reproduce the above copyright notice,
17**	this list of conditions and the following disclaimer in the documentation
18**	and/or other materials provided with the distribution.
19**
20**	Neither the name of Holger Zuleger HZnet nor the names of its contributors may
21**	be used to endorse or promote products derived from this software without
22**	specific prior written permission.
23**
24**	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25**	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26**	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27**	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
28**	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29**	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30**	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31**	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32**	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33**	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34**	POSSIBILITY OF SUCH DAMAGE.
35**
36**
37*****************************************************************/
38# include <stdio.h>
39# include <string.h>
40# include <stdlib.h>
41# include <ctype.h>
42# include <sys/types.h>
43# include <sys/stat.h>
44# include <sys/time.h>
45# include <time.h>
46# include <assert.h>
47# include <errno.h>
48# include <syslog.h>
49#ifdef HAVE_CONFIG_H
50# include <config.h>
51#endif
52# include "config_zkt.h"
53# include "misc.h"
54# include "debug.h"
55#define extern
56# include "log.h"
57#undef extern
58
59/*****************************************************************
60**	module internal vars & declarations
61*****************************************************************/
62static	FILE	*lg_fp;
63static	FILE	*lg_fpsave;
64static	int	lg_minfilelevel;
65static	int	lg_syslogging;
66static	int	lg_minsyslevel;
67static	long	lg_errcnt;
68static	const char	*lg_progname;
69
70typedef	struct {
71	lg_lvl_t	level;
72	const	char	*str;
73	int		syslog_level;
74} lg_symtbl_t;
75
76static	lg_symtbl_t	symtbl[] = {
77	{ LG_NONE,	"none",		-1 },
78	{ LG_DEBUG,	"debug",	LOG_DEBUG },
79	{ LG_INFO,	"info",		LOG_INFO },
80	{ LG_NOTICE,	"notice",	LOG_NOTICE },
81	{ LG_WARNING,	"warning",	LOG_WARNING },
82	{ LG_ERROR,	"error",	LOG_ERR },
83	{ LG_FATAL,	"fatal",	LOG_CRIT },
84
85	{ LG_NONE,	"user",		LOG_USER },
86	{ LG_NONE,	"daemon",	LOG_DAEMON },
87	{ LG_NONE,	"local0",	LOG_LOCAL0 },
88	{ LG_NONE,	"local1",	LOG_LOCAL1 },
89	{ LG_NONE,	"local2",	LOG_LOCAL2 },
90	{ LG_NONE,	"local3",	LOG_LOCAL3 },
91	{ LG_NONE,	"local4",	LOG_LOCAL4 },
92	{ LG_NONE,	"local5",	LOG_LOCAL5 },
93	{ LG_NONE,	"local6",	LOG_LOCAL6 },
94	{ LG_NONE,	"local7",	LOG_LOCAL7 },
95	{ LG_NONE,	NULL,		-1 }
96};
97
98# define	MAXFNAME	(1023)
99/*****************************************************************
100**	function definitions (for function declarations see log.h)
101*****************************************************************/
102
103/*****************************************************************
104**	lg_fileopen (path, name) -- open the log file
105**	Name is a (absolute or relative) file or directory name.
106**	If path is given and name is a relative path name then path
107**	is prepended to name.
108**	returns the open file pointer or NULL on error
109*****************************************************************/
110static	FILE	*lg_fileopen (const char *path, const char *name)
111{
112	int	len;
113	FILE	*fp;
114	struct	tm	*t;
115	time_t	sec;
116	char	fname[MAXFNAME+1];
117
118	if ( name == NULL || *name == '\0' )
119		return NULL;
120	else if ( *name == '/' || path == NULL )
121		snprintf (fname, MAXFNAME, "%s", name);
122	else
123		snprintf (fname, MAXFNAME, "%s/%s", path, name);
124
125# ifdef LOG_TEST
126	fprintf (stderr, "\t ==> \"%s\"", fname);
127# endif
128	if ( is_directory (fname) )
129	{
130		len = strlen (fname);
131
132		time (&sec);
133		t = gmtime (&sec);
134		snprintf (fname+len, MAXFNAME-len, LOG_FNAMETMPL,
135			t->tm_year + 1900, t->tm_mon+1, t->tm_mday,
136			t->tm_hour, t->tm_min, t->tm_sec);
137# ifdef LOG_TEST
138	fprintf (stderr, " isdir \"%s\"", fname);
139# endif
140	}
141
142# ifdef LOG_TEST
143	fprintf (stderr, "\n");
144# endif
145
146	if ( (fp = fopen (fname, "a")) == NULL )
147		return NULL;
148
149	return fp;
150}
151
152/*****************************************************************
153**	lg_str2lvl (level_name)
154*****************************************************************/
155lg_lvl_t	lg_str2lvl (const char *name)
156{
157	lg_symtbl_t	*p;
158
159	if ( !name )
160		return LG_NONE;
161
162	for ( p = symtbl; p->str; p++ )
163		if ( strcasecmp (name, p->str) == 0 )
164			return p->level;
165
166	return LG_NONE;
167}
168
169/*****************************************************************
170**	lg_lvl2syslog (level)
171*****************************************************************/
172lg_lvl_t	lg_lvl2syslog (lg_lvl_t level)
173{
174	lg_symtbl_t	*p;
175
176	for ( p = symtbl; p->str; p++ )
177		if ( level == p->level )
178			return p->syslog_level;
179
180	assert ( p->str != NULL );	/* we assume not to reach this! */
181
182	return LOG_DEBUG;	/* if not found, return DEBUG as default */
183}
184
185/*****************************************************************
186**	lg_str2syslog (facility_name)
187*****************************************************************/
188int	lg_str2syslog (const char *facility)
189{
190	lg_symtbl_t	*p;
191
192	dbg_val1 ("lg_str2syslog (%s)\n", facility);
193	if ( !facility )
194		return LG_NONE;
195
196	for ( p = symtbl; p->str; p++ )
197		if ( strcasecmp (facility, p->str) == 0 )
198			return p->syslog_level;
199
200	return LG_NONE;
201}
202
203/*****************************************************************
204**	lg_lvl2str (level)
205*****************************************************************/
206const	char	*lg_lvl2str (lg_lvl_t level)
207{
208	lg_symtbl_t	*p;
209
210	if ( level < LG_DEBUG )
211		return "none";
212
213	for ( p = symtbl; p->str; p++ )
214		if ( level == p->level )
215			return p->str;
216	return "fatal";
217}
218
219/*****************************************************************
220**	lg_geterrcnt () -- returns the current value of the internal
221**	error counter
222*****************************************************************/
223long	lg_geterrcnt ()
224{
225	return lg_errcnt;
226}
227
228/*****************************************************************
229**	lg_seterrcnt () -- sets the internal error counter
230**	returns the current value
231*****************************************************************/
232long	lg_seterrcnt (long value)
233{
234	return lg_errcnt = value;
235}
236
237/*****************************************************************
238**	lg_reseterrcnt () -- resets the internal error counter to 0
239**	returns the current value
240*****************************************************************/
241long	lg_reseterrcnt ()
242{
243	return lg_seterrcnt (0L);
244}
245
246
247/*****************************************************************
248**	lg_open (prog, facility, syslevel, path, file, filelevel)
249**		-- open the log channel
250**	return values:
251**		 0 on success
252**		 -1 on file open error
253*****************************************************************/
254int	lg_open (const char *progname, const char *facility, const char *syslevel, const char *path, const char *file, const char *filelevel)
255{
256	int	sysfacility;
257
258	dbg_val6 ("lg_open (%s, %s, %s, %s, %s, %s)\n", progname, facility, syslevel, path, file, filelevel);
259
260	lg_minsyslevel = lg_str2lvl (syslevel);
261	lg_minfilelevel = lg_str2lvl (filelevel);
262
263	sysfacility = lg_str2syslog (facility);
264	if ( sysfacility >= 0 )
265	{
266		lg_syslogging = 1;
267		dbg_val2 ("lg_open: openlog (%s, LOG_NDELAY, %d)\n", progname, lg_str2syslog (facility));
268		openlog (progname, LOG_NDELAY, lg_str2syslog (facility));
269	}
270	if ( file && * file )
271	{
272		if ( (lg_fp = lg_fileopen (path, file)) == NULL )
273			return -1;
274		lg_progname = progname;
275	}
276
277	return 0;
278}
279
280/*****************************************************************
281**	lg_close () -- close the open filepointer for error logging
282**	return 0 if no error log file is currently open,
283**	otherwise the return code of fclose is returned.
284*****************************************************************/
285int	lg_close ()
286{
287	int	ret = 0;
288
289	if ( lg_syslogging )
290	{
291		closelog ();
292		lg_syslogging = 0;
293	}
294	if ( lg_fp )
295	{
296		ret = fclose (lg_fp);
297		lg_fp = NULL;
298	}
299
300	return ret;
301}
302
303/*****************************************************************
304**	lg_zone_start (domain)
305**		-- reopen the log channel
306**	return values:
307**		 0 on success
308**		 -1 on file open error
309*****************************************************************/
310int	lg_zone_start (const char *dir, const char *domain)
311{
312	char	fname[255+1];
313
314	dbg_val2 ("lg_zone_start (%s, %s)\n", dir, domain);
315
316	snprintf (fname, sizeof (fname), LOG_DOMAINTMPL, domain);
317	if ( lg_fp )
318		lg_fpsave = lg_fp;
319	lg_fp = lg_fileopen (dir, fname);
320
321	return lg_fp != NULL;
322}
323
324/*****************************************************************
325**	lg_zone_end (domain)
326**		-- close the (reopened) log channel
327**	return values:
328**		 0 on success
329**		 -1 on file open error
330*****************************************************************/
331int	lg_zone_end ()
332{
333	if ( lg_fp && lg_fpsave )
334	{
335		lg_close ();
336		lg_fp = lg_fpsave;
337		lg_fpsave = NULL;
338		return 1;
339	}
340
341	return 0;
342}
343
344/*****************************************************************
345**
346**	lg_args (level, argc, argv[])
347**	log all command line arguments (up to a length of 511 chars)
348**	with priority level
349**
350*****************************************************************/
351void	lg_args (lg_lvl_t level, int argc, char * const argv[])
352{
353	char	cmdline[511+1];
354	int	len;
355	int	i;
356
357	len = 0;
358	for ( i = 0; i < argc && len < sizeof (cmdline); i++ )
359		len += snprintf (cmdline+len, sizeof (cmdline) - len, " %s", argv[i]);
360
361#if 1
362	lg_mesg (level, "------------------------------------------------------------");
363#else
364	lg_mesg (level, "");
365#endif
366	lg_mesg (level, "running%s ", cmdline);
367}
368
369/*****************************************************************
370**
371**	lg_mesg (level, fmt, ...)
372**
373**	Write a given message to the error log file and counts
374**	all messages written with an level greater than LOG_ERR.
375**
376**	All messages will be on one line in the logfile, so it's
377**	not necessary to add an '\n' to the message.
378**
379**	To call this function before an elog_open() is called is
380**	useless!
381**
382*****************************************************************/
383void	lg_mesg (int priority, char *fmt, ...)
384{
385	va_list ap;
386	struct	timeval	tv;
387	struct	tm	*t;
388	char	format[256];
389
390	assert (fmt != NULL);
391	assert (priority >= LG_DEBUG && priority <= LG_FATAL);
392
393	format[0] ='\0';
394
395	dbg_val3 ("syslog = %d prio = %d >= sysmin = %d\n", lg_syslogging, priority, lg_minsyslevel);
396	if ( lg_syslogging && priority >= lg_minsyslevel )
397	{
398#if defined (LOG_WITH_LEVEL) && LOG_WITH_LEVEL
399		snprintf (format, sizeof (format), "%s: %s", lg_lvl2str(priority), fmt);
400		fmt = format;
401#endif
402		va_start(ap, fmt);
403		vsyslog (lg_lvl2syslog (priority), fmt, ap);
404		va_end(ap);
405	}
406
407	dbg_val3 ("filelg = %d prio = %d >= filmin = %d\n", lg_fp!=NULL, priority, lg_minfilelevel);
408	if ( lg_fp && priority >= lg_minfilelevel )
409	{
410#if defined (LOG_WITH_TIMESTAMP) && LOG_WITH_TIMESTAMP
411		gettimeofday (&tv, NULL);
412		t = localtime ((time_t *) &tv.tv_sec);
413		fprintf (lg_fp, "%04d-%02d-%02d ",
414			t->tm_year+1900, t->tm_mon+1, t->tm_mday);
415		fprintf (lg_fp, "%02d:%02d:%02d.%03ld: ",
416			t->tm_hour, t->tm_min, t->tm_sec, tv.tv_usec / 1000);
417#endif
418#if defined (LOG_WITH_PROGNAME) && LOG_WITH_PROGNAME
419		if ( lg_progname )
420			fprintf (lg_fp, "%s: ", lg_progname);
421#endif
422#if defined (LOG_WITH_LEVEL) && LOG_WITH_LEVEL
423		if ( fmt != format )	/* level is not in fmt string */
424			fprintf (lg_fp, "%s: ", lg_lvl2str(priority));
425#endif
426		va_start(ap, fmt);
427		vfprintf (lg_fp, fmt, ap);
428		va_end(ap);
429		fprintf (lg_fp, "\n");
430	}
431
432	if ( priority >= LG_ERROR )
433		lg_errcnt++;
434}
435
436
437#ifdef LOG_TEST
438const char *progname;
439int	main (int argc, char *argv[])
440{
441	const	char	*levelstr;
442	const	char	*newlevelstr;
443	int	level;
444	int	err;
445
446	progname = *argv;
447
448	if ( --argc )
449		levelstr = *++argv;
450	else
451		levelstr = "fatal";
452
453	level = lg_str2lvl (levelstr);
454	newlevelstr = lg_lvl2str (level+1);
455	dbg_val4 ("base level = %s(%d) newlevel = %s(%d)\n", levelstr, level, newlevelstr, level+1);
456	if ( (err = lg_open (progname,
457#if 1
458				"user",
459#else
460				"none",
461#endif
462				levelstr, ".",
463#if 1
464				"test.log",
465#else
466				NULL,
467#endif
468				newlevelstr)) )
469		fprintf (stderr, "\topen error %d\n", err);
470	else
471	{
472		lg_mesg (LG_DEBUG, "debug message");
473		lg_mesg (LG_INFO, "INFO message");
474		lg_mesg (LG_NOTICE, "Notice message");
475		lg_mesg (LG_WARNING, "Warning message");
476		lg_mesg (LG_ERROR, "Error message");
477		lg_mesg (LG_FATAL, "Fatal message ");
478	}
479
480	if ( (err = lg_close ()) < 0 )
481		fprintf (stderr, "\tclose error %d\n", err);
482
483	return 0;
484}
485#endif
486