File: | log/nslog.c |
Warning: | line 994, column 9 Duplicate code detected |
Note: | line 996, column 9 Similar code here |
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://www.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 | * nslog.c -- |
33 | * |
34 | * Implements access logging in the NCSA Common Log format. |
35 | * |
36 | */ |
37 | |
38 | #include "ns.h" |
39 | #include <ctype.h> /* isspace */ |
40 | |
41 | #define LOG_COMBINED0x01u 0x01u |
42 | #define LOG_FMTTIME0x02u 0x02u |
43 | #define LOG_REQTIME0x04u 0x04u |
44 | #define LOG_PARTIALTIMES0x08u 0x08u |
45 | #define LOG_CHECKFORPROXY0x10u 0x10u |
46 | #define LOG_SUPPRESSQUERY0x20u 0x20u |
47 | #define LOG_THREADNAME0x40u 0x40u |
48 | #define LOG_MASKIP0x80u 0x80u |
49 | |
50 | #if !defined(PIPE_BUF4096) |
51 | # define PIPE_BUF4096 512 |
52 | #endif |
53 | |
54 | NS_EXTERNextern __attribute__ ((visibility ("default"))) const int Ns_ModuleVersion; |
55 | NS_EXPORT__attribute__ ((visibility ("default"))) const int Ns_ModuleVersion = 1; |
56 | |
57 | |
58 | typedef struct { |
59 | Ns_Mutex lock; |
60 | const char *module; |
61 | const char *filename; |
62 | const char *rollfmt; |
63 | const char *extendedHeaders; |
64 | const char **requestHeaders; |
65 | const char **responseHeaders; |
66 | const char *driverPattern; |
67 | int nrRequestHeaders; |
68 | int nrResponseHeaders; |
69 | int fd; |
70 | unsigned int flags; |
71 | int maxbackup; |
72 | int maxlines; |
73 | int curlines; |
74 | struct NS_SOCKADDR_STORAGEsockaddr_storage ipv4maskStruct; |
75 | struct sockaddr *ipv4maskPtr; |
76 | #ifdef HAVE_IPV61 |
77 | struct NS_SOCKADDR_STORAGEsockaddr_storage ipv6maskStruct; |
78 | struct sockaddr *ipv6maskPtr; |
79 | #endif |
80 | Tcl_DString buffer; |
81 | } Log; |
82 | |
83 | /* |
84 | * Local functions defined in this file |
85 | */ |
86 | |
87 | static Ns_SchedProc LogRollCallback; |
88 | static Ns_ShutdownProc LogCloseCallback; |
89 | static Ns_TraceProc LogTrace; |
90 | static Ns_ArgProc LogArg; |
91 | static Ns_TclTraceProc AddCmds; |
92 | static Tcl_ObjCmdProc LogObjCmd; |
93 | |
94 | NS_EXPORT__attribute__ ((visibility ("default"))) Ns_ModuleInitProc Ns_ModuleInit; |
95 | |
96 | static Ns_ReturnCode LogFlush(Log *logPtr, Tcl_DString *dsPtr); |
97 | static Ns_LogCallbackProc LogOpen; |
98 | static Ns_LogCallbackProc LogClose; |
99 | static Ns_LogCallbackProc LogRoll; |
100 | |
101 | static void AppendEscaped(Tcl_DString *dsPtr, const char *toProcess) |
102 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))); |
103 | |
104 | static Ns_ReturnCode ParseExtendedHeaders(Log *logPtr, const char *str) |
105 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); |
106 | static void |
107 | AppendExtHeaders(Tcl_DString *dsPtr, const char **argv, const Ns_Set *set) |
108 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); |
109 | |
110 | |
111 | /* |
112 | *---------------------------------------------------------------------- |
113 | * |
114 | * Ns_ModuleInit -- |
115 | * |
116 | * Module initialization routine. |
117 | * |
118 | * Results: |
119 | * NS_OK. |
120 | * |
121 | * Side effects: |
122 | * Log file is opened, trace routine is registered, and, if |
123 | * configured, log file roll signal and scheduled procedures |
124 | * are registered. |
125 | * |
126 | *---------------------------------------------------------------------- |
127 | */ |
128 | |
129 | NS_EXPORT__attribute__ ((visibility ("default"))) Ns_ReturnCode |
130 | Ns_ModuleInit(const char *server, const char *module) |
131 | { |
132 | const char *path, *file; |
133 | Log *logPtr; |
134 | Tcl_DString ds; |
135 | static bool_Bool first = NS_TRUE1; |
136 | Ns_ReturnCode result; |
137 | |
138 | NS_NONNULL_ASSERT(module != NULL)((void) (0)); |
139 | |
140 | /* |
141 | * Sanity check to provide a meaningful error instead of a |
142 | * crash. Currently, we do not allow one to register this module globally. |
143 | */ |
144 | if (server == NULL((void*)0)) { |
145 | Ns_Fatal("Module %s: requires a concrete server (cannot be used as a global module)", |
146 | module); |
147 | } |
148 | |
149 | /* |
150 | * Register the info callbacks just once. This assumes we are |
151 | * called w/o locking from within the server startup. |
152 | */ |
153 | |
154 | if (first) { |
155 | first = NS_FALSE0; |
156 | Ns_RegisterProcInfo((ns_funcptr_t)LogRollCallback, "nslog:roll", LogArg); |
157 | Ns_RegisterProcInfo((ns_funcptr_t)LogCloseCallback, "nslog:close", LogArg); |
158 | Ns_RegisterProcInfo((ns_funcptr_t)LogTrace, "nslog:conntrace", LogArg); |
159 | Ns_RegisterProcInfo((ns_funcptr_t)AddCmds, "nslog:initinterp", LogArg); |
160 | } |
161 | |
162 | Tcl_DStringInit(&ds); |
163 | |
164 | logPtr = ns_calloc(1u, sizeof(Log)); |
165 | logPtr->module = module; |
166 | logPtr->fd = NS_INVALID_FD(-1); |
167 | Ns_MutexInit(&logPtr->lock); |
168 | Ns_MutexSetName2(&logPtr->lock, "nslog", server); |
169 | Tcl_DStringInit(&logPtr->buffer); |
170 | |
171 | path = Ns_ConfigSectionPath(NULL((void*)0), server, module, (char *)0L); |
172 | |
173 | /* |
174 | * Determine the name of the log file |
175 | */ |
176 | |
177 | file = Ns_ConfigString(path, "file", "access.log"); |
178 | if (Ns_PathIsAbsolute(file) == NS_TRUE1) { |
179 | logPtr->filename = ns_strdup(file); |
180 | } else { |
181 | /* |
182 | * If log file is not given in absolute format, it is expected to |
183 | * exist in the global logs directory if such exists or module |
184 | * specific directory, which is created if necessary. |
185 | */ |
186 | |
187 | if (Ns_HomePathExists("logs", (char *)0L)) { |
188 | (void) Ns_HomePath(&ds, "logs", "/", file, (char *)0L); |
189 | } else { |
190 | Tcl_Obj *dirpath; |
191 | int rc; |
192 | |
193 | Tcl_DStringSetLength(&ds, 0); |
194 | (void) Ns_ModulePath(&ds, server, module, (char *)0L); |
195 | dirpath = Tcl_NewStringObj(ds.string, -1); |
196 | Tcl_IncrRefCount(dirpath)++(dirpath)->refCount; |
197 | rc = Tcl_FSCreateDirectory(dirpath); |
198 | Tcl_DecrRefCount(dirpath)do { Tcl_Obj *_objPtr = (dirpath); if (_objPtr->refCount-- <= 1) { TclFreeObj(_objPtr); } } while(0); |
199 | if (rc != TCL_OK0 && Tcl_GetErrno() != EEXIST17 && Tcl_GetErrno() != EISDIR21) { |
200 | Ns_Log(Error, "nslog: create directory (%s) failed: '%s'", |
201 | ds.string, strerror(Tcl_GetErrno())); |
202 | Tcl_DStringFree(&ds); |
203 | return NS_ERROR; |
204 | } |
205 | Tcl_DStringSetLength(&ds, 0); |
206 | (void) Ns_ModulePath(&ds, server, module, file, (char *)0L); |
207 | } |
208 | logPtr->filename = Ns_DStringExport(&ds); |
209 | } |
210 | |
211 | /* |
212 | * Get other parameters from configuration file |
213 | */ |
214 | |
215 | logPtr->rollfmt = ns_strcopy(Ns_ConfigGetValue(path, "rollfmt")); |
216 | logPtr->maxbackup = Ns_ConfigIntRange(path, "maxbackup", 100, 1, INT_MAX2147483647); |
217 | logPtr->maxlines = Ns_ConfigIntRange(path, "maxbuffer", 0, 0, INT_MAX2147483647); |
218 | if (Ns_ConfigBool(path, "formattedtime", NS_TRUE1)) { |
219 | logPtr->flags |= LOG_FMTTIME0x02u; |
220 | } |
221 | if (Ns_ConfigBool(path, "logcombined", NS_TRUE1)) { |
222 | logPtr->flags |= LOG_COMBINED0x01u; |
223 | } |
224 | if (Ns_ConfigBool(path, "logreqtime", NS_FALSE0)) { |
225 | logPtr->flags |= LOG_REQTIME0x04u; |
226 | } |
227 | if (Ns_ConfigBool(path, "logpartialtimes", NS_FALSE0)) { |
228 | logPtr->flags |= LOG_PARTIALTIMES0x08u; |
229 | } |
230 | if (Ns_ConfigBool(path, "logthreadname", NS_FALSE0)) { |
231 | logPtr->flags |= LOG_THREADNAME0x40u; |
232 | } |
233 | if (Ns_ConfigBool(path, "suppressquery", NS_FALSE0)) { |
234 | logPtr->flags |= LOG_SUPPRESSQUERY0x20u; |
235 | } |
236 | if (Ns_ConfigBool(path, "checkforproxy", NS_FALSE0)) { |
237 | Ns_Log(Warning, "parameter checkforproxy of module nslog is deprecated; " |
238 | "use global parameter reversproxymode instead"); |
239 | logPtr->flags |= LOG_CHECKFORPROXY0x10u; |
240 | } |
241 | |
242 | logPtr->driverPattern = Ns_ConfigString(path, "driver", NULL((void*)0)); |
243 | |
244 | logPtr->ipv4maskPtr = NULL((void*)0); |
245 | #ifdef HAVE_IPV61 |
246 | logPtr->ipv6maskPtr = NULL((void*)0); |
247 | #endif |
248 | if (Ns_ConfigBool(path, "masklogaddr", NS_FALSE0)) { |
249 | const char* maskString; |
250 | const char *default_ipv4MaskString = "255.255.255.0"; |
251 | #ifdef HAVE_IPV61 |
252 | const char *default_ipv6MaskString = "ff:ff:ff:ff::"; |
253 | #endif |
254 | logPtr->flags |= LOG_MASKIP0x80u; |
255 | |
256 | #ifdef HAVE_IPV61 |
257 | maskString = Ns_ConfigGetValue(path, "maskipv6"); |
258 | if (maskString == NULL((void*)0)) { |
259 | maskString = default_ipv6MaskString; |
260 | } |
261 | |
262 | if (ns_inet_pton((struct sockaddr *)&logPtr->ipv6maskStruct, maskString) == 1) { |
263 | logPtr->ipv6maskPtr = (struct sockaddr *)&logPtr->ipv6maskStruct; |
264 | } |
265 | #endif |
266 | maskString = Ns_ConfigGetValue(path, "maskipv4"); |
267 | if (maskString == NULL((void*)0)) { |
268 | maskString = default_ipv4MaskString; |
269 | } |
270 | if (ns_inet_pton((struct sockaddr *)&logPtr->ipv4maskStruct, maskString) == 1) { |
271 | logPtr->ipv4maskPtr = (struct sockaddr *)&logPtr->ipv4maskStruct; |
272 | } |
273 | } |
274 | |
275 | /* |
276 | * Schedule various log roll and shutdown options. |
277 | */ |
278 | |
279 | if (Ns_ConfigBool(path, "rolllog", NS_TRUE1)) { |
280 | int hour = Ns_ConfigIntRange(path, "rollhour", 0, 0, 23); |
281 | |
282 | Ns_ScheduleDaily(LogRollCallback, logPtr, |
283 | 0, hour, 0, NULL((void*)0)); |
284 | } |
285 | if (Ns_ConfigBool(path, "rollonsignal", NS_FALSE0)) { |
286 | Ns_RegisterAtSignal((Ns_Callback *)(ns_funcptr_t)LogRollCallback, logPtr); |
287 | } |
288 | |
289 | /* |
290 | * Parse extended headers; it is just a list of names |
291 | */ |
292 | (void)ParseExtendedHeaders(logPtr, Ns_ConfigGetValue(path, "extendedheaders")); |
293 | |
294 | /* |
295 | * Open the log and register the trace |
296 | */ |
297 | |
298 | if (LogOpen(logPtr) != NS_OK) { |
299 | return NS_ERROR; |
300 | } |
301 | |
302 | Ns_RegisterServerTrace(server, LogTrace, logPtr); |
303 | Ns_RegisterAtShutdown(LogCloseCallback, logPtr); |
304 | result = Ns_TclRegisterTrace(server, AddCmds, logPtr, NS_TCL_TRACE_CREATE); |
305 | |
306 | return result; |
307 | } |
308 | |
309 | static Ns_ReturnCode |
310 | AddCmds(Tcl_Interp *interp, const void *arg) |
311 | { |
312 | const Log *logPtr = arg; |
313 | |
314 | Tcl_CreateObjCommand(interp, "ns_accesslog", LogObjCmd, (ClientData)logPtr, NULL((void*)0)); |
315 | return NS_OK; |
316 | } |
317 | |
318 | /* |
319 | *---------------------------------------------------------------------- |
320 | * |
321 | * ParseExtendedHeaders -- |
322 | * |
323 | * Parse a string specifying the extended parameters. |
324 | * The string might be: |
325 | * |
326 | * - a Tcl list of plain request header fields, like e.g. |
327 | * {Referer X-Forwarded-For} |
328 | * |
329 | * - a Tcl list of header fields with tags to denote request or response |
330 | * header fields, like e.g. {req:Referer response:Content-Type} |
331 | * |
332 | * Results: |
333 | * None |
334 | * |
335 | * Side effects: |
336 | * Upadating fields in logPtr |
337 | * |
338 | *---------------------------------------------------------------------- |
339 | */ |
340 | static Ns_ReturnCode |
341 | ParseExtendedHeaders(Log *logPtr, const char *str) |
342 | { |
343 | Ns_ReturnCode result = NS_OK; |
344 | |
345 | NS_NONNULL_ASSERT(logPtr != NULL)((void) (0)); |
346 | |
347 | if (str != NULL((void*)0)) { |
348 | int argc; |
349 | const char **argv; |
350 | |
351 | if (Tcl_SplitList(NULL((void*)0), str, &argc, &argv) != TCL_OK0) { |
352 | Ns_Log(Error, "nslog: invalid 'extendedHeaders' parameter: '%s'", str); |
353 | result = NS_ERROR; |
354 | |
355 | } else { |
356 | int i, tagged = 0; |
357 | |
358 | if (logPtr->extendedHeaders != NULL((void*)0)) { |
359 | ns_free((char *)logPtr->extendedHeaders); |
360 | } |
361 | if (logPtr->requestHeaders != NULL((void*)0)) { |
362 | Tcl_Free((char *) logPtr->requestHeaders); |
363 | } |
364 | if (logPtr->responseHeaders != NULL((void*)0)) { |
365 | Tcl_Free((char *)logPtr->responseHeaders); |
366 | } |
367 | logPtr->extendedHeaders = ns_strdup(str); |
368 | |
369 | for (i = 0; i < argc; i++) { |
370 | const char *fieldName = argv[i]; |
371 | |
372 | if (strchr(fieldName, ':') != NULL((void*)0)) { |
373 | tagged ++; |
374 | } |
375 | } |
376 | if (tagged == 0) { |
377 | logPtr->requestHeaders = (const char **)argv; |
378 | logPtr->nrRequestHeaders = argc; |
379 | logPtr->responseHeaders = NULL((void*)0); |
380 | logPtr->nrResponseHeaders = 0; |
381 | } else { |
382 | Tcl_DString requestHeaderFields, responseHeaderFields; |
383 | int nrRequestsHeaderFields = 0, nrResponseHeaderFields = 0; |
384 | |
385 | Tcl_DStringInit(&requestHeaderFields); |
386 | Tcl_DStringInit(&responseHeaderFields); |
387 | |
388 | for (i = 0; i < argc; i++) { |
389 | const char *fieldName = argv[i]; |
390 | char *suffix = strchr(fieldName, ':'); |
391 | |
392 | if (suffix != NULL((void*)0)) { |
393 | *suffix = '\0'; |
394 | suffix ++; |
395 | if (strncmp(fieldName, "request", 3) == 0) { |
396 | Tcl_DStringAppendElement(&requestHeaderFields, suffix); |
397 | nrRequestsHeaderFields++; |
398 | } else if (strncmp(fieldName, "response", 3) == 0) { |
399 | Tcl_DStringAppendElement(&responseHeaderFields, suffix); |
400 | nrResponseHeaderFields++; |
401 | } else { |
402 | Ns_Log(Error, "nslog: ignore invalid entry prefix '%s' in extendedHeaders parameter", |
403 | fieldName); |
404 | } |
405 | } else { |
406 | /* |
407 | * No prefix, assume request header field |
408 | */ |
409 | Tcl_DStringAppendElement(&requestHeaderFields, suffix); |
410 | nrRequestsHeaderFields++; |
411 | } |
412 | } |
413 | (void) Tcl_SplitList(NULL((void*)0), requestHeaderFields.string, |
414 | &logPtr->nrRequestHeaders, |
415 | &logPtr->requestHeaders); |
416 | (void) Tcl_SplitList(NULL((void*)0), responseHeaderFields.string, |
417 | &logPtr->nrResponseHeaders, |
418 | &logPtr->responseHeaders); |
419 | |
420 | Tcl_DStringFree(&requestHeaderFields); |
421 | Tcl_DStringFree(&responseHeaderFields); |
422 | Tcl_Free((char*)argv); |
423 | } |
424 | } |
425 | } |
426 | return result; |
427 | } |
428 | |
429 | /* |
430 | *---------------------------------------------------------------------- |
431 | * |
432 | * LogObjCmd -- |
433 | * |
434 | * Implements "ns_accesslog". |
435 | * |
436 | * Results: |
437 | * Standard Tcl result. |
438 | * |
439 | * Side effects: |
440 | * Depends on command. |
441 | * |
442 | *---------------------------------------------------------------------- |
443 | */ |
444 | |
445 | static int |
446 | LogObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv) |
447 | { |
448 | const char *strarg; |
449 | int rc, cmd, result = TCL_OK0; |
450 | Tcl_DString ds; |
451 | Log *logPtr = clientData; |
452 | |
453 | enum { |
454 | ROLLFMT, MAXBACKUP, MAXBUFFER, EXTHDRS, |
455 | FLAGS, FILE, ROLL |
456 | }; |
457 | static const char *const subcmd[] = { |
458 | "rollfmt", "maxbackup", "maxbuffer", "extendedheaders", |
459 | "flags", "file", "roll", NULL((void*)0) |
460 | }; |
461 | |
462 | if (objc < 2) { |
463 | Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); |
464 | return TCL_ERROR1; |
465 | } |
466 | rc = Tcl_GetIndexFromObj(interp, objv[1], subcmd, "option", 0, &cmd)Tcl_GetIndexFromObjStruct(interp, objv[1], subcmd, sizeof(char *), "option", 0, &cmd); |
467 | if (rc != TCL_OK0) { |
468 | return TCL_ERROR1; |
469 | } |
470 | |
471 | switch (cmd) { |
472 | case ROLLFMT: |
473 | Ns_MutexLock(&logPtr->lock); |
474 | if (objc > 2) { |
475 | strarg = ns_strdup(Tcl_GetString(objv[2])); |
476 | if (logPtr->rollfmt != NULL((void*)0)) { |
477 | ns_free((char *)logPtr->rollfmt); |
478 | } |
479 | logPtr->rollfmt = strarg; |
480 | } |
481 | strarg = logPtr->rollfmt; |
482 | Ns_MutexUnlock(&logPtr->lock); |
483 | if (strarg != NULL((void*)0)) { |
484 | Tcl_SetObjResult(interp, Tcl_NewStringObj(strarg, -1)); |
485 | } |
486 | break; |
487 | |
488 | case MAXBACKUP: |
489 | { |
490 | int intarg = 0; |
491 | |
492 | if (objc > 2) { |
493 | if (Tcl_GetIntFromObj(interp, objv[2], &intarg) != TCL_OK0) { |
494 | result = TCL_ERROR1; |
495 | } else { |
496 | if (intarg < 1) { |
497 | intarg = 100; |
498 | } |
499 | } |
500 | } |
501 | if (result == TCL_OK0) { |
502 | Ns_MutexLock(&logPtr->lock); |
503 | if (objc > 2) { |
504 | logPtr->maxbackup = intarg; |
505 | } else { |
506 | intarg = logPtr->maxbackup; |
507 | } |
508 | Ns_MutexUnlock(&logPtr->lock); |
509 | Tcl_SetObjResult(interp, Tcl_NewIntObj(intarg)); |
510 | } |
511 | } |
512 | break; |
513 | |
514 | case MAXBUFFER: |
515 | { |
516 | int intarg = 0; |
517 | |
518 | if (objc > 2) { |
519 | if (Tcl_GetIntFromObj(interp, objv[2], &intarg) != TCL_OK0) { |
520 | result = TCL_ERROR1; |
521 | } else { |
522 | if (intarg < 0) { |
523 | intarg = 0; |
524 | } |
525 | } |
526 | } |
527 | if (result == TCL_OK0) { |
528 | Ns_MutexLock(&logPtr->lock); |
529 | if (objc > 2) { |
530 | logPtr->maxlines = intarg; |
531 | } else { |
532 | intarg = logPtr->maxlines; |
533 | } |
534 | Ns_MutexUnlock(&logPtr->lock); |
535 | Tcl_SetObjResult(interp, Tcl_NewIntObj(intarg)); |
536 | } |
537 | } |
538 | break; |
539 | |
540 | case EXTHDRS: |
541 | { |
542 | Ns_MutexLock(&logPtr->lock); |
543 | if (objc > 2) { |
544 | result = ParseExtendedHeaders(logPtr, Tcl_GetString(objv[2])); |
545 | } |
546 | if (result == TCL_OK0) { |
547 | Tcl_SetObjResult(interp, Tcl_NewStringObj(logPtr->extendedHeaders, -1)); |
548 | } else { |
549 | Ns_TclPrintfResult(interp, "invalid value: %s", |
550 | Tcl_GetString(objv[2])); |
551 | } |
552 | Ns_MutexUnlock(&logPtr->lock); |
553 | } |
554 | break; |
555 | |
556 | case FLAGS: |
557 | { |
558 | unsigned int flags; |
559 | |
560 | Tcl_DStringInit(&ds); |
561 | if (objc > 2) { |
562 | flags = 0u; |
563 | Tcl_DStringAppend(&ds, Tcl_GetString(objv[2]), -1); |
564 | Ns_StrToLower(ds.string); |
565 | if (strstr(ds.string, "logcombined")) { |
566 | flags |= LOG_COMBINED0x01u; |
567 | } |
568 | if (strstr(ds.string, "formattedtime")) { |
569 | flags |= LOG_FMTTIME0x02u; |
570 | } |
571 | if (strstr(ds.string, "logreqtime")) { |
572 | flags |= LOG_REQTIME0x04u; |
573 | } |
574 | if (strstr(ds.string, "logpartialtimes")) { |
575 | flags |= LOG_PARTIALTIMES0x08u; |
576 | } |
577 | if (strstr(ds.string, "checkforproxy")) { |
578 | flags |= LOG_CHECKFORPROXY0x10u; |
579 | } |
580 | if (strstr(ds.string, "suppressquery")) { |
581 | flags |= LOG_SUPPRESSQUERY0x20u; |
582 | } |
583 | Tcl_DStringSetLength(&ds, 0); |
584 | Ns_MutexLock(&logPtr->lock); |
585 | logPtr->flags = flags; |
586 | Ns_MutexUnlock(&logPtr->lock); |
587 | } else { |
588 | Ns_MutexLock(&logPtr->lock); |
589 | flags = logPtr->flags; |
590 | Ns_MutexUnlock(&logPtr->lock); |
591 | } |
592 | if ((flags & LOG_COMBINED0x01u)) { |
593 | Tcl_DStringAppend(&ds, "logcombined ", -1); |
594 | } |
595 | if ((flags & LOG_FMTTIME0x02u)) { |
596 | Tcl_DStringAppend(&ds, "formattedtime ", -1); |
597 | } |
598 | if ((flags & LOG_REQTIME0x04u)) { |
599 | Tcl_DStringAppend(&ds, "logreqtime ", -1); |
600 | } |
601 | if ((flags & LOG_PARTIALTIMES0x08u)) { |
602 | Tcl_DStringAppend(&ds, "logpartialtimes ", -1); |
603 | } |
604 | if ((flags & LOG_CHECKFORPROXY0x10u)) { |
605 | Tcl_DStringAppend(&ds, "checkforproxy ", -1); |
606 | } |
607 | if ((flags & LOG_SUPPRESSQUERY0x20u)) { |
608 | Tcl_DStringAppend(&ds, "suppressquery ", -1); |
609 | } |
610 | Tcl_DStringResult(interp, &ds); |
611 | } |
612 | break; |
613 | |
614 | case FILE: |
615 | if (objc > 2) { |
616 | Tcl_DStringInit(&ds); |
617 | strarg = Tcl_GetString(objv[2]); |
618 | if (Ns_PathIsAbsolute(strarg) == NS_FALSE0) { |
619 | Ns_HomePath(&ds, strarg, (char *)0L); |
620 | strarg = ds.string; |
621 | } |
622 | Ns_MutexLock(&logPtr->lock); |
623 | LogClose(logPtr); |
624 | ns_free((char *)logPtr->filename); |
625 | logPtr->filename = ns_strdup(strarg); |
626 | Tcl_DStringFree(&ds); |
627 | LogOpen(logPtr); |
628 | } else { |
629 | Ns_MutexLock(&logPtr->lock); |
630 | } |
631 | Tcl_SetObjResult(interp, Tcl_NewStringObj(logPtr->filename, -1)); |
632 | Ns_MutexUnlock(&logPtr->lock); |
633 | break; |
634 | |
635 | case ROLL: |
636 | { |
637 | Ns_ReturnCode status = NS_ERROR; |
638 | |
639 | Ns_MutexLock(&logPtr->lock); |
640 | if (objc == 2) { |
641 | status = LogRoll(logPtr); |
642 | } else if (objc > 2) { |
643 | strarg = Tcl_GetString(objv[2]); |
644 | if (Tcl_FSAccess(objv[2], F_OK0) == 0) { |
645 | status = Ns_RollFile(strarg, logPtr->maxbackup); |
646 | } else { |
647 | Tcl_Obj *path = Tcl_NewStringObj(logPtr->filename, -1); |
648 | |
649 | Tcl_IncrRefCount(path)++(path)->refCount; |
650 | rc = Tcl_FSRenameFile(path, objv[2]); |
651 | Tcl_DecrRefCount(path)do { Tcl_Obj *_objPtr = (path); if (_objPtr->refCount-- <= 1) { TclFreeObj(_objPtr); } } while(0); |
652 | if (rc != 0) { |
653 | status = NS_ERROR; |
654 | } else { |
655 | LogFlush(logPtr, &logPtr->buffer); |
656 | status = LogOpen(logPtr); |
657 | } |
658 | } |
659 | } |
660 | if (status != NS_OK) { |
661 | Ns_TclPrintfResult(interp, "could not roll \"%s\": %s", |
662 | logPtr->filename, Tcl_PosixError(interp)); |
663 | } |
664 | Ns_MutexUnlock(&logPtr->lock); |
665 | if (status != NS_OK) { |
666 | result = TCL_ERROR1; |
667 | } |
668 | } |
669 | break; |
670 | } |
671 | |
672 | return result; |
673 | } |
674 | |
675 | /* |
676 | *---------------------------------------------------------------------- |
677 | * |
678 | * AppendEscaped -- |
679 | * |
680 | * Append a string with escaped characters |
681 | * |
682 | * Results: |
683 | * None. |
684 | * |
685 | * Side effects: |
686 | * updated dstring |
687 | * |
688 | *---------------------------------------------------------------------- |
689 | */ |
690 | |
691 | static void |
692 | AppendEscaped(Tcl_DString *dsPtr, const char *toProcess) |
693 | { |
694 | const char *breakChar; |
695 | |
696 | NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0)); |
697 | NS_NONNULL_ASSERT(toProcess != NULL)((void) (0)); |
698 | |
699 | do { |
700 | breakChar = strpbrk(toProcess, "\r\n\t\\\""); |
701 | if (breakChar == NULL((void*)0)) { |
702 | /* |
703 | * No break-char found, append all and stop |
704 | */ |
705 | Tcl_DStringAppend(dsPtr, toProcess, -1); |
706 | } else { |
707 | /* |
708 | * Append the break-char free prefix |
709 | */ |
710 | Tcl_DStringAppend(dsPtr, toProcess, (int)(breakChar - toProcess)); |
711 | |
712 | /* |
713 | * Escape the break-char |
714 | */ |
715 | switch (*breakChar) { |
716 | case '\n': |
717 | Tcl_DStringAppend(dsPtr, "\\n", 2); |
718 | break; |
719 | case '\r': |
720 | Tcl_DStringAppend(dsPtr, "\\r", 2); |
721 | break; |
722 | case '\t': |
723 | Tcl_DStringAppend(dsPtr, "\\t", 2); |
724 | break; |
725 | case '"': |
726 | Tcl_DStringAppend(dsPtr, "\\\"", 2); |
727 | break; |
728 | case '\\': |
729 | Tcl_DStringAppend(dsPtr, "\\\\", 2); |
730 | break; |
731 | default: |
732 | /*should not happen */ assert(0)((void) (0)); |
733 | break; |
734 | } |
735 | |
736 | /* |
737 | * Check for further protected characters after the break char. |
738 | */ |
739 | toProcess = breakChar + 1; |
740 | } |
741 | } while (breakChar != NULL((void*)0)); |
742 | } |
743 | |
744 | /* |
745 | *---------------------------------------------------------------------- |
746 | * |
747 | * AppendExtHeaders -- |
748 | * |
749 | * Append named extended header fields from provided set to log entry. |
750 | * |
751 | * Results: |
752 | * None |
753 | * |
754 | * Side effects: |
755 | * Append to Tcl_DString |
756 | * |
757 | *---------------------------------------------------------------------- |
758 | */ |
759 | |
760 | static void |
761 | AppendExtHeaders(Tcl_DString *dsPtr, const char **argv, const Ns_Set *set) |
762 | { |
763 | NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0)); |
764 | |
765 | if (set != NULL((void*)0) && argv != NULL((void*)0)) { |
766 | const char **h; |
767 | |
768 | for (h = argv; *h != NULL((void*)0); h++) { |
769 | const char *p; |
770 | |
771 | Tcl_DStringAppend(dsPtr, " \"", 2); |
772 | p = Ns_SetIGet(set, *h); |
773 | if (p != NULL((void*)0)) { |
774 | AppendEscaped(dsPtr, p); |
775 | } |
776 | Tcl_DStringAppend(dsPtr, "\"", 1); |
777 | } |
778 | } |
779 | } |
780 | |
781 | |
782 | /* |
783 | *---------------------------------------------------------------------- |
784 | * |
785 | * LogTrace -- |
786 | * |
787 | * Trace routine for appending the log with the current |
788 | * connection results. |
789 | * |
790 | * Results: |
791 | * None. |
792 | * |
793 | * Side effects: |
794 | * Entry is appended to the open log. |
795 | * |
796 | *---------------------------------------------------------------------- |
797 | */ |
798 | |
799 | static void |
800 | LogTrace(void *arg, Ns_Conn *conn) |
801 | { |
802 | Log *logPtr = arg; |
803 | const char *user, *p, *driverName; |
804 | char buffer[PIPE_BUF4096], *bufferPtr = NULL((void*)0); |
805 | int n, i; |
806 | Ns_ReturnCode status; |
807 | size_t bufferSize = 0u; |
808 | Tcl_DString ds, *dsPtr = &ds; |
809 | char ipString[NS_IPADDR_SIZE46]; |
810 | struct NS_SOCKADDR_STORAGEsockaddr_storage ipStruct, maskedStruct; |
811 | struct sockaddr *maskPtr = NULL((void*)0), |
812 | *ipPtr = (struct sockaddr *)&ipStruct, |
813 | *maskedPtr = (struct sockaddr *)&maskedStruct; |
814 | |
815 | driverName = Ns_ConnDriverName(conn); |
816 | Ns_Log(Debug, "nslog called with driver pattern '%s' via driver '%s' req: %s", |
817 | logPtr->driverPattern, driverName, conn->request.line); |
818 | |
819 | if (logPtr->driverPattern != NULL((void*)0) |
820 | && Tcl_StringMatch(driverName, logPtr->driverPattern) == 0 |
821 | ) { |
822 | /* |
823 | * This is not for us. |
824 | */ |
825 | return; |
826 | } |
827 | |
828 | Tcl_DStringInit(dsPtr); |
829 | Ns_MutexLock(&logPtr->lock); |
830 | |
831 | /* |
832 | * Append the peer address. |
833 | */ |
834 | if ((logPtr->flags & LOG_CHECKFORPROXY0x10u) != 0u) { |
835 | /* |
836 | * This branch is deprecated and kept only for backward |
837 | * compatibility (added Dec 2020). |
838 | */ |
839 | p = Ns_ConnForwardedPeerAddr(conn); |
840 | if (*p == '\0') { |
841 | p = Ns_ConnPeerAddr(conn); |
842 | } |
843 | } else { |
844 | p = Ns_ConnConfiguredPeerAddr(conn); |
845 | } |
846 | |
847 | /* |
848 | * Check if the actual IP address can be converted to internal format (this |
849 | * should be always possible). |
850 | */ |
851 | if ((logPtr->flags |= LOG_MASKIP0x80u) |
852 | && (ns_inet_pton(ipPtr, p) == 1) |
853 | ) { |
854 | |
855 | /* |
856 | * Depending on the class of the IP address, use the appropriate mask. |
857 | */ |
858 | if (ipPtr->sa_family == AF_INET2) { |
859 | maskPtr = logPtr->ipv4maskPtr; |
860 | } |
861 | #ifdef HAVE_IPV61 |
862 | if (ipPtr->sa_family == AF_INET610) { |
863 | maskPtr = logPtr->ipv6maskPtr; |
864 | } |
865 | #endif |
866 | /* |
867 | * If the mask is non-null, the IP "anonymizing" was configured. |
868 | */ |
869 | if (maskPtr != NULL((void*)0)) { |
870 | Ns_SockaddrMask(ipPtr, maskPtr, maskedPtr); |
871 | ns_inet_ntop(maskedPtr, ipString, NS_IPADDR_SIZE46); |
872 | p = ipString; |
873 | } |
874 | } |
875 | |
876 | Tcl_DStringAppend(dsPtr, p, -1); |
877 | |
878 | /* |
879 | * Append the thread name, if requested. |
880 | * This eases to link access-log with error-log entries |
881 | */ |
882 | Tcl_DStringAppend(dsPtr, " ", 1); |
883 | if ((logPtr->flags & LOG_THREADNAME0x40u) != 0) { |
884 | Tcl_DStringAppend(dsPtr, Ns_ThreadGetName(), -1); |
885 | Tcl_DStringAppend(dsPtr, " ", 1); |
886 | } else { |
887 | Tcl_DStringAppend(dsPtr, "- ", 2); |
888 | } |
889 | |
890 | /* |
891 | * Append the authorized user, if any. Watch usernames |
892 | * with embedded blanks; we must properly quote them. |
893 | */ |
894 | |
895 | user = Ns_ConnAuthUser(conn); |
896 | if (user == NULL((void*)0)) { |
897 | Tcl_DStringAppend(dsPtr, "- ", 2); |
898 | } else { |
899 | int quote = 0; |
900 | |
901 | for (p = user; *p && !quote; p++) { |
902 | quote = (CHARTYPE(space, *p)(((*__ctype_b_loc ())[(int) (((int)((unsigned char)(*p))))] & (unsigned short int) _ISspace)) != 0); |
903 | } |
904 | if (quote != 0) { |
905 | Tcl_DStringAppend(dsPtr, "\"", 1); |
906 | Tcl_DStringAppend(dsPtr, user, -1); |
907 | Tcl_DStringAppend(dsPtr, "\" ", 2); |
908 | } else { |
909 | Tcl_DStringAppend(dsPtr, user, -1); |
910 | Tcl_DStringAppend(dsPtr, " ", 1); |
911 | } |
912 | } |
913 | |
914 | /* |
915 | * Append a common log format timestamp including GMT offset |
916 | */ |
917 | |
918 | if (!(logPtr->flags & LOG_FMTTIME0x02u)) { |
919 | Ns_DStringPrintf(dsPtr, "[%" PRId64"l" "d" "]", (int64_t) time(NULL((void*)0))); |
920 | } else { |
921 | char buf[41]; /* Big enough for Ns_LogTime(). */ |
922 | |
923 | Ns_LogTime(buf); |
924 | Tcl_DStringAppend(dsPtr, buf, -1); |
925 | } |
926 | |
927 | /* |
928 | * Append the request line plus query data (if configured) |
929 | */ |
930 | |
931 | if (likely(conn->request.line != NULL)(__builtin_expect((conn->request.line != ((void*)0)), 1))) { |
932 | const char *string = (logPtr->flags & LOG_SUPPRESSQUERY0x20u) ? |
933 | conn->request.url : |
934 | conn->request.line; |
935 | |
936 | Tcl_DStringAppend(dsPtr, " \"", 2); |
937 | if (likely(string != NULL)(__builtin_expect((string != ((void*)0)), 1))) { |
938 | AppendEscaped(dsPtr, string); |
939 | } |
940 | Tcl_DStringAppend(dsPtr, "\" ", 2); |
941 | |
942 | } else { |
943 | Tcl_DStringAppend(dsPtr, " \"\" ", 4); |
944 | } |
945 | |
946 | /* |
947 | * Construct and append the HTTP status code and bytes sent |
948 | */ |
949 | |
950 | n = Ns_ConnResponseStatus(conn); |
951 | Ns_DStringPrintf(dsPtr, "%d %" PRIdz"zd", (n != 0) ? n : 200, Ns_ConnContentSent(conn)); |
952 | |
953 | /* |
954 | * Append the referrer (using the misspelled header field "Referer") and |
955 | * user-agent headers (if any) |
956 | */ |
957 | |
958 | if ((logPtr->flags & LOG_COMBINED0x01u)) { |
959 | |
960 | Tcl_DStringAppend(dsPtr, " \"", 2); |
961 | p = Ns_SetIGet(conn->headers, "referer"); |
962 | if (p != NULL((void*)0)) { |
963 | AppendEscaped(dsPtr, p); |
964 | } |
965 | Tcl_DStringAppend(dsPtr, "\" \"", 3); |
966 | p = Ns_SetIGet(conn->headers, "user-agent"); |
967 | if (p != NULL((void*)0)) { |
968 | AppendEscaped(dsPtr, p); |
969 | } |
970 | Tcl_DStringAppend(dsPtr, "\"", 1); |
971 | } |
972 | |
973 | /* |
974 | * Append the request's elapsed time and queue time (if enabled) |
975 | */ |
976 | |
977 | if ((logPtr->flags & LOG_REQTIME0x04u) != 0u) { |
978 | Ns_Time reqTime, now; |
979 | Ns_GetTime(&now); |
980 | Ns_DiffTime(&now, Ns_ConnStartTime(conn), &reqTime); |
981 | Tcl_DStringAppend(dsPtr, " ", 1); |
982 | Ns_DStringAppendTime(dsPtr, &reqTime); |
983 | |
984 | } |
985 | |
986 | if ((logPtr->flags & LOG_PARTIALTIMES0x08u) != 0u) { |
987 | Ns_Time acceptTime, queueTime, filterTime, runTime; |
988 | Ns_Time *startTimePtr = Ns_ConnStartTime(conn); |
989 | |
990 | Ns_ConnTimeSpans(conn, &acceptTime, &queueTime, &filterTime, &runTime); |
991 | |
992 | Tcl_DStringAppend(dsPtr, " \"", 2); |
993 | Ns_DStringAppendTime(dsPtr, startTimePtr); |
994 | Tcl_DStringAppend(dsPtr, " ", 1); |
Duplicate code detected | |
995 | Ns_DStringAppendTime(dsPtr, &acceptTime); |
996 | Tcl_DStringAppend(dsPtr, " ", 1); |
Similar code here | |
997 | Ns_DStringAppendTime(dsPtr, &queueTime); |
998 | Tcl_DStringAppend(dsPtr, " ", 1); |
999 | Ns_DStringAppendTime(dsPtr, &filterTime); |
1000 | Tcl_DStringAppend(dsPtr, " ", 1); |
1001 | Ns_DStringAppendTime(dsPtr, &runTime); |
1002 | Tcl_DStringAppend(dsPtr, "\"", 1); |
1003 | } |
1004 | |
1005 | /* |
1006 | * Append the extended headers (if any) |
1007 | */ |
1008 | AppendExtHeaders(dsPtr, logPtr->requestHeaders, conn->headers); |
1009 | AppendExtHeaders(dsPtr, logPtr->responseHeaders, conn->outputheaders); |
1010 | |
1011 | for (i = 0; i < dsPtr->length; i++) { |
1012 | /* |
1013 | * Quick fix to disallow terminal escape characters in the log |
1014 | * file. See e.g. http://www.securityfocus.com/bid/37712/info |
1015 | */ |
1016 | if (unlikely(dsPtr->string[i] == 0x1b)(__builtin_expect((dsPtr->string[i] == 0x1b), 0))) { |
1017 | dsPtr->string[i] = 7; /* bell */ |
1018 | } |
1019 | } |
1020 | |
1021 | Ns_Log(Ns_LogAccessDebug, "%s", dsPtr->string); |
1022 | |
1023 | /* |
1024 | * Append the trailing newline and optionally |
1025 | * flush the buffer |
1026 | */ |
1027 | |
1028 | Tcl_DStringAppend(dsPtr, "\n", 1); |
1029 | |
1030 | if (logPtr->maxlines == 0) { |
1031 | bufferSize = (size_t)dsPtr->length; |
1032 | if (bufferSize < PIPE_BUF4096) { |
1033 | /* |
1034 | * Only ns_write() operations < PIPE_BUF are guaranteed to be atomic |
1035 | */ |
1036 | bufferPtr = dsPtr->string; |
1037 | status = NS_OK; |
1038 | } else { |
1039 | status = LogFlush(logPtr, dsPtr); |
1040 | } |
1041 | } else { |
1042 | Tcl_DStringAppend(&logPtr->buffer, dsPtr->string, dsPtr->length); |
1043 | if (++logPtr->curlines > logPtr->maxlines) { |
1044 | bufferSize = (size_t)logPtr->buffer.length; |
1045 | if (bufferSize < PIPE_BUF4096) { |
1046 | /* |
1047 | * Only ns_write() operations < PIPE_BUF are guaranteed to be |
1048 | * atomic. In most cases, the other branch is used. |
1049 | */ |
1050 | memcpy(buffer, logPtr->buffer.string, bufferSize); |
1051 | bufferPtr = buffer; |
1052 | Tcl_DStringSetLength(&logPtr->buffer, 0); |
1053 | status = NS_OK; |
1054 | } else { |
1055 | status = LogFlush(logPtr, &logPtr->buffer); |
1056 | } |
1057 | logPtr->curlines = 0; |
1058 | } else { |
1059 | status = NS_OK; |
1060 | } |
1061 | } |
1062 | Ns_MutexUnlock(&logPtr->lock); |
1063 | (void)(status); /* ignore status */ |
1064 | |
1065 | if (likely(bufferPtr != NULL)(__builtin_expect((bufferPtr != ((void*)0)), 1)) && likely(logPtr->fd >= 0)(__builtin_expect((logPtr->fd >= 0), 1)) && likely(bufferSize > 0)(__builtin_expect((bufferSize > 0), 1))) { |
1066 | (void)NsAsyncWrite(logPtr->fd, bufferPtr, bufferSize); |
1067 | } |
1068 | |
1069 | Tcl_DStringFree(dsPtr); |
1070 | } |
1071 | |
1072 | |
1073 | /* |
1074 | *---------------------------------------------------------------------- |
1075 | * |
1076 | * LogOpen -- |
1077 | * |
1078 | * Open the access log, closing previous log if opened. |
1079 | * Assume caller is holding the log mutex. |
1080 | * |
1081 | * Results: |
1082 | * NS_OK or NS_ERROR. |
1083 | * |
1084 | * Side effects: |
1085 | * Log re-opened. |
1086 | * |
1087 | *---------------------------------------------------------------------- |
1088 | */ |
1089 | |
1090 | static Ns_ReturnCode |
1091 | LogOpen(void *arg) |
1092 | { |
1093 | int fd; |
1094 | Ns_ReturnCode status; |
1095 | Log *logPtr = (Log *)arg; |
1096 | |
1097 | fd = ns_openopen(logPtr->filename, O_APPEND02000 | O_WRONLY01 | O_CREAT0100 | O_CLOEXEC02000000, 0644); |
1098 | if (fd == NS_INVALID_FD(-1)) { |
1099 | Ns_Log(Error, "nslog: error '%s' opening '%s'", |
1100 | strerror(errno(*__errno_location ())), logPtr->filename); |
1101 | status = NS_ERROR; |
1102 | } else { |
1103 | status = NS_OK; |
1104 | if (logPtr->fd >= 0) { |
1105 | ns_closeclose(logPtr->fd); |
1106 | } |
1107 | |
1108 | logPtr->fd = fd; |
1109 | Ns_Log(Notice, "nslog: opened '%s'", logPtr->filename); |
1110 | } |
1111 | |
1112 | return status; |
1113 | } |
1114 | |
1115 | |
1116 | /* |
1117 | *---------------------------------------------------------------------- |
1118 | * |
1119 | * LogClose -- |
1120 | * |
1121 | * Flush and/or close the log. |
1122 | * Assume caller is holding the log mutex. |
1123 | * |
1124 | * Results: |
1125 | * NS_TRUE or NS_FALSE if log was closed. |
1126 | * |
1127 | * Side effects: |
1128 | * Buffer entries, if any, are flushed. |
1129 | * |
1130 | *---------------------------------------------------------------------- |
1131 | */ |
1132 | |
1133 | static Ns_ReturnCode |
1134 | LogClose(void *arg) |
1135 | { |
1136 | Ns_ReturnCode status = NS_OK; |
1137 | Log *logPtr = (Log *)arg; |
1138 | |
1139 | if (logPtr->fd >= 0) { |
1140 | status = LogFlush(logPtr, &logPtr->buffer); |
1141 | ns_closeclose(logPtr->fd); |
1142 | logPtr->fd = NS_INVALID_FD(-1); |
1143 | Tcl_DStringFree(&logPtr->buffer); |
1144 | Ns_Log(Notice, "nslog: closed '%s'", logPtr->filename); |
1145 | } |
1146 | |
1147 | return status; |
1148 | } |
1149 | |
1150 | |
1151 | /* |
1152 | *---------------------------------------------------------------------- |
1153 | * |
1154 | * LogFlush -- |
1155 | * |
1156 | * Flush a log buffer to the open log file. |
1157 | * Assume caller is holding the log mutex. |
1158 | * |
1159 | * Results: |
1160 | * None. |
1161 | * |
1162 | * Side effects: |
1163 | * Will disable the log on error. |
1164 | * |
1165 | *---------------------------------------------------------------------- |
1166 | */ |
1167 | |
1168 | static Ns_ReturnCode |
1169 | LogFlush(Log *logPtr, Tcl_DString *dsPtr) |
1170 | { |
1171 | int len = dsPtr->length; |
1172 | char *buf = dsPtr->string; |
1173 | |
1174 | if (len > 0) { |
1175 | if (logPtr->fd >= 0 && ns_writewrite(logPtr->fd, buf, (size_t)len) != len) { |
1176 | Ns_Log(Error, "nslog: logging disabled: ns_write() failed: '%s'", |
1177 | strerror(errno(*__errno_location ()))); |
1178 | ns_closeclose(logPtr->fd); |
1179 | logPtr->fd = NS_INVALID_FD(-1); |
1180 | } |
1181 | Tcl_DStringSetLength(dsPtr, 0); |
1182 | } |
1183 | |
1184 | return (logPtr->fd == NS_INVALID_FD(-1)) ? NS_ERROR : NS_OK; |
1185 | } |
1186 | |
1187 | |
1188 | /* |
1189 | *---------------------------------------------------------------------- |
1190 | * |
1191 | * LogRoll -- |
1192 | * |
1193 | * Roll and re-open the access log. This procedure is scheduled |
1194 | * and/or registered at signal catching. |
1195 | * |
1196 | * Assume caller is holding the log mutex. |
1197 | * |
1198 | * Results: |
1199 | * None. |
1200 | * |
1201 | * Side effects: |
1202 | * Files are rolled to new names. |
1203 | * |
1204 | *---------------------------------------------------------------------- |
1205 | */ |
1206 | |
1207 | static Ns_ReturnCode |
1208 | LogRoll(void *arg) |
1209 | { |
1210 | Ns_ReturnCode status; |
1211 | Log *logPtr = (Log *)arg; |
1212 | |
1213 | status = Ns_RollFileCondFmt(LogOpen, LogClose, logPtr, |
1214 | logPtr->filename, |
1215 | logPtr->rollfmt, |
1216 | logPtr->maxbackup); |
1217 | |
1218 | //if (status == NS_OK) { |
1219 | // status = LogOpen(logPtr); |
1220 | //} |
1221 | |
1222 | return status; |
1223 | } |
1224 | |
1225 | |
1226 | /* |
1227 | *---------------------------------------------------------------------- |
1228 | * |
1229 | * LogCloseCallback, LogRollCallback - |
1230 | * |
1231 | * Close or roll the log. |
1232 | * |
1233 | * Results: |
1234 | * None. |
1235 | * |
1236 | * Side effects: |
1237 | * See LogClose and LogRoll. |
1238 | * |
1239 | *---------------------------------------------------------------------- |
1240 | */ |
1241 | |
1242 | static void |
1243 | LogCallbackProc(Ns_LogCallbackProc proc, void *arg, const char *desc) |
1244 | { |
1245 | int status; |
1246 | Log *logPtr = arg; |
1247 | |
1248 | Ns_MutexLock(&logPtr->lock); |
1249 | status =(*proc)(logPtr); |
1250 | Ns_MutexUnlock(&logPtr->lock); |
1251 | |
1252 | if (status != NS_OK) { |
1253 | Ns_Log(Error, "nslog: failed: %s '%s': '%s'", desc, logPtr->filename, |
1254 | strerror(Tcl_GetErrno())); |
1255 | } |
1256 | } |
1257 | |
1258 | static void |
1259 | LogCloseCallback(const Ns_Time *toPtr, void *arg) |
1260 | { |
1261 | if (toPtr == NULL((void*)0)) { |
1262 | LogCallbackProc(LogClose, arg, "close"); |
1263 | } |
1264 | } |
1265 | |
1266 | static void |
1267 | LogRollCallback(void *arg, int UNUSED(id)UNUSED_id __attribute__((__unused__))) |
1268 | { |
1269 | LogCallbackProc(LogRoll, arg, "roll"); |
1270 | } |
1271 | |
1272 | |
1273 | /* |
1274 | *---------------------------------------------------------------------- |
1275 | * |
1276 | * LogArg -- |
1277 | * |
1278 | * Copy log filename as argument for callback introspection queries. |
1279 | * |
1280 | * Results: |
1281 | * None. |
1282 | * |
1283 | * Side effects: |
1284 | * None. |
1285 | * |
1286 | *---------------------------------------------------------------------- |
1287 | */ |
1288 | |
1289 | static void |
1290 | LogArg(Tcl_DString *dsPtr, const void *arg) |
1291 | { |
1292 | const Log *logPtr = arg; |
1293 | |
1294 | Tcl_DStringAppendElement(dsPtr, logPtr->filename); |
1295 | } |
1296 | |
1297 | /* |
1298 | * Local Variables: |
1299 | * mode: c |
1300 | * c-basic-offset: 4 |
1301 | * fill-column: 78 |
1302 | * indent-tabs-mode: nil |
1303 | * End: |
1304 | */ |