1 /*
2  * log.c - logging  module of websh 3
3  * nca-073-9
4  *
5  * Copyright (c) 1996-2000 by Netcetera AG.
6  * Copyright (c) 2001 by Apache Software Foundation.
7  * All rights reserved.
8  *
9  * See the file "license.terms" for information on usage and
10  * redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  *
12  * @(#) $Id: log.c 814683 2009-09-14 15:11:40Z ronnie $
13  *
14  */
15 
16 #include "log.h"
17 #include "logtochannel.h"
18 #include "logtocmd.h"
19 #include "logtofile.h"
20 #include "logtosyslog.h"
21 #include "tcl.h"
22 #include <stdio.h>
23 
24 
25 /* ----------------------------------------------------------------------------
26  * Init --
27  *   entry point for module weblog of websh3
28  * ------------------------------------------------------------------------- */
log_Init(Tcl_Interp * interp)29 int DLLEXPORT log_Init(Tcl_Interp * interp)
30 {
31 
32     LogData *logData = NULL;
33     LogPlugIn *logtofile = NULL;
34     LogPlugIn *logtochannel = NULL;
35     LogPlugIn *logtocmd = NULL;
36     LogPlugIn *logtosyslog = NULL;
37 
38     /* --------------------------------------------------------------------------
39      * interpreter running ?
40      * ----------------------------------------------------------------------- */
41     if (interp == NULL)
42 	return TCL_ERROR;
43 
44     /* --------------------------------------------------------------------------
45      * init internal data, and register with interp
46      * ----------------------------------------------------------------------- */
47     logData = createLogData();
48     WebAssertData(interp, logData, "log", TCL_ERROR);
49 
50     /* --------------------------------------------------------------------------
51      * register data with interp
52      * ----------------------------------------------------------------------- */
53     Tcl_SetAssocData(interp, WEB_LOG_ASSOC_DATA,
54 		     destroyLogData, (ClientData) logData);
55 
56     /* --------------------------------------------------------------------------
57      * register commands
58      * ----------------------------------------------------------------------- */
59     Tcl_CreateObjCommand(interp, "web::log",
60 			 Web_Log,
61 			 (ClientData) logData, (Tcl_CmdDeleteProc *) NULL);
62 
63     Tcl_CreateObjCommand(interp, "web::loglevel",
64 			 Web_LogFilter,
65 			 (ClientData) logData, (Tcl_CmdDeleteProc *) NULL);
66 
67     /* for compatibility with Websh 3.5 and earlier*/
68     Tcl_CreateObjCommand(interp, "web::logfilter",
69 			 Web_LogFilter,
70 			 (ClientData) logData, (Tcl_CmdDeleteProc *) NULL);
71 
72     Tcl_CreateObjCommand(interp, "web::logdest",
73 			 Web_LogDest,
74 			 (ClientData) logData, (Tcl_CmdDeleteProc *) NULL);
75 
76     /* --------------------------------------------------------------------------
77      * register log handler "channel"
78      * ----------------------------------------------------------------------- */
79     logtochannel = createLogPlugIn();
80     WebAssertData(interp, logtochannel, "log_Init/logtochannel plugin",
81 		  TCL_ERROR)
82 
83     logtochannel->constructor = createLogToChannel;
84     logtochannel->destructor = destroyLogToChannel;
85     logtochannel->handler = logToChannel;
86 
87     registerLogPlugIn(interp, "channel", logtochannel);
88 
89     /* --------------------------------------------------------------------------
90      * register log handler "file"
91      * ----------------------------------------------------------------------- */
92     logtofile = createLogPlugIn();
93     WebAssertData(interp, logtofile, "log_Init/logtofile plugin", TCL_ERROR)
94 
95     logtofile->constructor = createLogToFile;
96     logtofile->destructor = destroyLogToFile;
97     logtofile->handler = logToFile;
98 
99     registerLogPlugIn(interp, "file", logtofile);
100 
101     /* --------------------------------------------------------------------------
102      * register log handler "command"
103      * ----------------------------------------------------------------------- */
104     logtocmd = createLogPlugIn();
105     WebAssertData(interp, logtocmd, "log_Init/logtocmd plugin", TCL_ERROR)
106 
107 	logtocmd->constructor = createLogToCmd;
108     logtocmd->destructor = destroyLogToCmd;
109     logtocmd->handler = logToCmd;
110 
111     registerLogPlugIn(interp, "command", logtocmd);
112 
113     /* --------------------------------------------------------------------------
114      * register log handler "syslog"
115      * ----------------------------------------------------------------------- */
116 #ifndef WIN32
117     logtosyslog = createLogPlugIn();
118     WebAssertData(interp, logtocmd, "log_Init/logtosyslog plugin", TCL_ERROR)
119 
120     logtosyslog->constructor = createLogToSyslog;
121     logtosyslog->destructor = destroyLogToSyslog;
122     logtosyslog->handler = logToSyslog;
123 
124     registerLogPlugIn(interp, "syslog", logtosyslog);
125 #endif
126     /* --------------------------------------------------------------------------
127      * done
128      * ----------------------------------------------------------------------- */
129     return TCL_OK;
130 }
131 
132 /* ----------------------------------------------------------------------------
133  * createLogData --
134  * ------------------------------------------------------------------------- */
createLogData()135 LogData *createLogData()
136 {
137 
138     LogData *logData = NULL;
139 
140     logData = WebAllocInternalData(LogData);
141 
142     if (logData != NULL) {
143 
144 	/* initialize list of destination and levels */
145 	logData->listOfFilters = NULL;
146 	logData->filterSize = 0;
147 	logData->listOfDests = NULL;
148 	logData->destSize = 0;
149 	/* fake calls to initialize filters and destinations */
150 	insertIntoFilterList(logData, (LogLevel *) NULL);
151 	insertIntoDestList(logData, (LogDest *) NULL);
152 
153 	HashUtlAllocInit(logData->listOfPlugIns, TCL_STRING_KEYS);
154 	logData->logSubst = LOG_SUBSTDEFAULT;
155 	logData->safeLog = LOG_SAFEDEFAULT;
156 	logData->keep = 0;
157     }
158 
159     return logData;
160 }
161 
162 /* ----------------------------------------------------------------------------
163  * destroyLogData --
164  * ------------------------------------------------------------------------- */
destroyLogData(ClientData clientData,Tcl_Interp * interp)165 void destroyLogData(ClientData clientData, Tcl_Interp * interp)
166 {
167 
168     LogData *logData = NULL;
169 
170     if (clientData != NULL) {
171 
172 	logData = (LogData *) clientData;
173 
174 	/* ..................................................................... */
175 	if (logData->listOfFilters != NULL) {
176 
177 	  int i;
178 	  LogLevel ** logLevels = logData->listOfFilters;
179 	  for (i = 0; i < logData->filterSize; i++) {
180 	    if (logLevels[i] != NULL) {
181 	      destroyLogLevel(logLevels[i], interp);
182 	    }
183 	  }
184 	  WebFreeIfNotNull(logData->listOfFilters);
185 	  logData->filterSize = 0;
186 	}
187 
188 	/* ................................................................ */
189 	if (logData->listOfDests != NULL) {
190 
191 	  int i;
192 	  LogDest ** logDests = logData->listOfDests;
193 	  for (i = 0; i < logData->destSize; i++) {
194 	    if (logDests[i] != NULL) {
195 	      destroyLogDest(logDests[i], interp);
196 	    }
197 	  }
198 	  WebFreeIfNotNull(logData->listOfDests);
199 	  logData->destSize = 0;
200 	}
201 
202 
203 	/* ................................................................ */
204 	if (logData->listOfPlugIns != NULL) {
205 
206 	    resetHashTableWithContent(logData->listOfPlugIns, TCL_STRING_KEYS,
207 				      destroyLogPlugIn, interp);
208 
209 	    HashUtlDelFree(logData->listOfPlugIns);
210 	    logData->listOfPlugIns = NULL;
211 	}
212 
213 	WebFreeIfNotNull(logData);
214     }
215 }
216 
217 
218 /* ----------------------------------------------------------------------------
219  * createLogLevel -- allocate memory for a new LogLevel, and set content
220  *   facility -> like "websh3"
221  *   min      -> alert | error | warning | info | debug
222  *   max      -> as min
223  *   cnt      -> if 0 <= cnt <= 2^15, use cnt for name of filter
224  *               otherwise: don't set name (as for web::log)
225  * ------------------------------------------------------------------------- */
createLogLevel()226 LogLevel *createLogLevel()
227 {
228 
229     LogLevel *logLevel = NULL;
230 
231     logLevel = WebAllocInternalData(LogLevel);
232     if (logLevel != NULL) {
233 
234 	logLevel->keep = 0;
235 	logLevel->facility = NULL;
236 	logLevel->minSeverity = -1;
237 	logLevel->maxSeverity = -1;
238     }
239 
240     return logLevel;
241 }
242 
243 /* ----------------------------------------------------------------------------
244  * destroyLogLevel -- free memory for LogLevel
245  * ------------------------------------------------------------------------- */
destroyLogLevel(void * level,void * dum)246 int destroyLogLevel(void *level, void *dum)
247 {
248 
249     LogLevel *logLevel = NULL;
250 
251     if (level != NULL) {
252 
253 	logLevel = (LogLevel *) level;
254 
255 	WebFreeIfNotNull(logLevel->facility);
256 	WebFreeIfNotNull(logLevel);
257     }
258     return TCL_OK;
259 }
260 
261 /* ----------------------------------------------------------------------------
262  * createLogName --
263  * ------------------------------------------------------------------------- */
createLogName(char * prefix,int cnt)264 char *createLogName(char *prefix, int cnt)
265 {
266 
267     char str[64];		/* more than enough space for logDest%d */
268     char *name;
269 
270     if (prefix == NULL)
271 	return NULL;
272     if (strlen(prefix) > 10)
273 	return NULL;
274 
275     if ((cnt >= 0) && (cnt < (1 << 15))) {
276 	sprintf(str, "%s%d", prefix, cnt);
277 	name = allocAndSet(str);
278     } else {
279 	name = NULL;
280     }
281 
282     return name;
283 }
284 
285 /* ----------------------------------------------------------------------------
286  * getIndextFromLogName --
287  * ------------------------------------------------------------------------- */
getIndexFromLogName(char * format,char * string)288 int getIndexFromLogName(char *format, char *string) {
289   int i = -1;
290   int index = -1;
291   if (sscanf(string, format, &index) == 1) {
292     i = index;
293   }
294   return i;
295 }
296 
297 /* ----------------------------------------------------------------------------
298  * insertIntoDestList --
299  * ------------------------------------------------------------------------- */
insertIntoDestList(LogData * logData,LogDest * logDest)300 char * insertIntoDestList(LogData *logData, LogDest *logDest) {
301   int i;
302   LogDest ** logDests = logData->listOfDests;
303   for (i = 0; i < logData->destSize; i++) {
304     if (logDests[i] == NULL) {
305       if (logDest == NULL)
306 	return NULL;
307       logDests[i] = logDest;
308       return createLogName(LOG_DEST_PREFIX, i);
309     }
310   }
311   {
312     /* list too small: increase size */
313     LogDest ** newLogDests = (LogDest **) Tcl_Alloc((LOG_LIST_INITIAL_SIZE + logData->destSize) * sizeof(LogDest *));
314     if (newLogDests == NULL) {return NULL;}
315     /* copy old list to new list */
316     memcpy(newLogDests, logDests, logData->destSize * sizeof(LogDest *));
317     /* init new entries to NULL */
318     for (i = 0; i < LOG_LIST_INITIAL_SIZE; i++) {
319       newLogDests[i + logData->destSize] = NULL;
320     }
321     logData->listOfDests = newLogDests;
322     logData->destSize += LOG_LIST_INITIAL_SIZE;
323     WebFreeIfNotNull(logDests);
324     if (logDest != NULL) {
325       /* only when not fake call to inizialize listOfDests*/
326       return insertIntoDestList(logData, logDest);
327     } else {
328       return NULL;
329     }
330   }
331 }
332 
333 /* ----------------------------------------------------------------------------
334  * insertIntoFilterList --
335  * ------------------------------------------------------------------------- */
insertIntoFilterList(LogData * logData,LogLevel * logLevel)336 char * insertIntoFilterList(LogData *logData, LogLevel *logLevel) {
337   int i;
338   LogLevel ** logLevels = logData->listOfFilters;
339   for (i = 0; i < logData->filterSize; i++) {
340     if (logLevels[i] == NULL) {
341       if (logLevel == NULL)
342 	return NULL;
343       logLevels[i] = logLevel;
344       return createLogName(LOG_FILTER_PREFIX, i);
345     }
346   }
347   {
348     /* list too small: increase size */
349     LogLevel ** newLogLevels = (LogLevel **) Tcl_Alloc((LOG_LIST_INITIAL_SIZE + logData->filterSize) * sizeof(LogLevel *));
350     if (newLogLevels == NULL) {return NULL;}
351     /* copy old list to new list */
352     memcpy(newLogLevels, logLevels, logData->filterSize * sizeof(LogLevel *));
353     /* init new entries to NULL */
354     for (i = 0; i < LOG_LIST_INITIAL_SIZE; i++) {
355       newLogLevels[i + logData->filterSize] = NULL;
356     }
357     logData->listOfFilters = newLogLevels;
358     logData->filterSize += LOG_LIST_INITIAL_SIZE;
359     WebFreeIfNotNull(logLevels);
360     if (logLevel != NULL) {
361       /* only when not fake call to inizialize listOfDests*/
362       return insertIntoFilterList(logData, logLevel);
363     } else {
364       return NULL;
365     }
366   }
367 }
368 
369 /* ----------------------------------------------------------------------------
370  * createLogDest --
371  * ------------------------------------------------------------------------- */
createLogDest()372 LogDest *createLogDest()
373 {
374 
375     LogDest *logDest = NULL;
376 
377     logDest = WebAllocInternalData(LogDest);
378 
379     logDest->keep = 0;
380     logDest->format = NULL;
381     logDest->maxCharInMsg = -1;	/* Tcl_Append... --> -1 is all chars */
382     logDest->plugIn = NULL;
383     logDest->plugInData = NULL;
384 
385     return logDest;
386 }
387 
388 /* ----------------------------------------------------------------------------
389  * destroyLogDest --
390  * ------------------------------------------------------------------------- */
destroyLogDest(void * dest,void * env)391 int destroyLogDest(void *dest, void *env)
392 {
393 
394     Tcl_Interp *interp = NULL;
395     LogDest *logDest = NULL;
396 
397     if ((dest == NULL) || (env == NULL))
398 	return TCL_ERROR;
399 
400     logDest = (LogDest *) dest;
401     interp = (Tcl_Interp *) env;
402 
403     /* ------------------------------------------------------------------------
404      * get rid of any data
405      * --------------------------------------------------------------------- */
406     if ((logDest->plugIn != NULL) && (logDest->plugInData != NULL)) {
407 
408 	/* --------------------------------------------------------------------
409 	 * call destructor
410 	 * ----------------------------------------------------------------- */
411 	logDest->plugIn->destructor(interp, logDest->plugInData);
412     }
413 
414     /* ------------------------------------------------------------------------
415      * unlink plugIn from logDest. Keep plugIn alive, though.
416      * --------------------------------------------------------------------- */
417     logDest->plugIn = NULL;
418 
419     if (logDest->filter != NULL)
420 	destroyLogLevel(logDest->filter, NULL);
421 
422     /* ------------------------------------------------------------------------
423      * free rest items in struct, if needed
424      * --------------------------------------------------------------------- */
425     WebFreeIfNotNull(logDest->format);
426 
427     WebFreeIfNotNull(logDest);
428 
429     return TCL_OK;
430 }
431 
432 
433 
434 /* ----------------------------------------------------------------------------
435  * Web_Log -- distribute a log message
436  * ------------------------------------------------------------------------- */
Web_Log(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])437 int Web_Log(ClientData clientData,
438 	    Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
439 {
440 
441     LogData *logData;
442 
443     /* ------------------------------------------------------------------------
444      * check for internal data
445      * --------------------------------------------------------------------- */
446     WebAssertData(interp, clientData, "Web_Log", TCL_ERROR);
447     logData = (LogData *) clientData;
448 
449     /* ------------------------------------------------------------------------
450      * try to be fast
451      * --------------------------------------------------------------------- */
452     if (objc == 3) {
453 	return logImpl(interp, logData, Tcl_GetString(objv[1]), objv[2]);
454     }
455 
456     /* ------------------------------------------------------------------------
457      * wrong args
458      * --------------------------------------------------------------------- */
459     Tcl_WrongNumArgs(interp, 1, objv, "level message");
460     return TCL_ERROR;
461 }
462 
463 
464 /* ----------------------------------------------------------------------------
465  * Web_LogDest -- manage list of dests
466  * ------------------------------------------------------------------------- */
Web_LogDest(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])467 int Web_LogDest(ClientData clientData,
468 		Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
469 {
470 
471     LogData *logData = NULL;
472     int idx;
473     int iCurArg;
474 
475     static TCLCONST char *params[] = { "-maxchar",
476 	"-format",
477 	NULL
478     };
479     enum params
480     { MAXCHAR, FORMAT };
481     static TCLCONST char *subCommands[] = {
482       WEB_LOG_SUBCMD_ADD,
483       WEB_LOG_SUBCMD_DELETE,
484       WEB_LOG_SUBCMD_NAMES,
485       WEB_LOG_SUBCMD_LEVELS,
486       NULL
487     };
488     enum subCommands
489     { ADD, DELETE, NAMES, LEVELS };
490 
491     /* --------------------------------------------------------------------------
492      * check for internal data
493      * ----------------------------------------------------------------------- */
494     WebAssertData(interp, clientData, "Web_LogDest", TCL_ERROR)
495 	logData = (LogData *) clientData;
496 
497     /* --------------------------------------------------------------------------
498      * check arguments
499      * ----------------------------------------------------------------------- */
500     if (objc < 2) {
501 	Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
502 	return TCL_ERROR;
503     }
504 
505     /* --------------------------------------------------------------------------
506      * scan for options
507      * ----------------------------------------------------------------------- */
508     if (Tcl_GetIndexFromObj(interp, objv[1], subCommands, "option", 0, &idx)
509 	!= TCL_OK)
510 	return TCL_ERROR;
511 
512     /* --------------------------------------------------------------------------
513      * switch on subcommand
514      * ----------------------------------------------------------------------- */
515 
516     switch ((enum subCommands) idx) {
517 
518     case ADD:{
519 
520 	    /* ------------------------------------------------------------------------
521 	     * ok, add sytnax is as follows:
522 	     * web::logdest add [-format bla -maxchar 100] level {type specific stuff}
523 	     * 0            1   2                          j     j+1
524 	     * --------------------------------------------------------------------- */
525 
526 	    char *name = NULL;
527 	    char *format = NULL;
528 	    LogLevel *logLevel = NULL;
529 	    LogPlugIn *logPlugIn = NULL;
530 	    ClientData logPlugInData = NULL;
531 	    LogDest *logDest = NULL;
532 	    Tcl_Obj *tcloTmp;
533 	    long maxCharInMsg = -1;
534 	    int iUnknown = 0;
535 
536 	    /* argdbg(objc,objv,stdout); */
537 
538 	    iCurArg =
539 		argIndexOfFirstArg(objc - 1, &(objv[1]), params, NULL) + 1;
540 
541 	    /* check for known options */
542 	    iUnknown =
543 		argHasOnlyAccepted(objc - 1, &(objv[1]), params, iCurArg - 1);
544 	    if (iUnknown > 0) {
545 		/* let Tcl format the error message */
546 		Tcl_GetIndexFromObj(interp, objv[iUnknown + 1], params,
547 				    "option", 0, &idx);
548 		return TCL_ERROR;
549 	    }
550 
551 	    /* check for -format */
552 	    if ((tcloTmp = argValueOfKey(objc, objv, (char *)params[FORMAT])) != NULL) {
553 		format = allocAndSet(Tcl_GetString(tcloTmp));
554 	    }
555 	    else {
556 		format = allocAndSet(WEB_LOG_DEFAULTFORMAT);
557 	    }
558 
559 	    /* check for -maxchar */
560 	    if ((tcloTmp =
561 		 argValueOfKey(objc, objv, (char *)params[MAXCHAR])) != NULL) {
562 		if (Tcl_GetLongFromObj(interp, tcloTmp, &maxCharInMsg) ==
563 		    TCL_ERROR) {
564 		    LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__,
565 			    __LINE__, "web::logdest", WEBLOG_INFO,
566 			    "cannot read long from -maxchar \"",
567 			    Tcl_GetString(tcloTmp), "\"", NULL);
568 		    return TCL_ERROR;
569 		}
570 	    }
571 
572 	    /* ------------------------------------------------------------------------
573 	     * now iCurArg is level, iCurArg+1 is type, (+2 is type specific)
574 	     * --------------------------------------------------------------------- */
575 	    if ((iCurArg + 1) >= objc) {
576 		Tcl_WrongNumArgs(interp, 1, objv, WEB_LOG_USAGE_LOGDEST_ADD);
577 		WebFreeIfNotNull(format);
578 		return TCL_ERROR;
579 	    }
580 
581 	    /* ------------------------------------------------------------------------
582 	     * get handler for type
583 	     * --------------------------------------------------------------------- */
584 	    logPlugIn = (LogPlugIn *) getFromHashTable(logData->listOfPlugIns,
585 						       Tcl_GetString(objv
586 								     [iCurArg
587 								      + 1]));
588 	    if (logPlugIn == NULL) {
589 		Tcl_SetResult(interp, "no log handler of type \"", NULL);
590 		Tcl_AppendResult(interp, Tcl_GetString(objv[iCurArg + 1]),
591 				 "\" registered", NULL);
592 		WebFreeIfNotNull(format);
593 		return TCL_ERROR;
594 	    }
595 
596 	    /* ------------------------------------------------------------------------
597 	     * parse level
598 	     * --------------------------------------------------------------------- */
599 	    logLevel =
600 		parseLogLevel(interp, Tcl_GetString(objv[iCurArg]), "user",
601 			      -1);
602 	    if (logLevel == NULL) {
603 		WebFreeIfNotNull(format);
604 		return TCL_ERROR;
605 	    }
606 
607 	    /* ------------------------------------------------------------------------
608 	     * call constructor
609 	     * --------------------------------------------------------------------- */
610 	    if ((logPlugInData =
611 		 logPlugIn->constructor(interp,
612 					clientData, objc - (iCurArg + 1),
613 					&(objv[iCurArg + 1]))) == NULL) {
614 		destroyLogLevel(logLevel, NULL);
615 		WebFreeIfNotNull(name);
616 		WebFreeIfNotNull(format);
617 		return TCL_ERROR;
618 	    }
619 
620 	    /* ------------------------------------------------------------------------
621 	     * ok, make the logDest
622 	     * --------------------------------------------------------------------- */
623 	    logDest = createLogDest();
624 	    if (logDest == NULL) {
625 		Tcl_SetResult(interp, "cannot create log destination", NULL);
626 		destroyLogLevel(logLevel, NULL);
627 		WebFreeIfNotNull(name);
628 		WebFreeIfNotNull(format);
629 		return TCL_ERROR;
630 	    }
631 	    logDest->filter = logLevel;
632 	    logDest->format = format;
633 	    logDest->plugIn = logPlugIn;
634 	    logDest->plugInData = logPlugInData;
635 	    logDest->maxCharInMsg = maxCharInMsg;
636 	    logDest->keep = logData->keep;
637 
638 	    /* ----------------------------------------------------------
639 	     * and add to list
640 	     * ---------------------------------------------------------- */
641 
642 	    name = insertIntoDestList(logData, logDest);
643 	    if (name == NULL) {
644 		Tcl_SetResult(interp, "cannot append new log destination to list", NULL);
645 		destroyLogDest(logDest, interp);
646 		destroyLogLevel(logLevel, NULL);
647 		WebFreeIfNotNull(format);
648 		return TCL_ERROR;
649 	    }
650 	    Tcl_SetResult(interp, name, Tcl_Free);
651 	    return TCL_OK;
652 	}
653     case NAMES:{
654 
655 	    Tcl_ResetResult(interp);
656 
657 	    if (logData->listOfDests != NULL) {
658 	      int i;
659 	      LogDest ** logDests = logData->listOfDests;
660 	      for (i = 0; i < logData->destSize; i++) {
661 		if (logDests[i] != NULL) {
662 		  char *name = createLogName(LOG_DEST_PREFIX, i);
663 		  if  (name != NULL) {
664 		    Tcl_AppendElement(interp, name);
665 		    Tcl_Free(name);
666 		  }
667 		}
668 	      }
669 	    }
670 	    return TCL_OK;
671 	}
672     case LEVELS:{
673 
674 	    int namesIsFirst = TCL_OK;
675 	    LogDest *logDest = NULL;
676 
677 	    Tcl_SetResult(interp, "", NULL);
678 
679 	    if (logData->listOfDests != NULL) {
680 
681 	      int i;
682 	      LogDest ** logDests = logData->listOfDests;
683 	      for (i = 0; i < logData->destSize; i++) {
684 		if (logDests[i] != NULL) {
685 		  char * name = createLogName(LOG_DEST_PREFIX, i);
686 		    if (namesIsFirst == TCL_ERROR)
687 			Tcl_AppendResult(interp, "\n", NULL);
688 		    else
689 			namesIsFirst = TCL_ERROR;
690 		    logDest = logDests[i];
691 		    Tcl_AppendResult(interp,
692 				     name, " ",
693 				     logDest->filter->facility, ".",
694 				     getSeverityName(logDest->filter->
695 						     minSeverity), "-",
696 				     getSeverityName(logDest->filter->
697 						     maxSeverity), NULL);
698 		    Tcl_Free(name);
699 		}
700 	      }
701 	    }
702 	    return TCL_OK;
703 	}
704     case DELETE:{
705 
706 	    /*      0               1      2 */
707 	    /*      web::loglogDest delete logDest1 */
708 
709 	    switch (objc) {
710 	    case 3: {
711 	      LogDest ** logDests = logData->listOfDests;
712 	      if (!strcmp("-requests", Tcl_GetString(objv[2]))) {
713 		/* special case: delete all destinations NOT created during web::initializer */
714 		int i;
715 		for (i = 0; i < logData->destSize; i++) {
716 		  if (logDests[i] != NULL && !logDests[i]->keep) {
717 		    destroyLogDest(logDests[i], interp);
718 		    logDests[i] = NULL;
719 		  }
720 		}
721 		return TCL_OK;
722 	      } else {
723 		int inx = getIndexFromLogName(LOG_DEST_PREFIX"%d", Tcl_GetString(objv[2]));
724 		if (inx < 0
725 		    || inx >= logData->destSize
726 		    || logDests[inx] == NULL) {
727 		  Tcl_SetResult(interp, "no such log destination \"", NULL);
728 		  Tcl_AppendResult(interp, Tcl_GetString(objv[2]), "\"",
729 				   NULL);
730 		  return TCL_ERROR;
731 		}
732 		destroyLogDest(logDests[inx], interp);
733 		logDests[inx] = NULL;
734 		return TCL_OK;
735 		break;
736 	      }
737 	    }
738 	    case 2:
739 		/* --------------------------------------------------------
740 		 * no argument --> resets the list
741 		 * -------------------------------------------------------- */
742 		if (logData->listOfDests != NULL) {
743 		  int i;
744 		  LogDest ** logDests = logData->listOfDests;
745 		  for (i = 0; i < logData->destSize; i++) {
746 		    if (logDests[i] != NULL) {
747 		      destroyLogDest(logDests[i], interp);
748 		      logDests[i] = NULL;
749 		    }
750 		  }
751 		}
752 		return TCL_OK;
753 		break;
754 	    default:
755 		Tcl_WrongNumArgs(interp, 1, objv, "delete ?destname?");
756 		return TCL_ERROR;
757 	    }
758 	    break;
759 	}
760     default:
761 	return TCL_OK;
762     }
763 }
764 
765 
766 /* ----------------------------------------------------------------------------
767  * Web_LogFilter -- manage list of filters
768  * ------------------------------------------------------------------------- */
Web_LogFilter(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])769 int Web_LogFilter(ClientData clientData,
770 		  Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
771 {
772 
773     LogData *logData;
774     int idx;
775 
776     static TCLCONST char *subCommands[] = {
777 	WEB_LOG_SUBCMD_ADD,
778 	WEB_LOG_SUBCMD_DELETE,
779 	WEB_LOG_SUBCMD_NAMES,
780 	WEB_LOG_SUBCMD_LEVELS,
781 	NULL
782     };
783     enum subCommands
784     { ADD, DELETE, NAMES, LEVELS };
785 
786     /* --------------------------------------------------------------------------
787      * check for internal data
788      * ----------------------------------------------------------------------- */
789     WebAssertData(interp, clientData, "Web_LogFilter", TCL_ERROR)
790 	logData = (LogData *) clientData;
791 
792     /* --------------------------------------------------------------------------
793      * enough arguments ?
794      * ----------------------------------------------------------------------- */
795     if (objc < 2) {
796 	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?");
797 	return TCL_ERROR;
798     }
799 
800     /* ------------------------------------------------------------------------
801      * check subcommand
802      * --------------------------------------------------------------------- */
803     if (Tcl_GetIndexFromObj(interp, objv[1], subCommands, "option", 0, &idx)
804 	!= TCL_OK) {
805 	return TCL_ERROR;
806     }
807 
808     switch ((enum subCommands) idx) {
809     case ADD:{
810 
811 	    /* ------------------------------------------------------------------------
812 	     * web::loglevel add sytnax is as follows:
813 	     * web::loglevel add level
814 	     * 0              1   2
815 	     * --------------------------------------------------------------------- */
816 	    LogLevel *logLevel = NULL;
817 	    char *name = NULL;
818 
819 	    /* ------------------------------------------------------------------------
820 	     * enough arguments ?
821 	     * --------------------------------------------------------------------- */
822 	    if (objc != 3) {
823 	        Tcl_WrongNumArgs(interp, 2, objv, "level");
824 		return TCL_ERROR;
825 	    }
826 
827 	    /* ------------------------------------------------------------------------
828 	     * parse level
829 	     * --------------------------------------------------------------------- */
830 	    logLevel =
831 		parseLogLevel(interp, Tcl_GetString(objv[2]), "user", -1);
832 	    if (logLevel == NULL) {
833 		WebFreeIfNotNull(name);
834 		return TCL_ERROR;
835 	    }
836 	    logLevel->keep = logData->keep;
837 
838 	    /* ------------------------------------------------------------------------
839 	     * and add to list
840 	     * --------------------------------------------------------------------- */
841 	    name = insertIntoFilterList(logData, logLevel);
842 	    if (name == NULL) {
843 		Tcl_SetResult(interp, "cannot append new log filter to list", NULL);
844 		destroyLogLevel(logLevel, NULL);
845 		return TCL_ERROR;
846 	    }
847 	    Tcl_SetResult(interp, name, Tcl_Free);
848 	    return TCL_OK;
849 	    break;
850 	}
851     case NAMES:{
852 
853 	    Tcl_ResetResult(interp);
854 
855 	    if (logData->listOfFilters != NULL) {
856 	      int i;
857 	      LogLevel **logLevels = logData->listOfFilters;
858 	      for (i = 0; i < logData->filterSize; i++) {
859 		if (logLevels[i] != NULL) {
860 		  char * name = createLogName(LOG_FILTER_PREFIX, i);
861 		  if (name != NULL) {
862 		    Tcl_AppendElement(interp, name);
863 		    Tcl_Free(name);
864 		  }
865 		}
866 	      }
867 	    }
868 	    return TCL_OK;
869 	    break;
870 	}
871 
872     case LEVELS:{
873 
874 	    LogLevel *logLevel = NULL;
875 	    int namesIsFirst = TCL_OK;
876 
877 	    Tcl_SetResult(interp, "", NULL);
878 
879 	    if (logData->listOfFilters != NULL) {
880 	      int i;
881 	      LogLevel ** logLevels = logData->listOfFilters;
882 	      for (i = 0; i < logData->filterSize; i++) {
883 		if (logLevels[i] != NULL) {
884 		  char * name = createLogName(LOG_FILTER_PREFIX, i);
885 		    if (namesIsFirst == TCL_ERROR)
886 			Tcl_AppendResult(interp, "\n", NULL);
887 		    else
888 			namesIsFirst = TCL_ERROR;
889 		    logLevel = logLevels[i];
890 		    Tcl_AppendResult(interp,
891 				     name, " ",
892 				     logLevel->facility, ".",
893 				     getSeverityName(logLevel->minSeverity),
894 				     "-",
895 				     getSeverityName(logLevel->maxSeverity),
896 				     NULL);
897 		    Tcl_Free(name);
898 		}
899 	      }
900 	    }
901 	    return TCL_OK;
902 	    break;
903 	}
904     case DELETE:{
905 
906 	    /*      0              1      2 */
907 	    /*      web::loglevel delete logLevel1 */
908 
909 	    switch (objc) {
910 	    case 3: {
911 	      LogLevel ** logLevels = logData->listOfFilters;
912 	      if (!strcmp("-requests", Tcl_GetString(objv[2]))) {
913 		/* special case: delete all levels NOT created during web::initializer */
914 		int i;
915 		for (i = 0; i < logData->filterSize; i++) {
916 		  if (logLevels[i] != NULL && !logLevels[i]->keep) {
917 		    destroyLogLevel(logLevels[i], interp);
918 		    logLevels[i] = NULL;
919 		  }
920 		}
921 		return TCL_OK;
922 	      } else {
923 		int inx = getIndexFromLogName(LOG_FILTER_PREFIX"%d", Tcl_GetString(objv[2]));
924 		if (inx < 0
925 		    || inx >= logData->filterSize
926 		    || logLevels[inx] == NULL) {
927 		  Tcl_SetResult(interp, "no such log filter \"", NULL);
928 		  Tcl_AppendResult(interp, Tcl_GetString(objv[2]), "\"",
929 				   NULL);
930 		  return TCL_ERROR;
931 		}
932 		destroyLogLevel(logLevels[inx], interp);
933 		logLevels[inx] = NULL;
934 		return TCL_OK;
935 		break;
936 	      }
937 	    }
938 	    case 2:
939 		/* -----------------------------------------------------
940 		 * no argument --> resets the list
941 		 * ----------------------------------------------------- */
942 		if (logData->listOfFilters != NULL) {
943 		  int i;
944 		  LogLevel ** logLevels = logData->listOfFilters;
945 		  for (i = 0; i < logData->filterSize; i++) {
946 		    if (logLevels[i] != NULL) {
947 		      destroyLogLevel(logLevels[i], interp);
948 		      logLevels[i] = NULL;
949 		    }
950 		  }
951 		}
952 		return TCL_OK;
953 		break;
954 	    default:
955 		Tcl_WrongNumArgs(interp, 1, objv, "delete ?filtername?");
956 		return TCL_ERROR;
957 	    }
958 	    break;
959         }
960     default:
961 	return TCL_OK;
962     }
963 }
964 
965 /* ----------------------------------------------------------------------------
966  * createLogPlugIn --
967  * ------------------------------------------------------------------------- */
createLogPlugIn()968 LogPlugIn DLLEXPORT *createLogPlugIn()
969 {
970 
971     LogPlugIn *logPlugIn = NULL;
972 
973     logPlugIn = WebAllocInternalData(LogPlugIn);
974     if (logPlugIn != NULL) {
975 	logPlugIn->constructor = NULL;
976 	logPlugIn->destructor = NULL;
977 	logPlugIn->handler = NULL;
978     }
979     return logPlugIn;
980 }
981 
982 /* ----------------------------------------------------------------------------
983  * destroyLogPlugIn --
984  * ------------------------------------------------------------------------- */
destroyLogPlugIn(void * plugIn,void * dum)985 int destroyLogPlugIn(void *plugIn, void *dum)
986 {
987 
988     LogPlugIn *logPlugIn = (LogPlugIn *) plugIn;
989 
990     if (logPlugIn != NULL) {
991 	WebFreeIfNotNull(logPlugIn);
992     } else {
993 	fprintf(stderr, "destroyLogPlugin: plugin doesn't exist.\n");
994     }
995     return TCL_OK;
996 }
997 
998 
999 /* ----------------------------------------------------------------------------
1000  * registerLogPlugIn --
1001  * ------------------------------------------------------------------------- */
registerLogPlugIn(Tcl_Interp * interp,char * type,LogPlugIn * logPlugIn)1002 int DLLEXPORT registerLogPlugIn(Tcl_Interp * interp, char *type, LogPlugIn * logPlugIn)
1003 {
1004 
1005     LogData *logData;
1006 
1007     if ((interp == NULL) || (logPlugIn == NULL) || (type == NULL))
1008 	return TCL_ERROR;
1009 
1010     /* --------------------------------------------------------------------------
1011      * try to get the logData data
1012      * ----------------------------------------------------------------------- */
1013     logData = (LogData *) Tcl_GetAssocData(interp, WEB_LOG_ASSOC_DATA, NULL);
1014     WebAssertData(interp, logData, "log", TCL_ERROR)
1015 
1016 	/* --------------------------------------------------------------------------
1017 	 * append
1018 	 * ----------------------------------------------------------------------- */
1019 	if (logData->listOfPlugIns == NULL)
1020 	return TCL_ERROR;
1021     return appendToHashTable(logData->listOfPlugIns, type,
1022 			     (ClientData) logPlugIn);
1023 }
1024 
1025 /* ----------------------------------------------------------------------------
1026  * LOG_MSG -- write log message to webshell log facility
1027  * <interp>   interp (needed to access log module data)
1028  * <flag>     use WRITE_LOG , SET_RESULT, INTERP_ERRORINFO
1029  * <filename> of caller "test.c"
1030  * <linenr>   of caller   "123"
1031  * <cmd>      caller         "testCommand"
1032  * <level>    webshell log level
1033  * <msg>      message parts (NULL-terminated list)
1034  * ------------------------------------------------------------------------- */
LOG_MSG(Tcl_Interp * interp,int flag,char * filename,int linenr,char * cmd,char * level,char * msg,...)1035 void DLLEXPORT LOG_MSG(Tcl_Interp * interp, int flag, char *filename, int linenr,
1036 	     char *cmd, char *level, char *msg, ...)
1037 {
1038 
1039     Tcl_Obj *errobj = NULL;
1040     Tcl_Obj *resobj = NULL;
1041 #ifdef DEBUG
1042     Tcl_Obj *debugobj = NULL;
1043 #endif
1044     char *msgpart = NULL;
1045 
1046     va_list ap;
1047 
1048     errobj = Tcl_NewObj();
1049     Tcl_IncrRefCount(errobj);
1050 
1051     resobj = Tcl_NewObj();
1052     Tcl_IncrRefCount(resobj);
1053 
1054     if (interp == NULL)
1055 	Tcl_AppendToObj(errobj, "interp = null", -1);
1056 
1057 #ifdef DEBUG
1058     debugobj = Tcl_NewIntObj(linenr);
1059     Tcl_IncrRefCount(debugobj);
1060     Tcl_AppendStringsToObj(errobj, "File:", filename,
1061 			   ", Line:", Tcl_GetString(debugobj),
1062 			   ", ", (char *) NULL);
1063 #endif
1064 
1065     Tcl_AppendStringsToObj(errobj, cmd, ": ", msg, (char *) NULL);
1066 
1067     if (flag & SET_RESULT)
1068 	Tcl_AppendStringsToObj(resobj, msg, NULL);
1069 
1070     for (va_start(ap, msg); (msgpart = va_arg(ap, char *)) != NULL;) {
1071 	Tcl_AppendStringsToObj(errobj, msgpart, NULL);
1072 	if (flag & SET_RESULT)
1073 	    Tcl_AppendStringsToObj(resobj, msgpart, NULL);
1074     }
1075 
1076     va_end(ap);
1077 
1078     if (interp != NULL && (flag & WRITE_LOG)) {
1079 	webLog(interp, level, Tcl_GetString(errobj));
1080     }
1081 
1082 #ifdef WRITE_TO_STDOUT
1083     printf("%s\n", Tcl_GetString(errobj));
1084     fflush(stdout);
1085 #endif
1086 
1087     if ((flag & INTERP_ERRORINFO) && (interp != NULL)) {
1088 	char *errorInfo = (char *) Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
1089 	if (errorInfo != NULL)
1090 	    webLog(interp, WEBLOG_DEBUG, errorInfo);
1091 	else
1092 	    webLog(interp, WEBLOG_DEBUG, "panic: errorInfo not set");
1093     }
1094 
1095     if (flag & SET_RESULT)
1096 	Tcl_SetObjResult(interp, resobj);
1097 
1098     Tcl_DecrRefCount(errobj);
1099     Tcl_DecrRefCount(resobj);	/* ok, ref count was incremented before */
1100 #ifdef DEBUG
1101     Tcl_DecrRefCount(debugobj);
1102 #endif
1103 }
1104