Coverage Report

Created: 2025-06-22 06:59

/src/MapServer/src/mapdebug.c
Line
Count
Source (jump to first uncovered line)
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
#include "mapserver.h"
31
#include "maperror.h"
32
#include "mapthread.h"
33
#include "maptime.h"
34
35
#include "cpl_conv.h"
36
37
#include <time.h>
38
#ifndef _WIN32
39
#include <sys/time.h>
40
#include <unistd.h>
41
#endif
42
#include <stdarg.h>
43
44
#ifdef NEED_NONBLOCKING_STDERR
45
#include <fcntl.h>
46
#endif
47
48
#ifdef _WIN32
49
#include <windows.h> /* OutputDebugStringA() */
50
#endif
51
52
#ifndef USE_THREAD
53
54
84.6k
debugInfoObj *msGetDebugInfoObj() {
55
84.6k
  static debugInfoObj debuginfo = {
56
84.6k
      MS_DEBUGLEVEL_ERRORSONLY, MS_DEBUGMODE_OFF, NULL, NULL, 0, NULL};
57
84.6k
  return &debuginfo;
58
84.6k
}
59
60
#else
61
62
static debugInfoObj *debuginfo_list = NULL;
63
64
debugInfoObj *msGetDebugInfoObj() {
65
  debugInfoObj *link;
66
  void *thread_id;
67
  debugInfoObj *ret_obj;
68
69
  msAcquireLock(TLOCK_DEBUGOBJ);
70
71
  thread_id = msGetThreadId();
72
73
  /* find link for this thread */
74
75
  for (link = debuginfo_list;
76
       link != NULL && link->thread_id != thread_id && link->next != NULL &&
77
       link->next->thread_id != thread_id;
78
       link = link->next) {
79
  }
80
81
  /* If the target thread link is already at the head of the list were ok */
82
  if (debuginfo_list != NULL && debuginfo_list->thread_id == thread_id) {
83
  }
84
85
  /* We don't have one ... initialize one. */
86
  else if (link == NULL || link->next == NULL) {
87
    debugInfoObj *new_link;
88
89
    new_link = (debugInfoObj *)msSmallMalloc(sizeof(debugInfoObj));
90
    new_link->next = debuginfo_list;
91
    new_link->thread_id = thread_id;
92
    new_link->global_debug_level = MS_DEBUGLEVEL_ERRORSONLY;
93
    new_link->debug_mode = MS_DEBUGMODE_OFF;
94
    new_link->errorfile = NULL;
95
    new_link->fp = NULL;
96
    debuginfo_list = new_link;
97
  }
98
99
  /* If the link is not already at the head of the list, promote it */
100
  else {
101
    debugInfoObj *target = link->next;
102
103
    link->next = link->next->next;
104
    target->next = debuginfo_list;
105
    debuginfo_list = target;
106
  }
107
108
  ret_obj = debuginfo_list;
109
110
  msReleaseLock(TLOCK_DEBUGOBJ);
111
112
  return ret_obj;
113
}
114
#endif
115
116
/* msSetErrorFile()
117
**
118
** Set output target, ready to write to it, open file if necessary
119
**
120
** If pszRelToPath != NULL then we will try to make the value relative to
121
** this path if it is not absolute already and it's not one of the special
122
** values (stderr, stdout, windowsdebug)
123
**
124
** Returns MS_SUCCESS/MS_FAILURE
125
*/
126
340
int msSetErrorFile(const char *pszErrorFile, const char *pszRelToPath) {
127
340
  char extended_path[MS_MAXPATHLEN];
128
340
  debugInfoObj *debuginfo = msGetDebugInfoObj();
129
130
340
  if (strcmp(pszErrorFile, "stderr") != 0 &&
131
340
      strcmp(pszErrorFile, "stdout") != 0 &&
132
340
      strcmp(pszErrorFile, "windowsdebug") != 0) {
133
    /* Try to make the path relative */
134
223
    if (msBuildPath(extended_path, pszRelToPath, pszErrorFile) == NULL)
135
1
      return MS_FAILURE;
136
222
    pszErrorFile = extended_path;
137
222
  }
138
139
339
  if (debuginfo->errorfile && pszErrorFile &&
140
339
      strcmp(debuginfo->errorfile, pszErrorFile) == 0) {
141
    /* Nothing to do, already writing to the right place */
142
170
    return MS_SUCCESS;
143
170
  }
144
145
  /* Close current output file if any */
146
169
  msCloseErrorFile();
147
148
  /* NULL or empty target will just close current output and return */
149
169
  if (pszErrorFile == NULL || *pszErrorFile == '\0')
150
0
    return MS_SUCCESS;
151
152
169
  if (strcmp(pszErrorFile, "stderr") == 0) {
153
44
    debuginfo->fp = stderr;
154
44
    debuginfo->errorfile = msStrdup(pszErrorFile);
155
44
    debuginfo->debug_mode = MS_DEBUGMODE_STDERR;
156
125
  } else if (strcmp(pszErrorFile, "stdout") == 0) {
157
27
    debuginfo->fp = stdout;
158
27
    debuginfo->errorfile = msStrdup(pszErrorFile);
159
27
    debuginfo->debug_mode = MS_DEBUGMODE_STDOUT;
160
98
  } else if (strcmp(pszErrorFile, "windowsdebug") == 0) {
161
#ifdef _WIN32
162
    debuginfo->errorfile = msStrdup(pszErrorFile);
163
    debuginfo->fp = NULL;
164
    debuginfo->debug_mode = MS_DEBUGMODE_WINDOWSDEBUG;
165
#else
166
0
    msSetError(
167
0
        MS_MISCERR,
168
0
        "'MS_ERRORFILE windowsdebug' is available only on Windows platforms.",
169
0
        "msSetErrorFile()");
170
0
    return MS_FAILURE;
171
0
#endif
172
98
  } else {
173
98
    debuginfo->fp = fopen(pszErrorFile, "a");
174
98
    if (debuginfo->fp == NULL) {
175
3
      msSetError(MS_MISCERR, "Failed to open MS_ERRORFILE %s",
176
3
                 "msSetErrorFile()", pszErrorFile);
177
3
      return MS_FAILURE;
178
3
    }
179
95
    debuginfo->errorfile = msStrdup(pszErrorFile);
180
95
    debuginfo->debug_mode = MS_DEBUGMODE_FILE;
181
95
  }
182
183
166
  return MS_SUCCESS;
184
169
}
185
186
/* msCloseErrorFile()
187
**
188
** Close current output file (if one is open) and reset related members
189
*/
190
169
void msCloseErrorFile() {
191
169
  debugInfoObj *debuginfo = msGetDebugInfoObj();
192
193
169
  if (debuginfo && debuginfo->debug_mode != MS_DEBUGMODE_OFF) {
194
165
    if (debuginfo->fp && debuginfo->debug_mode == MS_DEBUGMODE_FILE)
195
94
      fclose(debuginfo->fp);
196
197
165
    if (debuginfo->fp && (debuginfo->debug_mode == MS_DEBUGMODE_STDERR ||
198
165
                          debuginfo->debug_mode == MS_DEBUGMODE_STDOUT))
199
71
      fflush(debuginfo->fp); /* just flush stderr or stdout */
200
201
165
    debuginfo->fp = NULL;
202
203
165
    msFree(debuginfo->errorfile);
204
165
    debuginfo->errorfile = NULL;
205
206
165
    debuginfo->debug_mode = MS_DEBUGMODE_OFF;
207
165
  }
208
169
}
209
210
/* msGetErrorFile()
211
**
212
** Returns name of current error file
213
**
214
** Returns NULL if not set.
215
*/
216
0
const char *msGetErrorFile() {
217
0
  debugInfoObj *debuginfo = msGetDebugInfoObj();
218
219
0
  if (debuginfo)
220
0
    return debuginfo->errorfile;
221
222
0
  return NULL;
223
0
}
224
225
/* Set/Get the current global debug level value, used as default value for
226
** new map and layer objects and to control msDebug() calls outside of
227
** the context of mapObj or layerObj.
228
**
229
*/
230
0
void msSetGlobalDebugLevel(int level) {
231
0
  debugInfoObj *debuginfo = msGetDebugInfoObj();
232
233
0
  if (debuginfo)
234
0
    debuginfo->global_debug_level = (debugLevel)level;
235
0
}
236
237
64.8k
debugLevel msGetGlobalDebugLevel() {
238
64.8k
  debugInfoObj *debuginfo = msGetDebugInfoObj();
239
240
64.8k
  if (debuginfo)
241
64.8k
    return debuginfo->global_debug_level;
242
243
0
  return MS_DEBUGLEVEL_ERRORSONLY;
244
64.8k
}
245
246
/* msDebugInitFromEnv()
247
**
248
** Init debug state from MS_ERRORFILE and MS_DEBUGLEVEL env vars if set
249
**
250
** Returns MS_SUCCESS/MS_FAILURE
251
*/
252
0
int msDebugInitFromEnv() {
253
0
  const char *val;
254
255
0
  if ((val = CPLGetConfigOption("MS_ERRORFILE", NULL)) != NULL) {
256
0
    if (msSetErrorFile(val, NULL) != MS_SUCCESS)
257
0
      return MS_FAILURE;
258
0
  }
259
260
0
  if ((val = CPLGetConfigOption("MS_DEBUGLEVEL", NULL)) != NULL)
261
0
    msSetGlobalDebugLevel(atoi(val));
262
263
0
  return MS_SUCCESS;
264
0
}
265
266
/* msDebugCleanup()
267
**
268
** Called by msCleanup to remove info related to this thread.
269
*/
270
0
void msDebugCleanup() {
271
  /* make sure file is closed */
272
0
  msCloseErrorFile();
273
274
#ifdef USE_THREAD
275
  {
276
    void *thread_id = msGetThreadId();
277
    debugInfoObj *link;
278
279
    msAcquireLock(TLOCK_DEBUGOBJ);
280
281
    /* find link for this thread */
282
283
    for (link = debuginfo_list;
284
         link != NULL && link->thread_id != thread_id && link->next != NULL &&
285
         link->next->thread_id != thread_id;
286
         link = link->next) {
287
    }
288
289
    if (link->thread_id == thread_id) {
290
      /* presumably link is at head of list.  */
291
      if (debuginfo_list == link)
292
        debuginfo_list = link->next;
293
294
      free(link);
295
    } else if (link->next != NULL && link->next->thread_id == thread_id) {
296
      debugInfoObj *next_link = link->next;
297
      link->next = link->next->next;
298
      free(next_link);
299
    }
300
    msReleaseLock(TLOCK_DEBUGOBJ);
301
  }
302
#endif
303
0
}
304
305
/* msDebug()
306
**
307
** Outputs/logs messages to the MS_ERRORFILE if one is set
308
** (see msSetErrorFile())
309
**
310
*/
311
19.2k
void msDebug(const char *pszFormat, ...) {
312
19.2k
  va_list args;
313
19.2k
  debugInfoObj *debuginfo = msGetDebugInfoObj();
314
19.2k
  char szMessage[MESSAGELENGTH];
315
316
19.2k
  if (debuginfo == NULL || debuginfo->debug_mode == MS_DEBUGMODE_OFF)
317
4.97k
    return; /* Don't waste time here! */
318
319
14.2k
  va_start(args, pszFormat);
320
14.2k
  vsnprintf(szMessage, MESSAGELENGTH, pszFormat, args);
321
14.2k
  va_end(args);
322
14.2k
  szMessage[MESSAGELENGTH - 1] = '\0';
323
324
14.2k
  msRedactCredentials(szMessage);
325
326
14.2k
  if (debuginfo->fp) {
327
    /* Writing to a stdio file handle */
328
329
#if defined(USE_FASTCGI)
330
    /* It seems the FastCGI stuff inserts a timestamp anyways, so  */
331
    /* we might as well skip this one if writing to stderr w/ FastCGI. */
332
    if (debuginfo->debug_mode != MS_DEBUGMODE_STDERR)
333
#endif
334
14.2k
    {
335
14.2k
      struct mstimeval tv;
336
14.2k
      time_t t;
337
14.2k
      msGettimeofday(&tv, NULL);
338
14.2k
      t = tv.tv_sec;
339
14.2k
      msIO_fprintf(debuginfo->fp, "[%s].%ld ", msStringChop(ctime(&t)),
340
14.2k
                   (long)tv.tv_usec);
341
14.2k
    }
342
343
14.2k
    msIO_fprintf(debuginfo->fp, "%s", szMessage);
344
14.2k
  }
345
#ifdef _WIN32
346
  else if (debuginfo->debug_mode == MS_DEBUGMODE_WINDOWSDEBUG) {
347
    /* Writing to Windows Debug Console */
348
    OutputDebugStringA(szMessage);
349
  }
350
#endif
351
14.2k
}
352
353
/* msDebug2()
354
**
355
** Variadic function with no operation
356
**
357
*/
358
0
void msDebug2(int level, ...) { (void)level; }