1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: Implementation of debug/logging, msDebug() and related functions.
6 * Author: Daniel Morissette
7 *
8 ******************************************************************************
9 * Copyright (c) 1996-2007 Regents of the University of Minnesota.
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies of this Software or works derived from this Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 ****************************************************************************/
29
30
31 #include "mapserver.h"
32 #include "maperror.h"
33 #include "mapthread.h"
34 #include "maptime.h"
35
36 #include <time.h>
37 #ifndef _WIN32
38 #include <sys/time.h>
39 #include <unistd.h>
40 #endif
41 #include <stdarg.h>
42
43 #ifdef NEED_NONBLOCKING_STDERR
44 #include <fcntl.h>
45 #endif
46
47 #ifdef _WIN32
48 #include <windows.h> /* OutputDebugStringA() */
49 #endif
50
51
52
53
54 #ifndef USE_THREAD
55
msGetDebugInfoObj()56 debugInfoObj *msGetDebugInfoObj()
57 {
58 static debugInfoObj debuginfo = {MS_DEBUGLEVEL_ERRORSONLY,
59 MS_DEBUGMODE_OFF, NULL, NULL
60 };
61 return &debuginfo;
62 }
63
64 #else
65
66 static debugInfoObj *debuginfo_list = NULL;
67
msGetDebugInfoObj()68 debugInfoObj *msGetDebugInfoObj()
69 {
70 debugInfoObj *link;
71 void* thread_id;
72 debugInfoObj *ret_obj;
73
74 msAcquireLock( TLOCK_DEBUGOBJ );
75
76 thread_id = msGetThreadId();
77
78 /* find link for this thread */
79
80 for( link = debuginfo_list;
81 link != NULL && link->thread_id != thread_id
82 && link->next != NULL && link->next->thread_id != thread_id;
83 link = link->next ) {}
84
85 /* If the target thread link is already at the head of the list were ok */
86 if( debuginfo_list != NULL && debuginfo_list->thread_id == thread_id ) {
87 }
88
89 /* We don't have one ... initialize one. */
90 else if( link == NULL || link->next == NULL ) {
91 debugInfoObj *new_link;
92
93 new_link = (debugInfoObj *) msSmallMalloc(sizeof(debugInfoObj));
94 new_link->next = debuginfo_list;
95 new_link->thread_id = thread_id;
96 new_link->global_debug_level = MS_DEBUGLEVEL_ERRORSONLY;
97 new_link->debug_mode = MS_DEBUGMODE_OFF;
98 new_link->errorfile = NULL;
99 new_link->fp = NULL;
100 debuginfo_list = new_link;
101 }
102
103 /* If the link is not already at the head of the list, promote it */
104 else if( link != NULL && link->next != NULL ) {
105 debugInfoObj *target = link->next;
106
107 link->next = link->next->next;
108 target->next = debuginfo_list;
109 debuginfo_list = target;
110 }
111
112 ret_obj = debuginfo_list;
113
114 msReleaseLock( TLOCK_DEBUGOBJ );
115
116 return ret_obj;
117 }
118 #endif
119
120
121 /* msSetErrorFile()
122 **
123 ** Set output target, ready to write to it, open file if necessary
124 **
125 ** If pszRelToPath != NULL then we will try to make the value relative to
126 ** this path if it is not absolute already and it's not one of the special
127 ** values (stderr, stdout, windowsdebug)
128 **
129 ** Returns MS_SUCCESS/MS_FAILURE
130 */
msSetErrorFile(const char * pszErrorFile,const char * pszRelToPath)131 int msSetErrorFile(const char *pszErrorFile, const char *pszRelToPath)
132 {
133 char extended_path[MS_MAXPATHLEN];
134 debugInfoObj *debuginfo = msGetDebugInfoObj();
135
136 if (strcmp(pszErrorFile, "stderr") != 0 &&
137 strcmp(pszErrorFile, "stdout") != 0 &&
138 strcmp(pszErrorFile, "windowsdebug") != 0) {
139 /* Try to make the path relative */
140 if(msBuildPath(extended_path, pszRelToPath, pszErrorFile) == NULL)
141 return MS_FAILURE;
142 pszErrorFile = extended_path;
143 }
144
145 if (debuginfo->errorfile && pszErrorFile &&
146 strcmp(debuginfo->errorfile, pszErrorFile) == 0) {
147 /* Nothing to do, already writing to the right place */
148 return MS_SUCCESS;
149 }
150
151 /* Close current output file if any */
152 msCloseErrorFile();
153
154 /* NULL or empty target will just close current output and return */
155 if (pszErrorFile == NULL || *pszErrorFile == '\0')
156 return MS_SUCCESS;
157
158 if (strcmp(pszErrorFile, "stderr") == 0) {
159 debuginfo->fp = stderr;
160 debuginfo->errorfile = msStrdup(pszErrorFile);
161 debuginfo->debug_mode = MS_DEBUGMODE_STDERR;
162 } else if (strcmp(pszErrorFile, "stdout") == 0) {
163 debuginfo->fp = stdout;
164 debuginfo->errorfile = msStrdup(pszErrorFile);
165 debuginfo->debug_mode = MS_DEBUGMODE_STDOUT;
166 } else if (strcmp(pszErrorFile, "windowsdebug") == 0) {
167 #ifdef _WIN32
168 debuginfo->errorfile = msStrdup(pszErrorFile);
169 debuginfo->fp = NULL;
170 debuginfo->debug_mode = MS_DEBUGMODE_WINDOWSDEBUG;
171 #else
172 msSetError(MS_MISCERR, "'MS_ERRORFILE windowsdebug' is available only on Windows platforms.", "msSetErrorFile()");
173 return MS_FAILURE;
174 #endif
175 } else {
176 debuginfo->fp = fopen(pszErrorFile, "a");
177 if (debuginfo->fp == NULL) {
178 msSetError(MS_MISCERR, "Failed to open MS_ERRORFILE %s", "msSetErrorFile()", pszErrorFile);
179 return MS_FAILURE;
180 }
181 debuginfo->errorfile = msStrdup(pszErrorFile);
182 debuginfo->debug_mode = MS_DEBUGMODE_FILE;
183 }
184
185 return MS_SUCCESS;
186 }
187
188 /* msCloseErrorFile()
189 **
190 ** Close current output file (if one is open) and reset related members
191 */
msCloseErrorFile()192 void msCloseErrorFile()
193 {
194 debugInfoObj *debuginfo = msGetDebugInfoObj();
195
196 if (debuginfo && debuginfo->debug_mode != MS_DEBUGMODE_OFF) {
197 if (debuginfo->fp && debuginfo->debug_mode == MS_DEBUGMODE_FILE)
198 fclose(debuginfo->fp);
199
200 if (debuginfo->fp && (debuginfo->debug_mode == MS_DEBUGMODE_STDERR ||
201 debuginfo->debug_mode == MS_DEBUGMODE_STDOUT))
202 fflush(debuginfo->fp); /* just flush stderr or stdout */
203
204 debuginfo->fp = NULL;
205
206 msFree(debuginfo->errorfile);
207 debuginfo->errorfile = NULL;
208
209 debuginfo->debug_mode = MS_DEBUGMODE_OFF;
210 }
211 }
212
213
214
215 /* msGetErrorFile()
216 **
217 ** Returns name of current error file
218 **
219 ** Returns NULL if not set.
220 */
msGetErrorFile()221 const char *msGetErrorFile()
222 {
223 debugInfoObj *debuginfo = msGetDebugInfoObj();
224
225 if (debuginfo)
226 return debuginfo->errorfile;
227
228 return NULL;
229 }
230
231 /* Set/Get the current global debug level value, used as default value for
232 ** new map and layer objects and to control msDebug() calls outside of
233 ** the context of mapObj or layerObj.
234 **
235 */
msSetGlobalDebugLevel(int level)236 void msSetGlobalDebugLevel(int level)
237 {
238 debugInfoObj *debuginfo = msGetDebugInfoObj();
239
240 if (debuginfo)
241 debuginfo->global_debug_level = (debugLevel)level;
242 }
243
msGetGlobalDebugLevel()244 debugLevel msGetGlobalDebugLevel()
245 {
246 debugInfoObj *debuginfo = msGetDebugInfoObj();
247
248 if (debuginfo)
249 return debuginfo->global_debug_level;
250
251 return MS_DEBUGLEVEL_ERRORSONLY;
252 }
253
254
255 /* msDebugInitFromEnv()
256 **
257 ** Init debug state from MS_ERRORFILE and MS_DEBUGLEVEL env vars if set
258 **
259 ** Returns MS_SUCCESS/MS_FAILURE
260 */
msDebugInitFromEnv()261 int msDebugInitFromEnv()
262 {
263 const char *val;
264
265 if( (val=getenv( "MS_ERRORFILE" )) != NULL ) {
266 if ( msSetErrorFile(val, NULL) != MS_SUCCESS )
267 return MS_FAILURE;
268 }
269
270 if( (val=getenv( "MS_DEBUGLEVEL" )) != NULL )
271 msSetGlobalDebugLevel(atoi(val));
272
273 return MS_SUCCESS;
274 }
275
276
277 /* msDebugCleanup()
278 **
279 ** Called by msCleanup to remove info related to this thread.
280 */
msDebugCleanup()281 void msDebugCleanup()
282 {
283 /* make sure file is closed */
284 msCloseErrorFile();
285
286 #ifdef USE_THREAD
287 {
288 void* thread_id = msGetThreadId();
289 debugInfoObj *link;
290
291 msAcquireLock( TLOCK_DEBUGOBJ );
292
293 /* find link for this thread */
294
295 for( link = debuginfo_list;
296 link != NULL && link->thread_id != thread_id
297 && link->next != NULL && link->next->thread_id != thread_id;
298 link = link->next ) {}
299
300 if( link->thread_id == thread_id ) {
301 /* presumably link is at head of list. */
302 if( debuginfo_list == link )
303 debuginfo_list = link->next;
304
305 free( link );
306 } else if( link->next != NULL && link->next->thread_id == thread_id ) {
307 debugInfoObj *next_link = link->next;
308 link->next = link->next->next;
309 free( next_link );
310 }
311 msReleaseLock( TLOCK_DEBUGOBJ );
312 }
313 #endif
314
315 }
316
317 /* msDebug()
318 **
319 ** Outputs/logs messages to the MS_ERRORFILE if one is set
320 ** (see msSetErrorFile())
321 **
322 */
msDebug(const char * pszFormat,...)323 void msDebug( const char * pszFormat, ... )
324 {
325 va_list args;
326 debugInfoObj *debuginfo = msGetDebugInfoObj();
327
328 if (debuginfo == NULL || debuginfo->debug_mode == MS_DEBUGMODE_OFF)
329 return; /* Don't waste time here! */
330
331 if (debuginfo->fp) {
332 /* Writing to a stdio file handle */
333
334 #if defined(USE_FASTCGI)
335 /* It seems the FastCGI stuff inserts a timestamp anyways, so */
336 /* we might as well skip this one if writing to stderr w/ FastCGI. */
337 if (debuginfo->debug_mode != MS_DEBUGMODE_STDERR)
338 #endif
339 {
340 struct mstimeval tv;
341 time_t t;
342 msGettimeofday(&tv, NULL);
343 t = tv.tv_sec;
344 msIO_fprintf(debuginfo->fp, "[%s].%ld ",
345 msStringChop(ctime(&t)), (long)tv.tv_usec);
346 }
347
348 va_start(args, pszFormat);
349 msIO_vfprintf(debuginfo->fp, pszFormat, args);
350 va_end(args);
351 }
352 #ifdef _WIN32
353 else if (debuginfo->debug_mode == MS_DEBUGMODE_WINDOWSDEBUG) {
354 /* Writing to Windows Debug Console */
355
356 char szMessage[MESSAGELENGTH];
357
358 va_start(args, pszFormat);
359 vsnprintf( szMessage, MESSAGELENGTH, pszFormat, args );
360 va_end(args);
361
362 szMessage[MESSAGELENGTH-1] = '\0';
363 OutputDebugStringA(szMessage);
364 }
365 #endif
366
367 }
368
369
370 /* msDebug2()
371 **
372 ** Variadic function with no operation
373 **
374 */
msDebug2(int level,...)375 void msDebug2( int level, ... )
376 {
377 }
378