Bug Summary

File:d/log.c
Warning:line 848, column 21
Using a fixed address is not portable because that address will probably not be valid in all environments or platforms

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name log.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsd -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsd -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c log.c
1/*
2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.1 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://mozilla.org/.
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9 * the License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Original Code is AOLserver Code and related documentation
13 * distributed by AOL.
14 *
15 * The Initial Developer of the Original Code is America Online,
16 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
17 * Inc. All Rights Reserved.
18 *
19 * Alternatively, the contents of this file may be used under the terms
20 * of the GNU General Public License (the "GPL"), in which case the
21 * provisions of GPL are applicable instead of those above. If you wish
22 * to allow use of your version of this file only under the terms of the
23 * GPL and not to allow others to use your version of this file under the
24 * License, indicate your decision by deleting the provisions above and
25 * replace them with the notice and other provisions required by the GPL.
26 * If you do not delete the provisions above, a recipient may use your
27 * version of this file under either the License or the GPL.
28 */
29
30
31/*
32 * log.c --
33 *
34 * Manage the global error log file.
35 */
36
37#include "nsd.h"
38
39#define COLOR_BUFFER_SIZE255u 255u
40#define TIME_BUFFER_SIZE100u 100u
41
42/*
43 * The following define available flags bits.
44 */
45
46#define LOG_ROLL0x01u 0x01u
47#define LOG_EXPAND0x02u 0x02u
48#define LOG_SEC0x04u 0x04u
49#define LOG_USEC0x08u 0x08u
50#define LOG_USEC_DIFF0x10u 0x10u
51#define LOG_THREAD0x20u 0x20u
52#define LOG_COLORIZE0x40u 0x40u
53
54/*
55 * The following struct represents a log entry header as stored in the
56 * per-thread cache. It is followed by a variable-length log string as
57 * passed by the caller (format expanded).
58 */
59
60typedef struct LogEntry {
61 Ns_LogSeverity severity; /* Entry's severity */
62 Ns_Time stamp; /* Timestamp of the entry */
63 size_t offset; /* Offset into the text buffer */
64 size_t length; /* Length of the log message */
65 struct LogEntry *nextPtr; /* Next in the list of entries */
66} LogEntry;
67
68/*
69 * The following struct represents one registered log callback.
70 * Log filters are used to produce log lines into the log sinks
71 * and are invoked one by one for every log entry in the cache.
72 * Each filter (usually) maintain its own log sink.
73 */
74
75typedef struct LogFilter {
76 Ns_LogFilter *proc; /* User-given function for generating logs */
77 Ns_FreeProc *freeArgProc; /* User-given function to free passed arg */
78 void *arg; /* Argument passed to proc and free */
79 int refcnt; /* Number of current consumers */
80 struct LogFilter *nextPtr; /* Maintains double linked list */
81 struct LogFilter *prevPtr;
82} LogFilter;
83
84/*
85 * The following struct maintains per-thread cached log entries.
86 * The cache is a simple dynamic string where variable-length
87 * LogEntry'ies (see below) are appended, one after another.
88 */
89
90typedef struct LogCache {
91 bool_Bool hold; /* Flag: keep log entries in cache */
92 bool_Bool finalizing; /* Flag: log is finalizing, no more ops allowed */
93 int count; /* Number of entries held in the cache */
94 time_t gtime; /* For GMT time calculation */
95 time_t ltime; /* For local time calculations */
96 char gbuf[TIME_BUFFER_SIZE100u]; /* Buffer for GMT time string rep */
97 char lbuf[TIME_BUFFER_SIZE100u]; /* Buffer for local time string rep */
98 size_t gbufSize;
99 size_t lbufSize;
100 LogEntry *firstEntry; /* First in the list of log entries */
101 LogEntry *currentEntry; /* Current in the list of log entries */
102 Ns_DStringTcl_DString buffer; /* The log entries cache text-cache */
103} LogCache;
104
105static LogEntry *LogEntryGet(LogCache *cachePtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
106static void LogEntryFree(LogCache *cachePtr, LogEntry *logEntryPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
107
108#if !defined(NS_THREAD_LOCAL__thread)
109static void LogEntriesFree(void *arg);
110#endif
111
112/*
113 * Local functions defined in this file
114 */
115
116static Ns_TlsCleanup FreeCache;
117static Tcl_PanicProc Panic;
118
119static Ns_LogFilter LogToFile;
120static Ns_LogFilter LogToTcl;
121static Ns_LogFilter LogToDString;
122
123static Tcl_ObjCmdProc NsLogCtlSeverityObjCmd;
124
125static LogCache* GetCache(void)
126 NS_GNUC_RETURNS_NONNULL;
127
128static int GetSeverityFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
129 void **addrPtrPtr)
130 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
131
132static void LogFlush(LogCache *cachePtr, LogFilter *listPtr, int count,
133 bool_Bool trunc, bool_Bool locked)
134 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
135
136static Ns_LogCallbackProc LogOpen;
137static Ns_LogCallbackProc LogClose;
138
139static char* LogTime(LogCache *cachePtr, const Ns_Time *timePtr, bool_Bool gmt)
140 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
141
142static Tcl_Obj *LogStats(void);
143
144static char *LogSeverityColor(char *buffer, Ns_LogSeverity severity)
145 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
146
147static int ObjvTableLookup(const char *path, const char *param, Ns_ObjvTable *tablePtr, int *idxPtr)
148 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4)));
149
150
151
152/*
153 * Static variables defined in this file
154 */
155
156static Ns_Tls tls;
157#if !defined(NS_THREAD_LOCAL__thread)
158static Ns_Tls tlsEntry;
159#endif
160static Ns_Mutex lock;
161static Ns_Cond cond;
162
163static bool_Bool logOpenCalled = NS_FALSE0;
164static const char *logfileName = NULL((void*)0);
165static const char *rollfmt = NULL((void*)0);
166static unsigned int flags = 0u;
167static int maxbackup;
168
169static LogFilter *filters;
170static const char *const filterType = "ns:logfilter";
171static const char *const severityType = "ns:logseverity";
172
173static const unsigned char LOG_COLOREND[] = { 0x1bu, UCHAR('[')((unsigned char)('[')), UCHAR('0')((unsigned char)('0')), UCHAR('m')((unsigned char)('m')), 0u };
174static const unsigned char LOG_COLORSTART[] = { 0x1bu, UCHAR('[')((unsigned char)('[')), 0u };
175
176typedef enum {
177 COLOR_BLACK = 30u,
178 COLOR_RED = 31u,
179 COLOR_GREEN = 32u,
180 COLOR_YELLOW = 33u,
181 COLOR_BLUE = 34u,
182 COLOR_MAGENTA = 35u,
183 COLOR_CYAN = 36u,
184 COLOR_GRAY = 37u,
185 COLOR_DEFAULT = 39u
186} LogColor;
187
188typedef enum {
189 COLOR_NORMAL = 0u,
190 COLOR_BRIGHT = 1u
191} LogColorIntensity;
192
193static LogColor prefixColor = COLOR_GREEN;
194static LogColorIntensity prefixIntensity = COLOR_NORMAL;
195
196static Ns_ObjvTable colors[] = {
197 {"black", COLOR_BLACK},
198 {"red", COLOR_RED},
199 {"green", COLOR_GREEN},
200 {"yellow", COLOR_YELLOW},
201 {"blue", COLOR_BLUE},
202 {"magenta", COLOR_MAGENTA},
203 {"cyan", COLOR_CYAN},
204 {"gray", COLOR_GRAY},
205 {"default", COLOR_DEFAULT},
206 {NULL((void*)0), 0u}
207};
208
209static Ns_ObjvTable intensities[] = {
210 {"normal", COLOR_NORMAL},
211 {"bright", COLOR_BRIGHT},
212 {NULL((void*)0), 0u}
213};
214
215/*
216 * The following table defines which severity levels
217 * are currently active. The order is important: keep
218 * it in sync with the Ns_LogSeverity enum.
219 *
220 * "640 (slots) should be enough for everyone..."
221 */
222
223static struct {
224 const char *label;
225 bool_Bool enabled;
226 long count;
227 LogColor color;
228 LogColorIntensity intensity;
229} severityConfig[640] = {
230 { "Notice", NS_TRUE1, 0, COLOR_DEFAULT, COLOR_NORMAL },
231 { "Warning", NS_TRUE1, 0, COLOR_DEFAULT, COLOR_BRIGHT },
232 { "Error", NS_TRUE1, 0, COLOR_RED, COLOR_BRIGHT },
233 { "Fatal", NS_TRUE1, 0, COLOR_RED, COLOR_BRIGHT },
234 { "Bug", NS_TRUE1, 0, COLOR_RED, COLOR_BRIGHT },
235 { "Debug", NS_FALSE0, 0, COLOR_BLUE, COLOR_NORMAL },
236 { "Dev", NS_FALSE0, 0, COLOR_GREEN, COLOR_NORMAL }
237};
238
239static const Ns_LogSeverity severityMaxCount = (Ns_LogSeverity)(sizeof(severityConfig) / sizeof(severityConfig[0]));
240static Ns_LogSeverity severityIdx = 0;
241
242static Tcl_HashTable severityTable; /* Map severity names to indexes for Tcl. */
243
244
245
246/*
247 *----------------------------------------------------------------------
248 *
249 * NsInitLog --
250 *
251 * Initialize the log API and TLS slot.
252 *
253 * Results:
254 * None.
255 *
256 * Side effects:
257 * None.
258 *
259 *----------------------------------------------------------------------
260 */
261
262void
263NsInitLog(void)
264{
265 Tcl_HashEntry *hPtr;
266 char buf[21];
267 int isNew;
268 Ns_LogSeverity i;
269
270 Ns_MutexSetName(&lock, "ns:log");
271 Ns_TlsAlloc(&tls, FreeCache);
272#if !defined(NS_THREAD_LOCAL__thread)
273 Ns_TlsAlloc(&tlsEntry, LogEntriesFree);
274#endif
275 Tcl_InitHashTable(&severityTable, TCL_STRING_KEYS(0));
276
277 Tcl_SetPanicProc(Panic);
278 Ns_AddLogFilter(LogToFile, INT2PTR(STDERR_FILENO)((void *)(intptr_t)(2)), NULL((void*)0));
1
Calling 'Ns_AddLogFilter'
279
280 /*
281 * Initialize the entire space with backwards-compatible integer keys.
282 */
283 for (i = PredefinedLogSeveritiesCount; i < severityMaxCount; i++) {
284 (void) ns_uint32toa(buf, (uint32_t)i);
285 hPtr = Tcl_CreateHashEntry(&severityTable, buf, &isNew)(*((&severityTable)->createProc))(&severityTable, (
const char *)(buf), &isNew)
;
286 Tcl_SetHashValue(hPtr, INT2PTR(i))((hPtr)->clientData = (ClientData) (((void *)(intptr_t)(i)
)))
;
287 severityConfig[i].label = Tcl_GetHashKey(&severityTable, hPtr)((void *) (((&severityTable)->keyType == (1) || (&
severityTable)->keyType == (-1)) ? (hPtr)->key.oneWordValue
: (hPtr)->key.string))
;
288 severityConfig[i].enabled = NS_FALSE0;
289 }
290
291 /*
292 * Initialize the built-in severities and lowercase aliases.
293 */
294 for (i = 0; i < PredefinedLogSeveritiesCount; i++) {
295 size_t labelLength;
296
297 (void) Ns_CreateLogSeverity(severityConfig[i].label);
298 labelLength = strlen(severityConfig[i].label);
299 if (labelLength < sizeof(buf)) {
300 memcpy(buf, severityConfig[i].label, labelLength + 1u);
301 } else {
302 memcpy(buf, severityConfig[i].label, sizeof(buf) - 1u);
303 buf[sizeof(buf) - 1u] = '\0';
304 }
305 hPtr = Tcl_CreateHashEntry(&severityTable, Ns_StrToLower(buf), &isNew)(*((&severityTable)->createProc))(&severityTable, (
const char *)(Ns_StrToLower(buf)), &isNew)
;
306 Tcl_SetHashValue(hPtr, INT2PTR(i))((hPtr)->clientData = (ClientData) (((void *)(intptr_t)(i)
)))
;
307 }
308}
309
310
311
312/*
313 *----------------------------------------------------------------------
314 *
315 * ObjvTableLookup --
316 *
317 * Lookup a value from an Ns_ObjvTable and return its associated
318 * value in the last parameter, if the lookup was successful.
319 *
320 * Results:
321 * Tcl return code.
322 *
323 * Side effects:
324 * None.
325 *
326 *----------------------------------------------------------------------
327 */
328
329static int
330ObjvTableLookup(const char *path, const char *param, Ns_ObjvTable *tablePtr, int *idxPtr)
331{
332 size_t len;
333 int result, pos = 1;
334 const char *valueString;
335
336 NS_NONNULL_ASSERT(path != NULL)((void) (0));
337 NS_NONNULL_ASSERT(param != NULL)((void) (0));
338 NS_NONNULL_ASSERT(tablePtr != NULL)((void) (0));
339 NS_NONNULL_ASSERT(idxPtr != NULL)((void) (0));
340
341 valueString = Ns_ConfigString(path, param, NS_EMPTY_STRING);
342 assert(valueString != NULL)((void) (0));
343
344 len = strlen(valueString);
345 if (len > 0u) {
346 Ns_ObjvSpec spec;
347 Tcl_Obj *objPtr = Tcl_NewStringObj(valueString, (int)len);
348
349 spec.arg = tablePtr;
350 spec.dest = idxPtr;
351 result = Ns_ObjvIndex(&spec, NULL((void*)0), &pos, &objPtr);
352
353 if (unlikely(result != TCL_OK)(__builtin_expect((result != 0), 0))) {
354 Ns_DStringTcl_DString ds, *dsPtr = &ds;
355
356 Ns_DStringInitTcl_DStringInit(dsPtr);
357 while (tablePtr->key != NULL((void*)0)) {
358 Ns_DStringNAppendTcl_DStringAppend(dsPtr, tablePtr->key, -1);
359 Ns_DStringNAppendTcl_DStringAppend(dsPtr, " ", 1);
360 tablePtr++;
361 }
362 Ns_DStringSetLengthTcl_DStringSetLength(dsPtr, Ns_DStringLength(dsPtr)((dsPtr)->length) - 1);
363 Ns_Log(Warning, "ignoring invalid value '%s' for parameter '%s'; "
364 "possible values are: %s",
365 valueString, param, Ns_DStringValue(dsPtr)((dsPtr)->string));
366 Ns_DStringFreeTcl_DStringFree(dsPtr);
367 }
368 Tcl_DecrRefCount(objPtr)do { Tcl_Obj *_objPtr = (objPtr); if (_objPtr->refCount-- <=
1) { TclFreeObj(_objPtr); } } while(0)
;
369
370 } else {
371 result = TCL_ERROR1;
372 }
373
374 return result;
375}
376
377
378/*
379 *----------------------------------------------------------------------
380 *
381 * NsConfigLog --
382 *
383 * Config the logging interface.
384 *
385 * Results:
386 * None.
387 *
388 * Side effects:
389 * Depends on configuration file.
390 *
391 *----------------------------------------------------------------------
392 */
393
394void
395NsConfigLog(void)
396{
397 Ns_DStringTcl_DString ds;
398 const char *path = NS_GLOBAL_CONFIG_PARAMETERS"ns/parameters";
399 Ns_Set *set = Ns_ConfigCreateSection(path);
400
401 severityConfig[Debug ].enabled = Ns_ConfigBool(path, "logdebug", NS_FALSE0);
402 severityConfig[Dev ].enabled = Ns_ConfigBool(path, "logdev", NS_FALSE0);
403 severityConfig[Notice].enabled = Ns_ConfigBool(path, "lognotice", NS_TRUE1);
404
405 if (Ns_ConfigBool(path, "logroll", NS_TRUE1) == NS_TRUE1) {
406 flags |= LOG_ROLL0x01u;
407 }
408 if (Ns_ConfigBool(path, "logsec", NS_TRUE1) == NS_TRUE1) {
409 flags |= LOG_SEC0x04u;
410 }
411 if (Ns_ConfigBool(path, "logusec", NS_FALSE0) == NS_TRUE1) {
412 flags |= LOG_USEC0x08u;
413 }
414 if (Ns_ConfigBool(path, "logusecdiff", NS_FALSE0) == NS_TRUE1) {
415 flags |= LOG_USEC_DIFF0x10u;
416 }
417 if (Ns_ConfigBool(path, "logexpanded", NS_FALSE0) == NS_TRUE1) {
418 flags |= LOG_EXPAND0x02u;
419 }
420 if (Ns_ConfigBool(path, "logthread", NS_TRUE1) == NS_TRUE1) {
421 flags |= LOG_THREAD0x20u;
422 }
423 if (Ns_ConfigBool(path, "logcolorize", NS_FALSE0) == NS_TRUE1) {
424 flags |= LOG_COLORIZE0x40u;
425 }
426 if ((flags & LOG_COLORIZE0x40u) != 0u) {
427 int result, idx;
428
429 result = ObjvTableLookup(path, "logprefixcolor", colors, &idx);
430 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
431 prefixColor = (LogColor)idx;
432 }
433 result = ObjvTableLookup(path, "logprefixintensity", intensities, &idx);
434 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
435 prefixIntensity = (LogColorIntensity)idx;
436 }
437 } else {
438 (void) Ns_ConfigString(path, "logprefixcolor", NS_EMPTY_STRING);
439 (void) Ns_ConfigString(path, "logprefixintensity", NS_EMPTY_STRING);
440 }
441
442 maxbackup = Ns_ConfigIntRange(path, "logmaxbackup", 10, 0, 999);
443
444 logfileName = Ns_ConfigString(path, "serverlog", "nsd.log");
445 if (Ns_PathIsAbsolute(logfileName) == NS_FALSE0) {
446 int length;
447
448 Ns_DStringInitTcl_DStringInit(&ds);
449 if (Ns_HomePathExists("logs", (char *)0L)) {
450 (void)Ns_HomePath(&ds, "logs", logfileName, (char *)0L);
451 } else {
452 (void)Ns_HomePath(&ds, logfileName, (char *)0L);
453 }
454 length = ds.length;
455 logfileName = Ns_DStringExport(&ds);
456 Ns_SetUpdateSz(set, "serverlog", 9, logfileName, length);
457 }
458
459 rollfmt = Ns_ConfigString(path, "logrollfmt", NS_EMPTY_STRING);
460
461}
462
463
464/*
465 *----------------------------------------------------------------------
466 *
467 * Ns_InfoErrorLog --
468 *
469 * Returns the filename of the log file.
470 *
471 * Results:
472 * Log filename or NULL if none.
473 *
474 * Side effects:
475 * None.
476 *
477 *----------------------------------------------------------------------
478 */
479
480const char *
481Ns_InfoErrorLog(void)
482{
483 return logfileName;
484}
485
486
487/*
488 *----------------------------------------------------------------------
489 *
490 * Ns_CreateLogSeverity --
491 *
492 * Create and return a new log severity with the given name,
493 * which will initially be disabled (except for the built-ins).
494 *
495 * Results:
496 * The severity.
497 *
498 * Side effects:
499 * Server will exit if max severities exceeded.
500 *
501 *----------------------------------------------------------------------
502 */
503
504Ns_LogSeverity
505Ns_CreateLogSeverity(const char *name)
506{
507 Ns_LogSeverity severity;
508 Tcl_HashEntry *hPtr;
509 int isNew = 0;
510
511 NS_NONNULL_ASSERT(name != NULL)((void) (0));
512
513 if (severityIdx >= severityMaxCount) {
514 Ns_Fatal("max log severities exceeded");
515 }
516 Ns_MutexLock(&lock);
517 hPtr = Tcl_CreateHashEntry(&severityTable, name, &isNew)(*((&severityTable)->createProc))(&severityTable, (
const char *)(name), &isNew)
;
518 if (isNew != 0) {
519 /*
520 * Create new severity.
521 */
522 severity = severityIdx++;
523 Tcl_SetHashValue(hPtr, INT2PTR(severity))((hPtr)->clientData = (ClientData) (((void *)(intptr_t)(severity
))))
;
524 severityConfig[severity].label = Tcl_GetHashKey(&severityTable, hPtr)((void *) (((&severityTable)->keyType == (1) || (&
severityTable)->keyType == (-1)) ? (hPtr)->key.oneWordValue
: (hPtr)->key.string))
;
525 if (severity > Dev) {
526 /*
527 * For the lower severities, we have already defaults; initialize
528 * just the higher ones.
529 */
530 severityConfig[severity].enabled = NS_FALSE0;
531 /*
532 * Initialize new severity with default colors.
533 */
534 severityConfig[severity].color = COLOR_DEFAULT;
535 severityConfig[severity].intensity = COLOR_NORMAL;
536 }
537 } else {
538 severity = PTR2INT(Tcl_GetHashValue(hPtr))((int)(intptr_t)(((hPtr)->clientData)));
539 }
540 Ns_MutexUnlock(&lock);
541
542 return severity;
543}
544
545
546/*
547 *----------------------------------------------------------------------
548 *
549 * Ns_LogSeverityName --
550 *
551 * Given a log severity, return a pointer to its name.
552 *
553 * Results:
554 * The severity name.
555 *
556 * Side effects:
557 * None.
558 *
559 *----------------------------------------------------------------------
560 */
561
562const char *
563Ns_LogSeverityName(Ns_LogSeverity severity)
564{
565 const char *result;
566
567 if (severity < severityMaxCount) {
568 result = severityConfig[severity].label;
569 } else {
570 result = "Unknown";
571 }
572 return result;
573}
574
575
576/*
577 *----------------------------------------------------------------------
578 *
579 * LogSeverityColor --
580 *
581 * Given a log severity, set color in terminal
582 *
583 * Results:
584 * A print string for setting the color in the terminal.
585 *
586 * Side effects:
587 * None.
588 *
589 *----------------------------------------------------------------------
590 */
591
592static char *
593LogSeverityColor(char *buffer, Ns_LogSeverity severity)
594{
595 if (severity < severityMaxCount) {
596 snprintf(buffer, COLOR_BUFFER_SIZE, "%s%d;%dm", LOG_COLORSTART,__builtin___snprintf_chk (buffer, 255u, 2 - 1, __builtin_object_size
(buffer, 2 > 1), "%s%d;%dm", LOG_COLORSTART, severityConfig
[severity].intensity, severityConfig[severity].color)
597 severityConfig[severity].intensity,__builtin___snprintf_chk (buffer, 255u, 2 - 1, __builtin_object_size
(buffer, 2 > 1), "%s%d;%dm", LOG_COLORSTART, severityConfig
[severity].intensity, severityConfig[severity].color)
598 severityConfig[severity].color)__builtin___snprintf_chk (buffer, 255u, 2 - 1, __builtin_object_size
(buffer, 2 > 1), "%s%d;%dm", LOG_COLORSTART, severityConfig
[severity].intensity, severityConfig[severity].color)
;
599 } else {
600 snprintf(buffer, COLOR_BUFFER_SIZE, "%s0m", LOG_COLORSTART)__builtin___snprintf_chk (buffer, 255u, 2 - 1, __builtin_object_size
(buffer, 2 > 1), "%s0m", LOG_COLORSTART)
;
601 }
602 return buffer;
603}
604
605
606/*
607 *----------------------------------------------------------------------
608 *
609 * Ns_LogSeverityEnabled --
610 *
611 * Return true if the given severity level is enabled.
612 *
613 * Results:
614 * Boolean
615 *
616 * Side effects:
617 * None.
618 *
619 *----------------------------------------------------------------------
620 */
621
622bool_Bool
623Ns_LogSeverityEnabled(Ns_LogSeverity severity)
624{
625 bool_Bool result;
626
627 if (likely(severity < severityMaxCount)(__builtin_expect((severity < severityMaxCount), 1))) {
628 result = severityConfig[severity].enabled;
629 } else {
630 result = NS_TRUE1;
631 }
632 return result;
633}
634
635
636/*
637 *----------------------------------------------------------------------
638 *
639 * Ns_LogSeveritySetEnabled --
640 *
641 * Allow from the C-API switching of enabled state on and off
642 *
643 * Results:
644 * Previous enabled state.
645 *
646 * Side effects:
647 * Upadating severityConfig
648 *
649 *----------------------------------------------------------------------
650 */
651bool_Bool
652Ns_LogSeveritySetEnabled(Ns_LogSeverity severity, bool_Bool enabled)
653{
654 bool_Bool result;
655
656 if (likely(severity < severityMaxCount)(__builtin_expect((severity < severityMaxCount), 1))) {
657 result = severityConfig[severity].enabled;
658 severityConfig[severity].enabled = enabled;
659 } else {
660 result = NS_FALSE0;
661 }
662
663 return result;
664}
665
666
667/*
668 *----------------------------------------------------------------------
669 *
670 * LogStats --
671 *
672 * Return a Tcl list containing the labels and counts for all
673 * severities. The function should be probably be guarded by a
674 * lock, but we have just single word operations and potentially
675 * incorrect counts are not fatal.
676 *
677 * Results:
678 * Tcl list
679 *
680 * Side effects:
681 * None
682 *
683 *----------------------------------------------------------------------
684 */
685static Tcl_Obj *
686LogStats(void)
687{
688 Ns_LogSeverity s;
689 Tcl_Obj *listObj;
690
691 listObj = Tcl_NewListObj(0, NULL((void*)0));
692 for (s = 0; s < severityIdx; s++) {
693 (void)Tcl_ListObjAppendElement(NULL((void*)0), listObj, Tcl_NewStringObj(severityConfig[s].label, -1));
694 (void)Tcl_ListObjAppendElement(NULL((void*)0), listObj, Tcl_NewLongObj(severityConfig[s].count));
695 }
696 return listObj;
697}
698
699
700/*
701 *----------------------------------------------------------------------
702 *
703 * Ns_Log --
704 *
705 * Send a message to the server log.
706 *
707 * Results:
708 * None.
709 *
710 * Side effects:
711 * None.
712 *
713 *----------------------------------------------------------------------
714 */
715
716void
717Ns_Log(Ns_LogSeverity severity, const char *fmt, ...)
718{
719 va_list ap;
720
721 NS_NONNULL_ASSERT(fmt != NULL)((void) (0));
722
723 va_start(ap, fmt)__builtin_va_start(ap, fmt);
724 Ns_VALog(severity, fmt, ap);
725 va_end(ap)__builtin_va_end(ap);
726}
727
728
729/*
730 *----------------------------------------------------------------------
731 *
732 * Ns_VALog --
733 *
734 * Add an entry to the log cache if the severity is not suppressed.
735 *
736 * Results:
737 * None.
738 *
739 * Side effects:
740 * None.
741 *
742 *----------------------------------------------------------------------
743 */
744
745void
746Ns_VALog(Ns_LogSeverity severity, const char *fmt, va_list apSrc)
747{
748 LogCache *cachePtr;
749
750 NS_NONNULL_ASSERT(fmt != NULL)((void) (0));
751
752 /*
753 * Skip if logging for selected severity is disabled
754 * or if severity level out of range(s).
755 */
756
757 if (Ns_LogSeverityEnabled(severity)) {
758 size_t length, offset;
759 LogEntry *entryPtr;
760 /*
761 * Track usage to provide statistics. The next line can lead
762 * potentially to data races (dirty reads).
763 */
764 severityConfig[severity].count ++;
765
766 /*
767 * Append new or reuse log entry record.
768 */
769 cachePtr = GetCache();
770 if (cachePtr->finalizing) {
771 fprintf(stderr, "Log cache is already finalized, ignore logging attempt\n")__fprintf_chk (stderr, 2 - 1, "Log cache is already finalized, ignore logging attempt\n"
)
;
772 return;
773 }
774 if (cachePtr->currentEntry != NULL((void*)0)) {
775 entryPtr = cachePtr->currentEntry->nextPtr;
776 } else {
777 entryPtr = cachePtr->firstEntry;
778 }
779 if (entryPtr == NULL((void*)0)) {
780 entryPtr = LogEntryGet(cachePtr);
781 entryPtr->nextPtr = NULL((void*)0);
782 if (cachePtr->currentEntry != NULL((void*)0)) {
783 cachePtr->currentEntry->nextPtr = entryPtr;
784 } else {
785 cachePtr->firstEntry = entryPtr;
786 }
787 }
788
789 cachePtr->currentEntry = entryPtr;
790 cachePtr->count++;
791
792 offset = (size_t)Ns_DStringLength(&cachePtr->buffer)((&cachePtr->buffer)->length);
793 Ns_DStringVPrintf(&cachePtr->buffer, fmt, apSrc);
794 length = (size_t)Ns_DStringLength(&cachePtr->buffer)((&cachePtr->buffer)->length) - offset;
795
796 entryPtr->severity = severity;
797 entryPtr->offset = offset;
798 entryPtr->length = length;
799 Ns_GetTime(&entryPtr->stamp);
800
801 /*
802 * Flush it out if not held or severity is "Fatal"
803 */
804 if (!cachePtr->hold || severity == Fatal) {
805 LogFlush(cachePtr, filters, -1, NS_TRUE1, NS_FALSE0);
806 }
807 }
808}
809
810
811/*
812 *----------------------------------------------------------------------
813 *
814 * Ns_AddLogFilter --
815 *
816 * Adds one user-given log filter.
817 *
818 * Results:
819 * None.
820 *
821 * Side effects:
822 * None.
823 *
824 *----------------------------------------------------------------------
825 */
826
827void
828Ns_AddLogFilter(Ns_LogFilter *procPtr, void *arg, Ns_FreeProc *freeProc)
829{
830 LogFilter *filterPtr = ns_calloc(1u, sizeof *filterPtr);
831
832 NS_NONNULL_ASSERT(procPtr != NULL)((void) (0));
833 NS_NONNULL_ASSERT(arg != NULL)((void) (0));
834
835 Ns_MutexLock(&lock);
836
837 if (filters != NULL((void*)0)) {
2
Assuming 'filters' is equal to NULL
3
Taking false branch
838 filters->nextPtr = filterPtr;
839 filterPtr->prevPtr = filters;
840 } else {
841 filterPtr->prevPtr = NULL((void*)0);
842 }
843
844 filterPtr->nextPtr = NULL((void*)0);
845 filters = filterPtr;
846
847 filterPtr->proc = procPtr;
848 filterPtr->arg = arg;
4
Using a fixed address is not portable because that address will probably not be valid in all environments or platforms
849 filterPtr->freeArgProc = freeProc;
850
851 Ns_MutexUnlock(&lock);
852}
853
854
855/*
856 *----------------------------------------------------------------------
857 *
858 * Ns_RemoveLogFilter --
859 *
860 * Removes user-given log filter.
861 *
862 * Results:
863 * None.
864 *
865 * Side effects:
866 * None.
867 *
868 *----------------------------------------------------------------------
869 */
870
871void
872Ns_RemoveLogFilter(Ns_LogFilter *procPtr, void *const arg)
873{
874 LogFilter *filterPtr;
875
876 NS_NONNULL_ASSERT(procPtr != NULL)((void) (0));
877 NS_NONNULL_ASSERT(arg != NULL)((void) (0));
878
879 Ns_MutexLock(&lock);
880 filterPtr = filters;
881 while(filterPtr != NULL((void*)0)) {
882 if (filterPtr->proc == procPtr && filterPtr->arg == arg) {
883 break;
884 }
885 filterPtr = filterPtr->prevPtr;
886 }
887 if (filterPtr != NULL((void*)0)) {
888 while (filterPtr->refcnt > 0) {
889 Ns_CondWait(&cond, &lock);
890 }
891 if (filterPtr->prevPtr != NULL((void*)0)) {
892 filterPtr->prevPtr->nextPtr = filterPtr->nextPtr;
893 }
894 if (filterPtr->nextPtr != NULL((void*)0)) {
895 filterPtr->nextPtr->prevPtr = filterPtr->prevPtr;
896 } else {
897 filters = filterPtr->prevPtr;
898 }
899 if (filterPtr->freeArgProc != NULL((void*)0) && filterPtr->arg != NULL((void*)0)) {
900 (*filterPtr->freeArgProc)(filterPtr->arg);
901 }
902 ns_free(filterPtr);
903 }
904 Ns_MutexUnlock(&lock);
905}
906
907
908/*
909 *----------------------------------------------------------------------
910 *
911 * Ns_Fatal, Panic --
912 *
913 * Send a message to the server log with severity level Fatal,
914 * and then exit the nsd process cleanly or abort.
915 *
916 * Results:
917 * None; does not return.
918 *
919 * Side effects:
920 * The process will exit or enter the debugger.
921 *
922 *----------------------------------------------------------------------
923 */
924
925void
926Ns_Fatal(const char *fmt, ...)
927{
928 va_list ap;
929
930 NS_NONNULL_ASSERT(fmt != NULL)((void) (0));
931
932 va_start(ap, fmt)__builtin_va_start(ap, fmt);
933 Ns_VALog(Fatal, fmt, ap);
934 va_end(ap)__builtin_va_end(ap);
935
936 if (nsconf.state.pipefd[1] != 0) {
937 /*
938 * Tell the parent process, that something went wrong.
939 */
940 if (ns_writewrite(nsconf.state.pipefd[1], "F", 1) < 1) {
941 /*
942 * In case, the write did not work, do nothing. Ignoring
943 * the result can lead to warnings due to
944 * warn_unused_result.
945 */
946 ;
947 }
948 }
949
950 _exit(1);
951}
952
953static void
954Panic(const char *fmt, ...)
955{
956 va_list ap;
957
958 va_start(ap, fmt)__builtin_va_start(ap, fmt);
959 Ns_VALog(Fatal, fmt, ap);
960 va_end(ap)__builtin_va_end(ap);
961
962 abort();
963}
964
965
966
967/*
968 *----------------------------------------------------------------------
969 *
970 * Ns_LogTime2, Ns_LogTime --
971 *
972 * Copy a local or GMT date and time string useful for common log
973 * format enties (e.g., nslog).
974 *
975 * Results:
976 * Pointer to given buffer.
977 *
978 * Side effects:
979 * Will put data into timeBuf, which must be at least 41 bytes
980 * long.
981 *
982 *----------------------------------------------------------------------
983 */
984
985char *
986Ns_LogTime(char *timeBuf)
987{
988 NS_NONNULL_ASSERT(timeBuf != NULL)((void) (0));
989
990 return Ns_LogTime2(timeBuf, NS_TRUE1);
991}
992
993char *
994Ns_LogTime2(char *timeBuf, bool_Bool gmt)
995{
996 Ns_Time now;
997 LogCache *cachePtr = GetCache();
998 const char *timeString;
999 size_t timeStringLength;
1000
1001 NS_NONNULL_ASSERT(timeBuf != NULL)((void) (0));
1002
1003 if (cachePtr->finalizing) {
1004 timeBuf[0] = 0;
1005 return timeBuf;
1006 }
1007
1008 /*
1009 * Add the log stamp
1010 */
1011 Ns_GetTime(&now);
1012 timeString = LogTime(cachePtr, &now, gmt);
1013 timeStringLength = gmt ? cachePtr->gbufSize : cachePtr->lbufSize;
1014
1015 assert(timeStringLength < 41)((void) (0));
1016 assert(timeStringLength == strlen(timeString))((void) (0));
1017
1018 return memcpy(timeBuf, timeString, timeStringLength + 1u);
1019}
1020
1021
1022/*
1023 *----------------------------------------------------------------------
1024 *
1025 * LogTime --
1026 *
1027 * Returns formatted local or gmt time from per-thread cache
1028 *
1029 * Results:
1030 * Pointer to per-thread buffer.
1031 *
1032 * Side effects:
1033 * The per-thread cache is updated with second resolution.
1034 *
1035 *----------------------------------------------------------------------
1036 */
1037
1038static char *
1039LogTime(LogCache *cachePtr, const Ns_Time *timePtr, bool_Bool gmt)
1040{
1041 time_t *tp;
1042 char *bp;
1043 size_t *sizePtr;
1044
1045 NS_NONNULL_ASSERT(cachePtr != NULL)((void) (0));
1046 NS_NONNULL_ASSERT(timePtr != NULL)((void) (0));
1047
1048 /*
1049 * Use either GMT or local time.
1050 */
1051 if (gmt) {
1052 tp = &cachePtr->gtime;
1053 bp = cachePtr->gbuf;
1054 sizePtr = &cachePtr->gbufSize;
1055 } else {
1056 tp = &cachePtr->ltime;
1057 bp = cachePtr->lbuf;
1058 sizePtr = &cachePtr->lbufSize;
1059 }
1060
1061 /*
1062 * LogTime has a granularity of seconds. For frequent updates the
1063 * print string is therefore cached. Check if the value for
1064 * seconds in the cache is the same as the required value. If not,
1065 * recompute the string and store it in the cache.
1066 */
1067 if (*tp != timePtr->sec) {
1068 size_t n;
1069 time_t secs;
1070 const struct tm *ptm;
1071
1072 *tp = timePtr->sec;
1073
1074 secs = timePtr->sec;
1075 ptm = ns_localtime(&secs);
1076
1077 n = strftime(bp, 32u, "[%d/%b/%Y:%H:%M:%S", ptm);
1078 if (!gmt) {
1079 bp[n++] = ']';
1080 bp[n] = '\0';
1081 } else {
1082 long gmtoff;
1083 char sign;
1084#ifdef HAVE_TM_GMTOFF1
1085 gmtoff = ptm->tm_gmtoff / 60;
1086#else
1087 gmtoff = -1 * (int)timezone / 60;
1088 if (daylight != 0 && ptm->tm_isdst != 0) {
1089 gmtoff += 60;
1090 }
1091#endif
1092 if (gmtoff < 0) {
1093 sign = '-';
1094 gmtoff *= -1;
1095 } else {
1096 sign = '+';
1097 }
1098 n += (size_t)snprintf(bp + n, TIME_BUFFER_SIZE - n, " %c%02ld%02ld]",__builtin___snprintf_chk (bp + n, 100u - n, 2 - 1, __builtin_object_size
(bp + n, 2 > 1), " %c%02ld%02ld]", sign, gmtoff/60, gmtoff
%60)
1099 sign, gmtoff/60, gmtoff%60)__builtin___snprintf_chk (bp + n, 100u - n, 2 - 1, __builtin_object_size
(bp + n, 2 > 1), " %c%02ld%02ld]", sign, gmtoff/60, gmtoff
%60)
;
1100 }
1101 *sizePtr = n;
1102 }
1103
1104 return bp;
1105}
1106
1107
1108/*
1109 *----------------------------------------------------------------------
1110 *
1111 * NsTclLogObjCmd --
1112 *
1113 * Implements "ns_log".
1114 *
1115 * Results:
1116 * Tcl result.
1117 *
1118 * Side effects:
1119 * See docs.
1120 *
1121 *----------------------------------------------------------------------
1122 */
1123
1124int
1125NsTclLogObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1126{
1127 void *addrPtr;
1128 int result = TCL_OK0;
1129
1130 if (objc < 3) {
1131 Tcl_WrongNumArgs(interp, 1, objv, "severity string ?string ...?");
1132 result = TCL_ERROR1;
1133 } else if (unlikely(GetSeverityFromObj(interp, objv[1], &addrPtr) != TCL_OK)(__builtin_expect((GetSeverityFromObj(interp, objv[1], &addrPtr
) != 0), 0))
) {
1134 result = TCL_ERROR1;
1135 } else {
1136 Ns_LogSeverity severity = PTR2INT(addrPtr)((int)(intptr_t)(addrPtr));
1137 Ns_DStringTcl_DString ds;
1138
1139 if (likely(objc == 3)(__builtin_expect((objc == 3), 1))) {
1140 Ns_Log(severity, "%s", Tcl_GetString(objv[2]));
1141 } else {
1142 int i;
1143
1144 Ns_DStringInitTcl_DStringInit(&ds);
1145 for (i = 2; i < objc; ++i) {
1146 Ns_DStringVarAppend(&ds, Tcl_GetString(objv[i]),
1147 i < (objc-1) ? " " : (char *)0, (char *)0L);
1148 }
1149 Ns_Log(severity, "%s", Ns_DStringValue(&ds)((&ds)->string));
1150 Ns_DStringFreeTcl_DStringFree(&ds);
1151 }
1152 }
1153 return result;
1154}
1155
1156
1157/*
1158 *----------------------------------------------------------------------
1159 *
1160 * NsLogCtlSeverityObjCmd --
1161 *
1162 * Implements "ns_logctl severtiy" command.
1163 *
1164 * Results:
1165 * Tcl result.
1166 *
1167 * Side effects:
1168 * See docs.
1169 *
1170 *----------------------------------------------------------------------
1171 */
1172
1173static int
1174NsLogCtlSeverityObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1175{
1176 Ns_LogSeverity severity = 0; /* default value for the error cases */
1177 void *addrPtr = NULL((void*)0);
1178 int result = TCL_OK0, color = -1, intensity = -1, givenEnabled = -1;
1179 Ns_ObjvSpec lopts[] = {
1180 {"-color", Ns_ObjvIndex, &color, colors},
1181 {"-intensity", Ns_ObjvIndex, &intensity, intensities},
1182 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
1183 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1184 };
1185 Ns_ObjvSpec args[] = {
1186 {"?enabled", Ns_ObjvBool, &givenEnabled, INT2PTR(NS_FALSE)((void *)(intptr_t)(0))},
1187 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1188 };
1189
1190 if (likely(objc < 3)(__builtin_expect((objc < 3), 1))) {
1191 Tcl_WrongNumArgs(interp, 2, objv, "severity-level ?-color color? ?-intensity intensity? ?bool?");
1192 result = TCL_ERROR1;
1193
1194 } else if (GetSeverityFromObj(interp, objv[2], &addrPtr) == TCL_OK0) {
1195 /*
1196 * Severity lookup was ok
1197 */
1198 severity = PTR2INT(addrPtr)((int)(intptr_t)(addrPtr));
1199
1200 } else if (objc > 3) {
1201 /*
1202 * Severity lookup failed, but more arguments are specified,
1203 * we create a new severity.
1204 */
1205 severity = Ns_CreateLogSeverity(Tcl_GetString(objv[2]));
1206 assert(severity < severityMaxCount)((void) (0));
1207
1208 } else {
1209 /*
1210 * Probably a typo when querying the severity.
1211 */
1212 result = TCL_ERROR1;
1213 }
1214
1215 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))
1216 && Ns_ParseObjv(lopts, args, interp, 2, objc-1, objv+1) != NS_OK
1217 ) {
1218 result = TCL_ERROR1;
1219 }
1220
1221 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
1222 bool_Bool enabled;
1223
1224 assert(severity < severityMaxCount)((void) (0));
1225
1226 /*
1227 * Don't allow one to deactivate Fatal.
1228 */
1229 if (givenEnabled != -1 && severity != Fatal) {
1230 enabled = severityConfig[severity].enabled;
1231 severityConfig[severity].enabled = (givenEnabled == 1);
1232 } else {
1233 enabled = Ns_LogSeverityEnabled(severity);
1234 }
1235
1236 /*
1237 * Set color attributes, when available.
1238 */
1239 if (color != -1) {
1240 severityConfig[severity].color = (LogColor)color;
1241 }
1242 if (intensity != -1) {
1243 severityConfig[severity].intensity = (LogColorIntensity)intensity;
1244 }
1245
1246 /*
1247 * Return the new enabled state.
1248 */
1249 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(enabled)Tcl_NewIntObj((enabled)!=0));
1250 }
1251
1252 return result;
1253}
1254
1255
1256/*
1257 *----------------------------------------------------------------------
1258 *
1259 * NsTclLogCtlObjCmd --
1260 *
1261 * Implements "ns_logctl". This command provides control over the
1262 * the activated severities or buffering of log messages.
1263 *
1264 * Results:
1265 * Tcl result.
1266 *
1267 * Side effects:
1268 * See docs.
1269 *
1270 *----------------------------------------------------------------------
1271 */
1272
1273int
1274NsTclLogCtlObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1275{
1276 int result = TCL_OK0, count, opt, i;
1277 Ns_DStringTcl_DString ds;
1278 Tcl_Obj *objPtr;
1279 LogCache *cachePtr = GetCache();
1280 LogFilter filter, *filterPtr = &filter;
1281 void *addr;
1282 Ns_TclCallback *cbPtr;
1283
1284 static const char *const opts[] = {
1285 "count",
1286 "flush",
1287 "get",
1288 "hold",
1289 "peek",
1290 "register",
1291 "release",
1292 "severities",
1293 "severity",
1294 "stats",
1295 "truncate",
1296 "unregister",
1297 NULL((void*)0)
1298 };
1299 enum {
1300 CCountIdx,
1301 CFlushIdx,
1302 CGetIdx,
1303 CHoldIdx,
1304 CPeekIdx,
1305 CRegisterIdx,
1306 CReleaseIdx,
1307 CSeveritiesIdx,
1308 CSeverityIdx,
1309 CStatsIdx,
1310 CTruncIdx,
1311 CUnregisterIdx
1312 };
1313
1314 if (objc < 2) {
1315 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?");
1316 result = TCL_ERROR1;
1317
1318 } else if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0,Tcl_GetIndexFromObjStruct(interp, objv[1], opts, sizeof(char *
), "option", 0, &opt)
1319 &opt)Tcl_GetIndexFromObjStruct(interp, objv[1], opts, sizeof(char *
), "option", 0, &opt)
!= TCL_OK0) {
1320 result = TCL_ERROR1;
1321
1322 } else if (cachePtr->finalizing) {
1323 /*
1324 * Silent Fail.
1325 */
1326 return TCL_OK0;
1327
1328 } else {
1329
1330 switch (opt) {
1331
1332 case CRegisterIdx:
1333 if (objc < 3) {
1334 Tcl_WrongNumArgs(interp, 2, objv, "script ?arg?");
1335 result = TCL_ERROR1;
1336 } else {
1337 cbPtr = Ns_TclNewCallback(interp, (ns_funcptr_t)Ns_TclCallbackProc,
1338 objv[2], objc - 3, objv + 3);
1339 Ns_AddLogFilter(LogToTcl, cbPtr, Ns_TclFreeCallback);
1340 Ns_TclSetAddrObj(Tcl_GetObjResult(interp), filterType, cbPtr);
1341 }
1342 break;
1343
1344 case CUnregisterIdx:
1345 if (objc != 3) {
1346 Tcl_WrongNumArgs(interp, 2, objv, "handle");
1347 result = TCL_ERROR1;
1348 } else if (Ns_TclGetAddrFromObj(interp, objv[2], filterType, &addr) != TCL_OK0) {
1349 result = TCL_ERROR1;
1350 } else {
1351 cbPtr = addr;
1352 Ns_RemoveLogFilter(LogToTcl, cbPtr);
1353 }
1354 break;
1355
1356 case CHoldIdx:
1357 cachePtr->hold = NS_TRUE1;
1358 break;
1359
1360 case CPeekIdx:
1361 case CGetIdx:
1362 memset(filterPtr, 0, sizeof(*filterPtr));
1363 filterPtr->proc = LogToDString;
1364 filterPtr->arg = &ds;
1365 Ns_DStringInitTcl_DStringInit(&ds);
1366 LogFlush(cachePtr, filterPtr, -1, (opt == CGetIdx), NS_FALSE0);
1367 Tcl_DStringResult(interp, &ds);
1368 break;
1369
1370 case CReleaseIdx:
1371 cachePtr->hold = NS_FALSE0;
1372 NS_FALL_THROUGH((void)0); /* fall through */
1373 case CFlushIdx:
1374 LogFlush(cachePtr, filters, -1, NS_TRUE1, NS_TRUE1);
1375 break;
1376
1377 case CCountIdx:
1378 Tcl_SetObjResult(interp, Tcl_NewIntObj(cachePtr->count));
1379 break;
1380
1381 case CTruncIdx:
1382 count = 0;
1383 if (objc > 2) {
1384 Ns_ObjvValueRange countRange = {0, INT_MAX2147483647};
1385 int oc = 1;
1386 Ns_ObjvSpec spec = {"?count", Ns_ObjvInt, &count, &countRange};
1387
1388 if (Ns_ObjvInt(&spec, interp, &oc, &objv[2]) != TCL_OK0) {
1389 result = TCL_ERROR1;
1390 }
1391 }
1392 if (result == TCL_OK0) {
1393 memset(filterPtr, 0, sizeof(*filterPtr));
1394 LogFlush(cachePtr, filterPtr, count, NS_TRUE1, NS_TRUE1);
1395 }
1396 break;
1397
1398 case CSeverityIdx:
1399 result = NsLogCtlSeverityObjCmd(clientData, interp, objc, objv);
1400 break;
1401
1402 case CSeveritiesIdx:
1403 /*
1404 * Return all registered severities in a list
1405 */
1406 objPtr = Tcl_GetObjResult(interp);
1407 for (i = 0; i < severityIdx; i++) {
1408 if (Tcl_ListObjAppendElement(interp, objPtr,
1409 Tcl_NewStringObj(severityConfig[i].label, -1))
1410 != TCL_OK0) {
1411 result = TCL_ERROR1;
1412 break;
1413 }
1414 }
1415 break;
1416
1417 case CStatsIdx:
1418 Tcl_SetObjResult(interp, LogStats());
1419 break;
1420
1421 default:
1422 /*
1423 * Unexpected value, raise an exception in development mode.
1424 */
1425 assert(opt && 0)((void) (0));
1426 break;
1427 }
1428 }
1429 return result;
1430}
1431
1432
1433/*
1434 *----------------------------------------------------------------------
1435 *
1436 * NsTclLogRollObjCmd --
1437 *
1438 * Implements "ns_logroll".
1439 *
1440 * Results:
1441 * Tcl result.
1442 *
1443 * Side effects:
1444 * See docs.
1445 *
1446 *----------------------------------------------------------------------
1447 */
1448
1449int
1450NsTclLogRollObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp,
1451 int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
1452{
1453 if (Ns_LogRoll() != NS_OK) {
1454 Ns_TclPrintfResult(interp, "could not roll server log");
1455 }
1456
1457 return TCL_OK0;
1458}
1459
1460
1461/*
1462 *----------------------------------------------------------------------
1463 *
1464 * Ns_LogRoll --
1465 *
1466 * Function and signal handler for SIGHUP which will roll the
1467 * system log (e.g. "error.log" or stderr). When NaviServer is
1468 * logging to stderr (when e.g. started with -f) no rolling will
1469 * be performed. The function returns potentially errors from
1470 * opening the file named by logfileName as result.
1471 *
1472 * Results:
1473 * NS_OK/NS_ERROR
1474 *
1475 * Side effects:
1476 * Will rename the log file and reopen it.
1477 *
1478 *----------------------------------------------------------------------
1479 */
1480
1481Ns_ReturnCode
1482Ns_LogRoll(void)
1483{
1484 Ns_ReturnCode status;
1485
1486 if (logfileName != NULL((void*)0) && logOpenCalled) {
1487 status = Ns_RollFileCondFmt(LogOpen, LogClose, NULL((void*)0),
1488 logfileName, rollfmt, maxbackup);
1489 } else {
1490 status = NS_OK;
1491 }
1492
1493 return status;
1494}
1495
1496
1497
1498/*
1499 *----------------------------------------------------------------------
1500 *
1501 * NsLogOpen --
1502 *
1503 * Open the log file. Adjust configurable parameters, too.
1504 *
1505 * Results:
1506 * None.
1507 *
1508 * Side effects:
1509 * Configures this module to use the newly opened log file. If
1510 * LogRoll is turned on in the configuration file, then it registers a
1511 * signal callback.
1512 *
1513 *----------------------------------------------------------------------
1514 */
1515
1516void
1517NsLogOpen(void)
1518{
1519 /*
1520 * Open the log and schedule the signal roll.
1521 */
1522
1523 if (LogOpen(NULL((void*)0)) != NS_OK) {
1524 Ns_Fatal("log: failed to open server log '%s': '%s'",
1525 logfileName, strerror(errno(*__errno_location ())));
1526 }
1527 if ((flags & LOG_ROLL0x01u) != 0u) {
1528 Ns_Callback *proc = (Ns_Callback *)(ns_funcptr_t)Ns_LogRoll;
1529 (void) Ns_RegisterAtSignal(proc, NULL((void*)0));
1530 }
1531 logOpenCalled = NS_TRUE1;
1532}
1533
1534
1535/*
1536 *----------------------------------------------------------------------
1537 *
1538 * LogOpen --
1539 *
1540 * Open the log filename specified in the global variable
1541 * 'logfileName'. If it is successfully opened, make that file the
1542 * sink for stdout and stderr too.
1543 *
1544 * Results:
1545 * NS_OK/NS_ERROR.
1546 *
1547 * Side effects:
1548 * None.
1549 *
1550 *----------------------------------------------------------------------
1551 */
1552
1553static Ns_ReturnCode
1554LogOpen(void *UNUSED(arg)UNUSED_arg __attribute__((__unused__)))
1555{
1556 int fd;
1557 Ns_ReturnCode status = NS_OK;
1558 unsigned int oflags;
1559
1560 oflags = O_WRONLY01 | O_APPEND02000 | O_CREAT0100;
1561
1562#ifdef O_LARGEFILE0
1563 oflags |= O_LARGEFILE0;
1564#endif
1565
1566 fd = ns_openopen(logfileName, (int)oflags, 0644);
1567 if (fd == NS_INVALID_FD(-1)) {
1568 Ns_Log(Error, "log: failed to re-open log file '%s': '%s'",
1569 logfileName, strerror(errno(*__errno_location ())));
1570 status = NS_ERROR;
1571 } else {
1572
1573 /*
1574 * Route stderr to the file
1575 */
1576 if (fd != STDERR_FILENO2 && ns_dup2dup2(fd, STDERR_FILENO2) == -1) {
1577 status = NS_ERROR;
1578 }
1579
1580 /*
1581 * Route stdout to the file
1582 */
1583 if (ns_dup2dup2(STDERR_FILENO2, STDOUT_FILENO1) == -1) {
1584 Ns_Log(Error, "log: failed to route stdout to file: '%s'",
1585 strerror(errno(*__errno_location ())));
1586 status = NS_ERROR;
1587 }
1588
1589 /*
1590 * Clean up dangling 'open' reference to the fd
1591 */
1592 if (fd != STDERR_FILENO2 && fd != STDOUT_FILENO1) {
1593 (void) ns_closeclose(fd);
1594 }
1595 }
1596 return status;
1597}
1598
1599
1600/*
1601 *----------------------------------------------------------------------
1602 *
1603 * LogFlush --
1604 *
1605 * Flush per-thread log cache, optionally truncating the
1606 * cache to some given count of log entries.
1607 *
1608 * Results:
1609 * None.
1610 *
1611 * Side effects:
1612 * None.
1613 *
1614 *----------------------------------------------------------------------
1615 */
1616
1617static void
1618LogFlush(LogCache *cachePtr, LogFilter *listPtr, int count, bool_Bool trunc, bool_Bool locked)
1619{
1620 int nentry = 0;
1621 LogFilter *cPtr;
1622 LogEntry *ePtr;
1623
1624 NS_NONNULL_ASSERT(cachePtr != NULL)((void) (0));
1625 NS_NONNULL_ASSERT(listPtr != NULL)((void) (0));
1626
1627 /*fprintf(stderr, "#### %s cachePtr %p locked %d count %d cachePtr->count %d hold %d\n",
1628 Ns_ThreadGetName(), (void*)cachePtr, locked, count, cachePtr->count, cachePtr->hold);*/
1629
1630 if (locked) {
1631 Ns_MutexLock(&lock);
1632 }
1633
1634 ePtr = cachePtr->firstEntry;
1635 while (ePtr != NULL((void*)0) && cachePtr->currentEntry != NULL((void*)0)) {
1636 const char *logString = Ns_DStringValue(&cachePtr->buffer)((&cachePtr->buffer)->string) + ePtr->offset;
1637
1638 /*
1639 * Since listPtr is never NULL, a repeat-unil loop is
1640 * sufficient to guarantee that the initial cPtr is not NULL
1641 * either.
1642 */
1643 cPtr = listPtr;
1644 do {
1645 if (cPtr->proc != NULL((void*)0)) {
1646 Ns_ReturnCode status;
1647
1648 if (locked) {
1649 cPtr->refcnt++;
1650 Ns_MutexUnlock(&lock);
1651 }
1652 status = (*cPtr->proc)(cPtr->arg, ePtr->severity,
1653 &ePtr->stamp, logString, ePtr->length);
1654 if (locked) {
1655 Ns_MutexLock(&lock);
1656 cPtr->refcnt--;
1657 Ns_CondBroadcast(&cond);
1658 }
1659 if (status == NS_ERROR) {
1660 /*
1661 * Callback signaled an error. Per definition we
1662 * will skip invoking other registered filters. In
1663 * such case we must assure that the current log
1664 * entry eventually gets written into some log
1665 * sink, so we use the default logfile sink.
1666 */
1667 (void) LogToFile(INT2PTR(STDERR_FILENO)((void *)(intptr_t)(2)), ePtr->severity,
1668 &ePtr->stamp, logString, ePtr->length);
1669 break;
1670 }
1671 }
1672 cPtr = cPtr->prevPtr;
1673 } while (cPtr != NULL((void*)0));
1674
1675 nentry++;
1676 if ((count > 0 && nentry >= count) || ePtr == cachePtr->currentEntry) {
1677 break;
1678 }
1679 ePtr = ePtr->nextPtr;
1680 }
1681
1682 if (trunc) {
1683 if (count > 0) {
1684 size_t length = (ePtr != NULL((void*)0)) ? (ePtr->offset + ePtr->length) : 0u;
1685 cachePtr->count = (length != 0u) ? nentry : 0;
1686 cachePtr->currentEntry = ePtr;
1687 Ns_DStringSetLengthTcl_DStringSetLength(&cachePtr->buffer, (int)length);
1688 } else {
1689 LogEntry *entryPtr, *tmpPtr;
1690
1691 /*
1692 * The cache is reset (count <= 0). If there are log
1693 * entries in the cache, first zero the pointers
1694 * to minimize free memory reads probability when
1695 * no locks are applied, then flush the memory.
1696 */
1697 entryPtr = cachePtr->firstEntry;
1698
1699 cachePtr->count = 0;
1700 cachePtr->currentEntry = NULL((void*)0);
1701 cachePtr->firstEntry = NULL((void*)0);
1702 Ns_DStringSetLengthTcl_DStringSetLength(&cachePtr->buffer, 0);
1703
1704 for (; entryPtr != NULL((void*)0); entryPtr = tmpPtr) {
1705 tmpPtr = entryPtr->nextPtr;
1706 LogEntryFree(cachePtr, entryPtr);
1707 }
1708 }
1709 }
1710
1711 if (locked) {
1712 Ns_MutexUnlock(&lock);
1713 }
1714}
1715
1716/*
1717 *----------------------------------------------------------------------
1718 *
1719 * LogClose --
1720 *
1721 * Close the logfile. Since unixes, this happens via stderr
1722 * magic, we need here just the handling for windows.
1723 *
1724 * Results:
1725 * NS_OK
1726 *
1727 * Side effects:
1728 * Closing the STDERR+STDOUT on windows.
1729 *
1730 *----------------------------------------------------------------------
1731 */
1732static Ns_ReturnCode
1733LogClose(void *UNUSED(arg)UNUSED_arg __attribute__((__unused__)))
1734{
1735 /*
1736 * Probably, LogFlush() should be done here as well, but it was
1737 * not used so far at this place.
1738 */
1739#ifdef _WIN32
1740 /* On Windows you MUST close stdout and stderr now, or
1741 Tcl_FSRenameFile() will fail with "Permission denied". */
1742 (void)ns_closeclose(STDOUT_FILENO1);
1743 (void)ns_closeclose(STDERR_FILENO2);
1744#endif
1745 return NS_OK;
1746}
1747
1748
1749
1750/*
1751 *----------------------------------------------------------------------
1752 *
1753 * LogToDString --
1754 *
1755 * Callback to write the log line to the passed dynamic string.
1756 *
1757 * Results:
1758 * Standard NS result code.
1759 *
1760 * Side effects:
1761 * None.
1762 *
1763 *----------------------------------------------------------------------
1764 */
1765
1766static Ns_ReturnCode
1767LogToDString(const void *arg, Ns_LogSeverity severity, const Ns_Time *stamp,
1768 const char *msg, size_t len)
1769{
1770 Ns_DStringTcl_DString *dsPtr = (Ns_DStringTcl_DString *)arg;
1771 LogCache *cachePtr = GetCache();
1772 char buffer[COLOR_BUFFER_SIZE255u];
1773
1774 NS_NONNULL_ASSERT(arg != NULL)((void) (0));
1775 NS_NONNULL_ASSERT(stamp != NULL)((void) (0));
1776 NS_NONNULL_ASSERT(msg != NULL)((void) (0));
1777
1778 if (cachePtr->finalizing) {
1779 /*
1780 * Silent fail.
1781 */
1782 return NS_OK;
1783 }
1784
1785 /*
1786 * In case colorization was configured, add the necessary escape
1787 * sequences.
1788 */
1789 if ((flags & LOG_COLORIZE0x40u) != 0u) {
1790 Ns_DStringPrintf(dsPtr, "%s%d;%dm", LOG_COLORSTART, prefixIntensity, prefixColor);
1791 }
1792
1793 if ((flags & LOG_SEC0x04u) != 0u) {
1794 const char *timeString;
1795 size_t timeStringLength;
1796
1797 /*
1798 * Add the log stamp
1799 */
1800 timeString = LogTime(cachePtr, stamp, NS_FALSE0);
1801 timeStringLength = cachePtr->lbufSize;
1802 Ns_DStringNAppendTcl_DStringAppend(dsPtr, timeString, (int)timeStringLength);
1803 }
1804
1805 if ((flags & LOG_USEC0x08u) != 0u) {
1806 Ns_DStringSetLengthTcl_DStringSetLength(dsPtr, Ns_DStringLength(dsPtr)((dsPtr)->length) - 1);
1807 Ns_DStringPrintf(dsPtr, ".%06ld]", stamp->usec);
1808 }
1809
1810 if ((flags & LOG_USEC_DIFF0x10u) != 0u) {
1811 Ns_Time now;
1812 static Ns_Time last = {0, 0};
1813
1814 Ns_GetTime(&now);
1815 /*
1816 * Initialize if needed.
1817 */
1818 if (last.sec == 0) {
1819 last.sec = now.sec;
1820 last.usec = now.usec;
1821 }
1822 /*
1823 * Skip last char.
1824 */
1825 Ns_DStringSetLengthTcl_DStringSetLength(dsPtr, Ns_DStringLength(dsPtr)((dsPtr)->length) - 1);
1826 /*
1827 * Handle change in seconds.
1828 */
1829 if (last.sec < now.sec) {
1830 last.sec = now.sec;
1831 Ns_DStringPrintf(dsPtr, "-%.6ld]", now.usec + (1000000 - last.usec));
1832 } else {
1833 Ns_DStringPrintf(dsPtr, "-%.6ld]", now.usec-last.usec);
1834 }
1835 last.usec = now.usec;
1836 }
1837 if ((flags & LOG_THREAD0x20u) != 0u) {
1838 Ns_DStringPrintf(dsPtr, "[%d.%" PRIxPTR"l" "x" "]", (int)Ns_InfoPid(), Ns_ThreadId());
1839 }
1840 if ((flags & LOG_COLORIZE0x40u) != 0u) {
1841 Ns_DStringPrintf(dsPtr, "[%s] %s%s%s: ",
1842 Ns_ThreadGetName(),
1843 (const char *)LOG_COLOREND,
1844 LogSeverityColor(buffer, severity),
1845 Ns_LogSeverityName(severity));
1846 } else {
1847 Ns_DStringPrintf(dsPtr, "[%s] %s: ",
1848 Ns_ThreadGetName(),
1849 Ns_LogSeverityName(severity));
1850 }
1851
1852 if ((flags & LOG_EXPAND0x02u) != 0u) {
1853 Ns_DStringNAppendTcl_DStringAppend(dsPtr, "\n ", 5);
1854 }
1855
1856 /*
1857 * Add the log message
1858 */
1859
1860 if (len == 0u) {
1861 len = strlen(msg);
1862 }
1863 if (nsconf.sanitize_logfiles > 0) {
1864 Ns_DStringAppendPrintable(dsPtr, nsconf.sanitize_logfiles == 2, msg, len);
1865 } else {
1866 Ns_DStringNAppendTcl_DStringAppend(dsPtr, msg, (int)len);
1867 }
1868 if ((flags & LOG_COLORIZE0x40u) != 0u) {
1869 Ns_DStringNAppendTcl_DStringAppend(dsPtr, (const char *)LOG_COLOREND, 4);
1870 }
1871 Ns_DStringNAppendTcl_DStringAppend(dsPtr, "\n", 1);
1872 if ((flags & LOG_EXPAND0x02u) != 0u) {
1873 Ns_DStringNAppendTcl_DStringAppend(dsPtr, "\n", 1);
1874 }
1875
1876 return NS_OK;
1877}
1878
1879
1880/*
1881 *----------------------------------------------------------------------
1882 *
1883 * LogToFile --
1884 *
1885 * Callback to write the log line to the passed file descriptor.
1886 *
1887 * Results:
1888 * Returns always NS_OK
1889 *
1890 * Side effects:
1891 * None.
1892 *
1893 *----------------------------------------------------------------------
1894 */
1895
1896static Ns_ReturnCode
1897LogToFile(const void *arg, Ns_LogSeverity severity, const Ns_Time *stamp,
1898 const char *msg, size_t len)
1899{
1900 int fd = PTR2INT(arg)((int)(intptr_t)(arg));
1901 Ns_DStringTcl_DString ds;
1902
1903 NS_NONNULL_ASSERT(arg != NULL)((void) (0));
1904 NS_NONNULL_ASSERT(stamp != NULL)((void) (0));
1905 NS_NONNULL_ASSERT(msg != NULL)((void) (0));
1906
1907 Ns_DStringInitTcl_DStringInit(&ds);
1908
1909 (void) LogToDString(&ds, severity, stamp, msg, len);
1910 (void) NsAsyncWrite(fd, Ns_DStringValue(&ds)((&ds)->string), (size_t)Ns_DStringLength(&ds)((&ds)->length));
1911
1912 Ns_DStringFreeTcl_DStringFree(&ds);
1913 return NS_OK;
1914}
1915
1916
1917/*
1918 *----------------------------------------------------------------------
1919 *
1920 * LogToTcl --
1921 *
1922 * Callback to pass the log information to Tcl.
1923 *
1924 * This function may return NS_ERROR in which case the
1925 * caller should skip invoking other filters.
1926 * In such case, a log-entry with the error message is
1927 * produced in the default log sink (log file).
1928 * For all other cases, caller should continue with the
1929 * next registered callback.
1930 *
1931 * Results:
1932 * Standard NS result code.
1933 *
1934 * Side effects:
1935 * This call deliberately does not use Ns_TclEvalCallback(), as
1936 * if the Tcl code throws error, that one will invoke
1937 * Ns_TclLogError() and will deadlock in the log code.
1938 *
1939 *----------------------------------------------------------------------
1940 */
1941
1942static Ns_ReturnCode
1943LogToTcl(const void *arg, Ns_LogSeverity severity, const Ns_Time *stamp,
1944 const char *msg, size_t len)
1945{
1946 Ns_ReturnCode status;
1947
1948 NS_NONNULL_ASSERT(arg != NULL)((void) (0));
1949 NS_NONNULL_ASSERT(stamp != NULL)((void) (0));
1950 NS_NONNULL_ASSERT(msg != NULL)((void) (0));
1951
1952 if (severity == Fatal) {
1953 /*
1954 * In case of Fatal severity, do nothing. We have to assume
1955 * that Tcl is not functioning anymore.
1956 */
1957 status = NS_OK;
1958
1959 } else {
1960 int ii, ret;
1961 void *logfile = INT2PTR(STDERR_FILENO)((void *)(intptr_t)(2));
1962 Tcl_Obj *stampObj;
1963 Ns_DStringTcl_DString ds, ds2;
1964 Tcl_Interp *interp;
1965 const Ns_TclCallback *cbPtr = (Ns_TclCallback *)arg;
1966
1967 /*
1968 * Try to obtain an interpreter:
1969 */
1970 interp = Ns_TclAllocateInterp(cbPtr->server);
1971 if (interp == NULL((void*)0)) {
1972 (void)LogToFile(logfile, Error, stamp,
1973 "LogToTcl: can't get interpreter", 0u);
1974 status = NS_ERROR;
1975 } else {
1976
1977 Ns_DStringInitTcl_DStringInit(&ds);
1978 stampObj = Tcl_NewObj();
1979 Ns_TclSetTimeObj(stampObj, stamp);
1980
1981 /*
1982 * Construct args for passing to the callback script:
1983 *
1984 * callback severity timestamp log ?arg...?
1985 *
1986 * The script may contain blanks therefore append as regular
1987 * string instead of as list element. Other arguments are
1988 * appended to it as elements.
1989 */
1990 Ns_DStringVarAppend(&ds, cbPtr->script, " ", Ns_LogSeverityName(severity), (char *)0L);
1991 Ns_DStringAppendElementTcl_DStringAppendElement(&ds, Tcl_GetString(stampObj));
1992 Tcl_DecrRefCount(stampObj)do { Tcl_Obj *_objPtr = (stampObj); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
1993
1994 /*
1995 * Append n bytes of msg as proper list element to ds. Since
1996 * Tcl_DStringAppendElement has no length parameter, we have
1997 * to use a temporary DString here.
1998 */
1999 Ns_DStringInitTcl_DStringInit(&ds2);
2000 Ns_DStringNAppendTcl_DStringAppend(&ds2, msg, (int)len);
2001 Ns_DStringAppendElementTcl_DStringAppendElement(&ds, ds2.string);
2002 Ns_DStringFreeTcl_DStringFree(&ds2);
2003
2004 for (ii = 0; ii < cbPtr->argc; ii++) {
2005 Ns_DStringAppendElementTcl_DStringAppendElement(&ds, cbPtr->argv[ii]);
2006 }
2007 ret = Tcl_EvalEx(interp, Ns_DStringValue(&ds)((&ds)->string), Ns_DStringLength(&ds)((&ds)->length), 0);
2008 if (ret == TCL_ERROR1) {
2009
2010 /*
2011 * Error in Tcl callback is always logged to file.
2012 */
2013 Ns_DStringSetLengthTcl_DStringSetLength(&ds, 0);
2014 Ns_DStringAppend(&ds, "LogToTcl: ")Tcl_DStringAppend((&ds), ("LogToTcl: "), -1);
2015 Ns_DStringAppend(&ds, Tcl_GetStringResult(interp))Tcl_DStringAppend((&ds), (Tcl_GetStringResult(interp)), -
1)
;
2016 (void)LogToFile(logfile, Error, stamp, Ns_DStringValue(&ds)((&ds)->string),
2017 (size_t)Ns_DStringLength(&ds)((&ds)->length));
2018 }
2019 Ns_DStringFreeTcl_DStringFree(&ds);
2020 Ns_TclDeAllocateInterp(interp);
2021
2022 status = (ret == TCL_ERROR1) ? NS_ERROR: NS_OK;
2023 }
2024 }
2025 return status;
2026}
2027
2028
2029/*
2030 *----------------------------------------------------------------------
2031 *
2032 * GetCache --
2033 *
2034 * Get the per-thread LogCache struct.
2035 *
2036 * Results:
2037 * Pointer to per-thread struct.
2038 *
2039 * Side effects:
2040 * Memory for struct is allocated on first call.
2041 *
2042 *----------------------------------------------------------------------
2043 */
2044
2045static LogCache *
2046GetCache(void)
2047{
2048#if defined(NS_THREAD_LOCAL__thread)
2049 static NS_THREAD_LOCAL__thread LogCache *cachePtr = NULL((void*)0);
2050
2051 if (cachePtr == NULL((void*)0)) {
2052 cachePtr = ns_calloc(1u, sizeof(LogCache));
2053 Ns_DStringInitTcl_DStringInit(&cachePtr->buffer);
2054 Ns_TlsSet(&tls, cachePtr);
2055 }
2056#else
2057 LogCache *cachePtr;
2058
2059 cachePtr = Ns_TlsGet(&tls);
2060 if (cachePtr == NULL((void*)0)) {
2061 cachePtr = ns_calloc(1u, sizeof(LogCache));
2062 Ns_DStringInitTcl_DStringInit(&cachePtr->buffer);
2063 Ns_TlsSet(&tls, cachePtr);
2064 }
2065#endif
2066 return cachePtr;
2067}
2068
2069#if !defined(NS_THREAD_LOCAL__thread)
2070static void LogEntriesFree(void *arg)
2071{
2072 LogEntry *logEntry = (LogEntry *)arg;
2073 fprintf(stderr, "LOGENTRY: free list %p\n", (void*)logEntry)__fprintf_chk (stderr, 2 - 1, "LOGENTRY: free list %p\n", (void
*)logEntry)
;
2074 ns_free(logEntry);
2075}
2076#endif
2077
2078static LogEntry *
2079LogEntryGet(LogCache *cachePtr)
2080{
2081 LogEntry *entryPtr;
2082
2083 if (cachePtr->hold) {
2084 entryPtr = ns_malloc(sizeof(LogEntry));
2085 } else {
2086#if defined(NS_THREAD_LOCAL__thread)
2087 static NS_THREAD_LOCAL__thread LogEntry logEntry = {0};
2088 entryPtr = &logEntry;
2089#else
2090 LogCache *logEntryList;
2091
2092 logEntryList = Ns_TlsGet(&tlsEntry);
2093 if (logEntryList == NULL((void*)0)) {
2094 logEntryList = ns_calloc(1u, sizeof(LogEntry));
2095 entryPtr = logEntryList;
2096 Ns_TlsSet(&tlsEntry, logEntryList);
2097 } else {
2098 entryPtr = logEntryList;
2099 logEntryList = entryPtr->nextPtr;
2100 entryPtr->nextPtr = NULL((void*)0);
2101 }
2102#endif
2103 }
2104 return entryPtr;
2105}
2106
2107static void
2108LogEntryFree(LogCache *cachePtr, LogEntry *logEntryPtr)
2109{
2110 if (cachePtr->hold) {
2111 memset(logEntryPtr, 0, sizeof(LogEntry));
2112 ns_free(logEntryPtr);
2113 } else {
2114#if defined(NS_THREAD_LOCAL__thread)
2115#else
2116 logEntryList = Ns_TlsGet(&tlsEntry);
2117 logEntryPtr->nextPtr = logEntryList;
2118 if (logEntryList != NULL((void*)0)) {
2119 logEntryList->nextPtr = logEntryPtr;
2120 }
2121#endif
2122 /*fprintf(stderr, "LOGENTRY: pushed\n");*/
2123 }
2124}
2125
2126
2127/*
2128 *----------------------------------------------------------------------
2129 *
2130 * FreeCache --
2131 *
2132 * TLS cleanup callback to destroy per-thread Cache struct.
2133 *
2134 * Results:
2135 * None.
2136 *
2137 * Side effects:
2138 * None.
2139 *
2140 *----------------------------------------------------------------------
2141 */
2142
2143static void
2144FreeCache(void *arg)
2145{
2146 LogCache *cachePtr = (LogCache *)arg;
2147
2148 if (!cachePtr->finalizing) {
2149
2150 cachePtr->finalizing = NS_TRUE1;
2151
2152 LogFlush(cachePtr, filters, -1, NS_TRUE1, NS_TRUE1);
2153
2154 Ns_DStringFreeTcl_DStringFree(&cachePtr->buffer);
2155 ns_free(cachePtr);
2156 }
2157}
2158
2159
2160/*
2161 *----------------------------------------------------------------------
2162 *
2163 * GetSeverityFromObj --
2164 *
2165 * Get the severity level from the Tcl_Obj, possibly setting
2166 * its internal representation.
2167 *
2168 * Results:
2169 * TCL_OK or TCL_ERROR.
2170 *
2171 * Side effects:
2172 * Error message left in Tcl interp on error.
2173 *
2174 *----------------------------------------------------------------------
2175 */
2176
2177static int
2178GetSeverityFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, void **addrPtrPtr)
2179{
2180 int result = TCL_OK0;
2181
2182 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
2183 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
2184 NS_NONNULL_ASSERT(addrPtrPtr != NULL)((void) (0));
2185
2186 if (Ns_TclGetOpaqueFromObj(objPtr, severityType, addrPtrPtr) != TCL_OK0) {
2187 const Tcl_HashEntry *hPtr;
2188
2189 Ns_MutexLock(&lock);
2190 hPtr = Tcl_FindHashEntry(&severityTable, Tcl_GetString(objPtr))(*((&severityTable)->findProc))(&severityTable, (const
char *)(Tcl_GetString(objPtr)))
;
2191 Ns_MutexUnlock(&lock);
2192
2193 if (hPtr != NULL((void*)0)) {
2194 *addrPtrPtr = Tcl_GetHashValue(hPtr)((hPtr)->clientData);
2195 } else {
2196 int i;
2197 /*
2198 * Check for a legacy integer severity.
2199 */
2200 if (Tcl_GetIntFromObj(NULL((void*)0), objPtr, &i) == TCL_OK0
2201 && i < severityMaxCount) {
2202 *addrPtrPtr = INT2PTR(i)((void *)(intptr_t)(i));
2203 } else {
2204 Tcl_DString ds;
2205
2206 Tcl_DStringInit(&ds);
2207 Ns_DStringPrintf(&ds, "unknown severity: \"%s\":"
2208 " should be one of: ", Tcl_GetString(objPtr));
2209 for (i = 0; i < severityIdx; i++) {
2210 Ns_DStringAppend(&ds, severityConfig[i].label)Tcl_DStringAppend((&ds), (severityConfig[i].label), -1);
2211 Ns_DStringNAppendTcl_DStringAppend(&ds, " ", 1);
2212 }
2213 Tcl_DStringResult(interp, &ds);
2214 result = TCL_ERROR1;
2215 }
2216 }
2217 if (result == TCL_OK0) {
2218 /*
2219 * Stash the severity for future speedy lookup.
2220 */
2221 Ns_TclSetOpaqueObj(objPtr, severityType, *addrPtrPtr);
2222 }
2223 }
2224
2225 return result;
2226}
2227
2228
2229/*
2230 *----------------------------------------------------------------------
2231 *
2232 * Ns_SetLogFlushProc, Ns_SetNsLogProc --
2233 *
2234 * Deprecated (and disabled) calls.
2235 *
2236 * Results:
2237 * None.
2238 *
2239 * Side effects:
2240 * Will exit the server.
2241 *
2242 *----------------------------------------------------------------------
2243 */
2244
2245void
2246Ns_SetLogFlushProc(Ns_LogFlushProc *UNUSED(procPtr)UNUSED_procPtr __attribute__((__unused__)))
2247{
2248 Ns_Fatal("Ns_SetLogFlushProc: deprecated, use Ns_AddLogFilter() instead");
2249}
2250
2251void
2252Ns_SetNsLogProc(Ns_LogProc *UNUSED(procPtr)UNUSED_procPtr __attribute__((__unused__)))
2253{
2254 Ns_Fatal("Ns_SetNsLogProc: deprecated, use Ns_AddLogFilter() instead");
2255}
2256
2257/*
2258 * Local Variables:
2259 * mode: c
2260 * c-basic-offset: 4
2261 * fill-column: 70
2262 * indent-tabs-mode: nil
2263 * End:
2264 */