• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MapServer / MapServer / 25792522046

13 May 2026 10:09AM UTC coverage: 42.438% (+0.001%) from 42.437%
25792522046

Pull #7504

github

web-flow
Merge 25359af0a into da86577a3
Pull Request #7504: mapserv: exit FastCGI loop promptly on SIGTERM/SIGUSR1

7 of 8 new or added lines in 1 file covered. (87.5%)

1 existing line in 1 file now uncovered.

64638 of 152310 relevant lines covered (42.44%)

27395.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.43
/src/apps/mapserv.c
1
/******************************************************************************
2
 * $id: mapserv.c 9470 2009-10-16 16:09:31Z sdlime $
3
 *
4
 * Project:  MapServer
5
 * Purpose:  MapServer CGI mainline.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2005 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
#ifndef _XOPEN_SOURCE
31
#define _XOPEN_SOURCE 500 // for putenv
32
#endif
33

34
#include "mapserver-config.h"
35
#include <stdbool.h>
36
#include <stdlib.h>
37

38
#ifdef USE_FASTCGI
39
#define NO_FCGI_DEFINES
40
#include "fcgi_stdio.h"
41
#endif
42

43
#include "mapserv.h"
44
#include "../mapio.h"
45
#include "../maptime.h"
46

47
#include "cpl_conv.h"
48

49
#ifndef _WIN32
50
#include <signal.h>
51
#endif
52

53
/************************************************************************/
54
/*                      FastCGI cleanup functions.                      */
55
/************************************************************************/
56

57
static int finish_process = 0;
58

59
#ifndef _WIN32
60
static void msCleanupOnSignal(int nInData) {
×
61
  (void)nInData;
62
  finish_process = 1;
×
63
#ifdef USE_FASTCGI
64
  /* Ask libfcgi to stop accepting new requests. This causes a blocked
65
   * FCGI_Accept() to return -1, so the main loop can exit promptly
66
   * instead of waiting for the next request to arrive. Safe to call
67
   * from a signal handler: it just sets an internal flag (declared in
68
   * fcgiapp.h, which is pulled in via fcgi_stdio.h above). */
NEW
69
  FCGX_ShutdownPending();
×
70
#endif
UNCOV
71
}
×
72
#endif
73

74
#ifdef _WIN32
75
static void msCleanupOnExit(void) { finish_process = 1; }
76
#endif
77

78
#ifdef USE_FASTCGI
79

80
/************************************************************************/
81
/*                           msIO_fcgiRead()                            */
82
/*                                                                      */
83
/*      This is the default implementation via stdio.                   */
84
/************************************************************************/
85

86
static int msIO_fcgiRead(void *cbData, void *data, int byteCount)
160✔
87

88
{
89
  return (int)FCGI_fread(data, 1, byteCount, (FCGI_FILE *)cbData);
160✔
90
}
91

92
/************************************************************************/
93
/*                           msIO_fcgiWrite()                           */
94
/*                                                                      */
95
/*      This is the default implementation via stdio.                   */
96
/************************************************************************/
97

98
static int msIO_fcgiWrite(void *cbData, void *data, int byteCount)
197,976✔
99

100
{
101
  return (int)FCGI_fwrite(data, 1, byteCount, (FCGI_FILE *)cbData);
197,976✔
102
}
103

104
/************************************************************************/
105
/*                    msIO_installFastCGIRedirect()                     */
106
/************************************************************************/
107
static int msIO_installFastCGIRedirect()
2,209✔
108

109
{
110
  msIOContext stdin_ctx, stdout_ctx, stderr_ctx;
111

112
  stdin_ctx.label = "fcgi";
2,209✔
113
  stdin_ctx.write_channel = MS_FALSE;
2,209✔
114
  stdin_ctx.readWriteFunc = msIO_fcgiRead;
2,209✔
115
  stdin_ctx.cbData = (void *)FCGI_stdin;
2,209✔
116

117
  stdout_ctx.label = "fcgi";
2,209✔
118
  stdout_ctx.write_channel = MS_TRUE;
2,209✔
119
  stdout_ctx.readWriteFunc = msIO_fcgiWrite;
2,209✔
120
  stdout_ctx.cbData = (void *)FCGI_stdout;
2,209✔
121

122
  stderr_ctx.label = "fcgi";
2,209✔
123
  stderr_ctx.write_channel = MS_TRUE;
2,209✔
124
  stderr_ctx.readWriteFunc = msIO_fcgiWrite;
2,209✔
125
  stderr_ctx.cbData = (void *)FCGI_stderr;
2,209✔
126

127
  msIO_installHandlers(&stdin_ctx, &stdout_ctx, &stderr_ctx);
2,209✔
128

129
  return MS_TRUE;
2,209✔
130
}
131

