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 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
60 | typedef 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 | ||||
75 | typedef 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 | ||||
90 | typedef 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 | ||||
105 | static LogEntry *LogEntryGet(LogCache *cachePtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); | |||
106 | static 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) | |||
109 | static void LogEntriesFree(void *arg); | |||
110 | #endif | |||
111 | ||||
112 | /* | |||
113 | * Local functions defined in this file | |||
114 | */ | |||
115 | ||||
116 | static Ns_TlsCleanup FreeCache; | |||
117 | static Tcl_PanicProc Panic; | |||
118 | ||||
119 | static Ns_LogFilter LogToFile; | |||
120 | static Ns_LogFilter LogToTcl; | |||
121 | static Ns_LogFilter LogToDString; | |||
122 | ||||
123 | static Tcl_ObjCmdProc NsLogCtlSeverityObjCmd; | |||
124 | ||||
125 | static LogCache* GetCache(void) | |||
126 | NS_GNUC_RETURNS_NONNULL; | |||
127 | ||||
128 | static 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 | ||||
132 | static 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 | ||||
136 | static Ns_LogCallbackProc LogOpen; | |||
137 | static Ns_LogCallbackProc LogClose; | |||
138 | ||||
139 | static 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 | ||||
142 | static Tcl_Obj *LogStats(void); | |||
143 | ||||
144 | static char *LogSeverityColor(char *buffer, Ns_LogSeverity severity) | |||
145 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); | |||
146 | ||||
147 | static 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 | ||||
156 | static Ns_Tls tls; | |||
157 | #if !defined(NS_THREAD_LOCAL__thread) | |||
158 | static Ns_Tls tlsEntry; | |||
159 | #endif | |||
160 | static Ns_Mutex lock; | |||
161 | static Ns_Cond cond; | |||
162 | ||||
163 | static bool_Bool logOpenCalled = NS_FALSE0; | |||
164 | static const char *logfileName = NULL((void*)0); | |||
165 | static const char *rollfmt = NULL((void*)0); | |||
166 | static unsigned int flags = 0u; | |||
167 | static int maxbackup; | |||
168 | ||||
169 | static LogFilter *filters; | |||
170 | static const char *const filterType = "ns:logfilter"; | |||
171 | static const char *const severityType = "ns:logseverity"; | |||
172 | ||||
173 | static const unsigned char LOG_COLOREND[] = { 0x1bu, UCHAR('[')((unsigned char)('[')), UCHAR('0')((unsigned char)('0')), UCHAR('m')((unsigned char)('m')), 0u }; | |||
174 | static const unsigned char LOG_COLORSTART[] = { 0x1bu, UCHAR('[')((unsigned char)('[')), 0u }; | |||
175 | ||||
176 | typedef 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 | ||||
188 | typedef enum { | |||
189 | COLOR_NORMAL = 0u, | |||
190 | COLOR_BRIGHT = 1u | |||
191 | } LogColorIntensity; | |||
192 | ||||
193 | static LogColor prefixColor = COLOR_GREEN; | |||
194 | static LogColorIntensity prefixIntensity = COLOR_NORMAL; | |||
195 | ||||
196 | static 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 | ||||
209 | static 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 | ||||
223 | static 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 | ||||
239 | static const Ns_LogSeverity severityMaxCount = (Ns_LogSeverity)(sizeof(severityConfig) / sizeof(severityConfig[0])); | |||
240 | static Ns_LogSeverity severityIdx = 0; | |||
241 | ||||
242 | static 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 | ||||
262 | void | |||
263 | NsInitLog(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)); | |||
| ||||
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 | ||||
329 | static int | |||
330 | ObjvTableLookup(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 | ||||
394 | void | |||
395 | NsConfigLog(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 | ||||
480 | const char * | |||
481 | Ns_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 | ||||
504 | Ns_LogSeverity | |||
505 | Ns_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 | ||||
562 | const char * | |||
563 | Ns_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 | ||||
592 | static char * | |||
593 | LogSeverityColor(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 | ||||
622 | bool_Bool | |||
623 | Ns_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 | */ | |||
651 | bool_Bool | |||
652 | Ns_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 | */ | |||
685 | static Tcl_Obj * | |||
686 | LogStats(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 | ||||
716 | void | |||
717 | Ns_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 | ||||
745 | void | |||
746 | Ns_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 | ||||
827 | void | |||
828 | Ns_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)) { | |||
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; | |||
| ||||
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 | ||||
871 | void | |||
872 | Ns_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 | ||||
925 | void | |||
926 | Ns_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 | ||||
953 | static void | |||
954 | Panic(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 | ||||
985 | char * | |||
986 | Ns_LogTime(char *timeBuf) | |||
987 | { | |||
988 | NS_NONNULL_ASSERT(timeBuf != NULL)((void) (0)); | |||
989 | ||||
990 | return Ns_LogTime2(timeBuf, NS_TRUE1); | |||
991 | } | |||
992 | ||||
993 | char * | |||
994 | Ns_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 | ||||
1038 | static char * | |||
1039 | LogTime(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 | ||||
1124 | int | |||
1125 | NsTclLogObjCmd(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 | ||||
1173 | static int | |||
1174 | NsLogCtlSeverityObjCmd(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 | ||||
1273 | int | |||
1274 | NsTclLogCtlObjCmd(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 | ||||
1449 | int | |||
1450 | NsTclLogRollObjCmd(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 | ||||
1481 | Ns_ReturnCode | |||
1482 | Ns_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 | ||||
1516 | void | |||
1517 | NsLogOpen(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 | ||||
1553 | static Ns_ReturnCode | |||
1554 | LogOpen(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 | ||||
1617 | static void | |||
1618 | LogFlush(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 | */ | |||
1732 | static Ns_ReturnCode | |||
1733 | LogClose(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 | ||||
1766 | static Ns_ReturnCode | |||
1767 | LogToDString(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 | ||||
1896 | static Ns_ReturnCode | |||
1897 | LogToFile(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 | ||||
1942 | static Ns_ReturnCode | |||
1943 | LogToTcl(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 | ||||
2045 | static LogCache * | |||
2046 | GetCache(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) | |||
2070 | static 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 | ||||
2078 | static LogEntry * | |||
2079 | LogEntryGet(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 | ||||
2107 | static void | |||
2108 | LogEntryFree(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 | ||||
2143 | static void | |||
2144 | FreeCache(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 | ||||
2177 | static int | |||
2178 | GetSeverityFromObj(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 | ||||
2245 | void | |||
2246 | Ns_SetLogFlushProc(Ns_LogFlushProc *UNUSED(procPtr)UNUSED_procPtr __attribute__((__unused__))) | |||
2247 | { | |||
2248 | Ns_Fatal("Ns_SetLogFlushProc: deprecated, use Ns_AddLogFilter() instead"); | |||
2249 | } | |||
2250 | ||||
2251 | void | |||
2252 | Ns_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 | */ |