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