132
#endif
133
/************************************************************************/
134
/*                                main()                                */
135
/************************************************************************/
136
int main(int argc, char *argv[]) {
4,414✔
137
  int sendheaders = MS_TRUE;
138
  struct mstimeval execstarttime, execendtime;
139
  struct mstimeval requeststarttime, requestendtime;
140
  mapservObj *mapserv = NULL;
141
  configObj *config = NULL;
142

143
  /*
144
  ** Process -v and -h command line arguments  first end exit. We want to avoid
145
  *any error messages
146
  ** associated with msLoadConfig() or msSetup().
147
  */
148
  const char *config_filename = NULL;
149
  const bool use_command_line_options = getenv("QUERY_STRING") == NULL;
2,209✔
150
  if (use_command_line_options) {
2,209✔
151
    /* WARNING:
152
     * Do not parse command line arguments (especially those that could have
153
     * dangerous consequences if controlled through a web request), without
154
     * checking that the QUERY_STRING environment variable is *not* set, because
155
     * in a CGI context, command line arguments can be generated from the
156
     * content of the QUERY_STRING, and thus cause a security problem. For ex,
157
     * "http://example.com/mapserv.cgi?-conf+bar would result in "mapserv.cgi
158
     * -conf bar" being invoked. See
159
     * https://github.com/MapServer/MapServer/pull/6429#issuecomment-952533589
160
     * and https://datatracker.ietf.org/doc/html/rfc3875#section-4.4
161
     */
162
    for (int iArg = 1; iArg < argc; iArg++) {
4,536✔
163
      if (strcmp(argv[iArg], "-v") == 0) {
2,327✔
164
        printf("%s\n", msGetVersion());
×
165
        fflush(stdout);
×
166
        exit(0);
×
167
      } else if (strcmp(argv[iArg], "-h") == 0 ||
2,327✔
168
                 strcmp(argv[iArg], "--help") == 0) {
2,327✔
169
        printf("Usage: mapserv [--help] [-v] [-nh] [QUERY_STRING=value] "
170
               "[PATH_INFO=value]\n");
171
        printf("                [-conf filename]\n");
172
        printf("\n");
173
        printf("Options :\n");
174
        printf("  -h, --help              Display this help message.\n");
175
        printf("  -v                      Display version and exit.\n");
176
        printf(
177
            "  -nh                     Suppress HTTP headers in CGI mode.\n");
178
        printf("  -conf filename          Filename of the MapServer "
179
               "configuration file.\n");
180
        printf("  QUERY_STRING=value      Set the QUERY_STRING in GET request "
181
               "mode.\n");
182
        printf("  PATH_INFO=value         Set the PATH_INFO for an API "
183
               "request.\n");
184
        fflush(stdout);
×
185
        exit(0);
×
186
      } else if (iArg < argc - 1 && strcmp(argv[iArg], "-conf") == 0) {
2,327✔
187
        config_filename = argv[iArg + 1];
44✔
188
        ++iArg;
44✔
189
      }
190
    }
191
  }
192

193
  config = msLoadConfig(config_filename); // first thing
2,209✔
194
  if (config == NULL) {
2,209✔
195
#ifdef USE_FASTCGI
196
    msIO_installFastCGIRedirect(); // FastCGI setup for error handling here
4✔
197
    FCGI_Accept();
4✔
198
#endif
199
    msCGIWriteError(mapserv);
4✔
200
    exit(0);
4✔
201
  }
202

203
  /* -------------------------------------------------------------------- */
204
  /*      Initialize mapserver.  This sets up threads, GD and GEOS as     */
205
  /*      well as using MS_ERRORFILE and MS_DEBUGLEVEL env vars if set.   */
206
  /* -------------------------------------------------------------------- */
207
  if (msSetup() != MS_SUCCESS) {
2,205✔
208
#ifdef USE_FASTCGI
209
    msIO_installFastCGIRedirect(); // FastCGI setup for error handling here
×
210
    FCGI_Accept();
×
211
#endif
212
    msCGIWriteError(mapserv);
×
213
    msCleanup();
×
214
    msFreeConfig(config);
×
215
    exit(0);
×
216
  }
217

218
  const char *ms_index_dir =
219
      msConfigGetEnv(config, "MS_INDEX_TEMPLATE_DIRECTORY");
2,205✔
220

221
  if (msGetGlobalDebugLevel() >= MS_DEBUGLEVEL_TUNING)
2,205✔
222
    msGettimeofday(&execstarttime, NULL);
×
223

224
  /* -------------------------------------------------------------------- */
225
  /*      Process arguments. In normal use as a cgi-bin there are no      */
226
  /*      commandline switches, but we provide a few for test/debug       */
227
  /*      purposes.                                                       */
228
  /* -------------------------------------------------------------------- */
229
  if (use_command_line_options) {
2,205✔
230
    for (int iArg = 1; iArg < argc; iArg++) {
4,564✔
231
      if (strcmp(argv[iArg], "-nh") == 0) {
2,359✔
232
        sendheaders = MS_FALSE;
233
        msIO_setHeaderEnabled(MS_FALSE);
×
234
      } else if (strncmp(argv[iArg], "QUERY_STRING=", 13) == 0) {
2,359✔
235
        /* Debugging hook... pass "QUERY_STRING=..." on the command-line */
236
        putenv("REQUEST_METHOD=GET");
2,044✔
237
        /* coverity[tainted_string] */
238
        putenv(argv[iArg]);
2,044✔
239
      } else if (strncmp(argv[iArg], "PATH_INFO=", 10) == 0) {
315✔
240
        /* Debugging hook for APIs... pass "PATH_INFO=..." on the command-line
241
         */
242
        putenv("REQUEST_METHOD=GET");
184✔
243
        /* coverity[tainted_string] */
244
        putenv(argv[iArg]);
184✔
245
      }
246
    }
247
  }
248

249
  /* -------------------------------------------------------------------- */
250
  /*      Setup cleanup magic, mainly for FastCGI case.                   */
251
  /* -------------------------------------------------------------------- */
252
#ifndef _WIN32
253
  {
254
    /* Install via sigaction without SA_RESTART so that a blocking
255
     * accept()/select() inside FCGI_Accept() returns EINTR when the
256
     * signal arrives while the process is idle. Combined with
257
     * FCGX_ShutdownPending() in the handler, this lets the FastCGI
258
     * loop exit on SIGTERM instead of waiting for the next request. */
259
    struct sigaction sa;
260
    memset(&sa, 0, sizeof(sa));
261
    sa.sa_handler = msCleanupOnSignal;
2,205✔
262
    sigemptyset(&sa.sa_mask);
2,205✔
263
    sigaddset(&sa.sa_mask, SIGUSR1);
2,205✔
264
    sigaddset(&sa.sa_mask, SIGTERM);
2,205✔
265
    sa.sa_flags = 0; /* deliberately no SA_RESTART */
2,205✔
266
    sigaction(SIGUSR1, &sa, NULL);
2,205✔
267
    sigaction(SIGTERM, &sa, NULL);
2,205✔
268
  }
269
#endif
270

271
#ifdef USE_FASTCGI
272
  msIO_installFastCGIRedirect();
2,205✔
273

274
  /* In FastCGI case we loop accepting multiple requests.  In normal CGI */
275
  /* use we only accept and process one request.  */
276
  while (!finish_process && FCGI_Accept() >= 0) {
4,410✔
277
#endif /* def USE_FASTCGI */
278

279
    /* -------------------------------------------------------------------- */
280
    /*      Process a request.                                              */
281
    /* -------------------------------------------------------------------- */
282
    mapserv = msAllocMapServObj();
2,205✔
283
    mapserv->sendheaders = sendheaders; /* override the default if necessary
2,205✔
284
                                           (via command line -nh switch) */
285

286
    mapserv->request->NumParams =
2,205✔
287
        loadParams(mapserv->request, NULL, NULL, 0, NULL);
2,205✔
288

289
    if (msCGIIsAPIRequest(mapserv) == MS_FALSE &&
2,205✔
290
        mapserv->request->NumParams == -1) { /* no QUERY_STRING or PATH_INFO */
2,023✔
291
      if (ms_index_dir != NULL) {
1✔
292
        // return the landing page
293
        msCGIDispatchIndexRequest(mapserv, config);
×
294
        goto end_request;
×
295
      }
296
      msCGIWriteError(mapserv);
1✔
297
      goto end_request;
1✔
298
    }
299

300
    mapserv->map = msCGILoadMap(mapserv, config);
2,204✔
301
    if (!mapserv->map) {
2,204✔
302
      for (int i = 0; i < mapserv->request->NumParams;
25✔
303
           i++) { /* a map parameter was sent */
6✔
304
        if (strcasecmp(mapserv->request->ParamNames[i], "map") == 0) {
19✔
305
          msCGIWriteError(mapserv);
13✔
306
          goto end_request;
13✔
307
        }
308
      }
309
      if (ms_index_dir != NULL && mapserv->request->path_info != NULL &&
6✔
310
          strcmp(mapserv->request->path_info, "/") == 0) {
4✔
311
        // return the landing page
312
        msCGIDispatchIndexRequest(mapserv, config);
2✔
313
        goto end_request;
2✔
314
      }
315
      msCGIWriteError(mapserv);
4✔
316
      goto end_request;
4✔
317
    }
318

319
    if (mapserv->map->debug >= MS_DEBUGLEVEL_TUNING)
2,185✔
320
      msGettimeofday(&requeststarttime, NULL);
3✔
321

322
#ifdef USE_FASTCGI
323
    if (mapserv->map->debug) {
2,185✔
324
      static int nRequestCounter = 1;
325

326
      msDebug("CGI Request %d on process %d\n", nRequestCounter, getpid());
45✔
327
      nRequestCounter++;
45✔
328
    }
329
#endif
330

331
    if (mapserv->request->api_path != NULL) {
2,185✔
332
      switch (mapserv->request->api_path_length) {
179✔
333
      case 0: // just in case, treat as normal
×
334
        if (msCGIDispatchRequest(mapserv) != MS_SUCCESS) {
×
335
          msCGIWriteError(mapserv);
×
336
          goto end_request;
×
337
        }
338
        break;
339

340
      case 1:
21✔
341
        if (ms_index_dir != NULL) {
21✔
342
          // Try normal dispatch first
343
          if (msCGIDispatchRequest(mapserv) != MS_SUCCESS) {
17✔
344
            // Fallback to landing page
345
            if (msCGIDispatchMapIndexRequest(mapserv, config) != MS_SUCCESS) {
13✔
346
              msCGIWriteError(mapserv);
×
347
              goto end_request;
×
348
            }
349
          }
350
        } else {
351
          if (msCGIDispatchRequest(mapserv) != MS_SUCCESS) {
4✔
352
            msCGIWriteError(mapserv);
2✔
353
            goto end_request;
2✔
354
          }
355
        }
356
        break;
357

358
      default: // length > 1
158✔
359
        if (msCGIDispatchAPIRequest(mapserv) != MS_SUCCESS) {
158✔
360
          msCGIWriteError(mapserv);
2✔
361
          goto end_request;
2✔
362
        }
363
        break;
364
      }
365
    } else {
366
      // api_path == NULL
367
      if (msCGIDispatchRequest(mapserv) != MS_SUCCESS) {
2,006✔
368
        msCGIWriteError(mapserv);
276✔
369
        goto end_request;
276✔
370
      }
371
    }
372

373
  end_request:
1,730✔
374
    if (mapserv->map && mapserv->map->debug >= MS_DEBUGLEVEL_TUNING) {
2,205✔
375
      msGettimeofday(&requestendtime, NULL);
3✔
376
      msDebug("mapserv request processing time (msLoadMap not incl.): %.3fs\n",
3✔
377
              (requestendtime.tv_sec + requestendtime.tv_usec / 1.0e6) -
3✔
378
                  (requeststarttime.tv_sec + requeststarttime.tv_usec / 1.0e6));
3✔
379
    }
380
    msFreeMapServObj(mapserv);
2,205✔
381
#ifdef USE_FASTCGI
382
    /* FCGI_ --- return to top of loop */
383
    msResetErrorList();
2,205✔
384
    continue;
2,205✔
385
  } /* end fastcgi loop */
386
#endif
387

388
  /* normal case, processing is complete */
389
  if (msGetGlobalDebugLevel() >= MS_DEBUGLEVEL_TUNING) {
2,205✔
390
    msGettimeofday(&execendtime, NULL);
×
391
    msDebug("mapserv total execution time: %.3fs\n",
×
392
            (execendtime.tv_sec + execendtime.tv_usec / 1.0e6) -
×
393
                (execstarttime.tv_sec + execstarttime.tv_usec / 1.0e6));
×
394
  }
395
  msCleanup();
2,205✔
396
  msFreeConfig(config);
2,205✔
397

398
#ifdef _WIN32
399
  /* flush pending writes to stdout */
400
  fflush(stdout);
401
#endif
402

403
  exit(0);
2,205✔
404
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc