~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/generators/mod_cgi.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
 
2
 * contributor license agreements.  See the NOTICE file distributed with
 
3
 * this work for additional information regarding copyright ownership.
 
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
 
5
 * (the "License"); you may not use this file except in compliance with
 
6
 * the License.  You may obtain a copy of the License at
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS IS" BASIS,
 
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
 
 
17
/*
 
18
 * http_script: keeps all script-related ramblings together.
 
19
 *
 
20
 * Compliant to CGI/1.1 spec
 
21
 *
 
22
 * Adapted by rst from original NCSA code by Rob McCool
 
23
 *
 
24
 * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for
 
25
 * custom error responses, and DOCUMENT_ROOT because we found it useful.
 
26
 * It also adds SERVER_ADMIN - useful for scripts to know who to mail when
 
27
 * they fail.
 
28
 */
 
29
 
 
30
#include "apr.h"
 
31
#include "apr_strings.h"
 
32
#include "apr_thread_proc.h"    /* for RLIMIT stuff */
 
33
#include "apr_optional.h"
 
34
#include "apr_buckets.h"
 
35
#include "apr_lib.h"
 
36
#include "apr_poll.h"
 
37
 
 
38
#define APR_WANT_STRFUNC
 
39
#define APR_WANT_MEMFUNC
 
40
#include "apr_want.h"
 
41
 
 
42
#define CORE_PRIVATE
 
43
 
 
44
#include "util_filter.h"
 
45
#include "ap_config.h"
 
46
#include "httpd.h"
 
47
#include "http_config.h"
 
48
#include "http_request.h"
 
49
#include "http_core.h"
 
50
#include "http_protocol.h"
 
51
#include "http_main.h"
 
52
#include "http_log.h"
 
53
#include "util_script.h"
 
54
#include "ap_mpm.h"
 
55
#include "mod_core.h"
 
56
#include "mod_cgi.h"
 
57
 
 
58
module AP_MODULE_DECLARE_DATA cgi_module;
 
59
 
 
60
static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgi_pfn_reg_with_ssi;
 
61
static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgi_pfn_gtv;
 
62
static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgi_pfn_ps;
 
63
static APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
 
64
 
 
65
/* Read and discard the data in the brigade produced by a CGI script */
 
66
static void discard_script_output(apr_bucket_brigade *bb);
 
67
 
 
68
/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
 
69
 * in ScriptAliased directories, which means we need to know if this
 
70
 * request came through ScriptAlias or not... so the Alias module
 
71
 * leaves a note for us.
 
72
 */
 
73
 
 
74
static int is_scriptaliased(request_rec *r)
 
75
{
 
76
    const char *t = apr_table_get(r->notes, "alias-forced-type");
 
77
    return t && (!strcasecmp(t, "cgi-script"));
 
78
}
 
79
 
 
80
/* Configuration stuff */
 
81
 
 
82
#define DEFAULT_LOGBYTES 10385760
 
83
#define DEFAULT_BUFBYTES 1024
 
84
 
 
85
typedef struct {
 
86
    const char *logname;
 
87
    long        logbytes;
 
88
    apr_size_t  bufbytes;
 
89
} cgi_server_conf;
 
90
 
 
91
static void *create_cgi_config(apr_pool_t *p, server_rec *s)
 
92
{
 
93
    cgi_server_conf *c =
 
94
    (cgi_server_conf *) apr_pcalloc(p, sizeof(cgi_server_conf));
 
95
 
 
96
    c->logname = NULL;
 
97
    c->logbytes = DEFAULT_LOGBYTES;
 
98
    c->bufbytes = DEFAULT_BUFBYTES;
 
99
 
 
100
    return c;
 
101
}
 
102
 
 
103
static void *merge_cgi_config(apr_pool_t *p, void *basev, void *overridesv)
 
104
{
 
105
    cgi_server_conf *base = (cgi_server_conf *) basev,
 
106
                    *overrides = (cgi_server_conf *) overridesv;
 
107
 
 
108
    return overrides->logname ? overrides : base;
 
109
}
 
110
 
 
111
static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg)
 
112
{
 
113
    server_rec *s = cmd->server;
 
114
    cgi_server_conf *conf = ap_get_module_config(s->module_config,
 
115
                                                 &cgi_module);
 
116
 
 
117
    conf->logname = ap_server_root_relative(cmd->pool, arg);
 
118
 
 
119
    if (!conf->logname) {
 
120
        return apr_pstrcat(cmd->pool, "Invalid ScriptLog path ",
 
121
                           arg, NULL);
 
122
    }
 
123
 
 
124
    return NULL;
 
125
}
 
126
 
 
127
static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy,
 
128
                                        const char *arg)
 
129
{
 
130
    server_rec *s = cmd->server;
 
131
    cgi_server_conf *conf = ap_get_module_config(s->module_config,
 
132
                                                 &cgi_module);
 
133
 
 
134
    conf->logbytes = atol(arg);
 
135
    return NULL;
 
136
}
 
137
 
 
138
static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy,
 
139
                                        const char *arg)
 
140
{
 
141
    server_rec *s = cmd->server;
 
142
    cgi_server_conf *conf = ap_get_module_config(s->module_config,
 
143
                                                 &cgi_module);
 
144
 
 
145
    conf->bufbytes = atoi(arg);
 
146
    return NULL;
 
147
}
 
148
 
 
149
static const command_rec cgi_cmds[] =
 
150
{
 
151
AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF,
 
152
     "the name of a log for script debugging info"),
 
153
AP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF,
 
154
     "the maximum length (in bytes) of the script debug log"),
 
155
AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF,
 
156
     "the maximum size (in bytes) to record of a POST request"),
 
157
    {NULL}
 
158
};
 
159
 
 
160
static int log_scripterror(request_rec *r, cgi_server_conf * conf, int ret,
 
161
                           apr_status_t rv, char *error)
 
162
{
 
163
    apr_file_t *f = NULL;
 
164
    apr_finfo_t finfo;
 
165
    char time_str[APR_CTIME_LEN];
 
166
    int log_flags = rv ? APLOG_ERR : APLOG_ERR;
 
167
 
 
168
    ap_log_rerror(APLOG_MARK, log_flags, rv, r,
 
169
                  "%s: %s", error, r->filename);
 
170
 
 
171
    /* XXX Very expensive mainline case! Open, then getfileinfo! */
 
172
    if (!conf->logname ||
 
173
        ((apr_stat(&finfo, conf->logname,
 
174
                   APR_FINFO_SIZE, r->pool) == APR_SUCCESS) &&
 
175
         (finfo.size > conf->logbytes)) ||
 
176
        (apr_file_open(&f, conf->logname,
 
177
                       APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT,
 
178
                       r->pool) != APR_SUCCESS)) {
 
179
        return ret;
 
180
    }
 
181
 
 
182
    /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
 
183
    apr_ctime(time_str, apr_time_now());
 
184
    apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri,
 
185
                    r->args ? "?" : "", r->args ? r->args : "", r->protocol);
 
186
    /* "%% 500 /usr/local/apache/cgi-bin */
 
187
    apr_file_printf(f, "%%%% %d %s\n", ret, r->filename);
 
188
 
 
189
    apr_file_printf(f, "%%error\n%s\n", error);
 
190
 
 
191
    apr_file_close(f);
 
192
    return ret;
 
193
}
 
194
 
 
195
/* Soak up stderr from a script and redirect it to the error log.
 
196
 */
 
197
static apr_status_t log_script_err(request_rec *r, apr_file_t *script_err)
 
198
{
 
199
    char argsbuffer[HUGE_STRING_LEN];
 
200
    char *newline;
 
201
    apr_status_t rv;
 
202
 
 
203
    while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN,
 
204
                               script_err)) == APR_SUCCESS) {
 
205
        newline = strchr(argsbuffer, '\n');
 
206
        if (newline) {
 
207
            *newline = '\0';
 
208
        }
 
209
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
210
                      "%s", argsbuffer);
 
211
    }
 
212
 
 
213
    return rv;
 
214
}
 
215
 
 
216
static int log_script(request_rec *r, cgi_server_conf * conf, int ret,
 
217
                      char *dbuf, const char *sbuf, apr_bucket_brigade *bb,
 
218
                      apr_file_t *script_err)
 
219
{
 
220
    const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in);
 
221
    const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
 
222
    char argsbuffer[HUGE_STRING_LEN];
 
223
    apr_file_t *f = NULL;
 
224
    apr_bucket *e;
 
225
    const char *buf;
 
226
    apr_size_t len;
 
227
    apr_status_t rv;
 
228
    int first;
 
229
    int i;
 
230
    apr_finfo_t finfo;
 
231
    char time_str[APR_CTIME_LEN];
 
232
 
 
233
    /* XXX Very expensive mainline case! Open, then getfileinfo! */
 
234
    if (!conf->logname ||
 
235
        ((apr_stat(&finfo, conf->logname,
 
236
                   APR_FINFO_SIZE, r->pool) == APR_SUCCESS) &&
 
237
         (finfo.size > conf->logbytes)) ||
 
238
        (apr_file_open(&f, conf->logname,
 
239
                       APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT,
 
240
                       r->pool) != APR_SUCCESS)) {
 
241
        /* Soak up script output */
 
242
        discard_script_output(bb);
 
243
        log_script_err(r, script_err);
 
244
        return ret;
 
245
    }
 
246
 
 
247
    /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
 
248
    apr_ctime(time_str, apr_time_now());
 
249
    apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri,
 
250
                    r->args ? "?" : "", r->args ? r->args : "", r->protocol);
 
251
    /* "%% 500 /usr/local/apache/cgi-bin" */
 
252
    apr_file_printf(f, "%%%% %d %s\n", ret, r->filename);
 
253
 
 
254
    apr_file_puts("%request\n", f);
 
255
    for (i = 0; i < hdrs_arr->nelts; ++i) {
 
256
        if (!hdrs[i].key)
 
257
            continue;
 
258
        apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
 
259
    }
 
260
    if ((r->method_number == M_POST || r->method_number == M_PUT) &&
 
261
        *dbuf) {
 
262
        apr_file_printf(f, "\n%s\n", dbuf);
 
263
    }
 
264
 
 
265
    apr_file_puts("%response\n", f);
 
266
    hdrs_arr = apr_table_elts(r->err_headers_out);
 
267
    hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
 
268
 
 
269
    for (i = 0; i < hdrs_arr->nelts; ++i) {
 
270
        if (!hdrs[i].key)
 
271
            continue;
 
272
        apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
 
273
    }
 
274
 
 
275
    if (sbuf && *sbuf)
 
276
        apr_file_printf(f, "%s\n", sbuf);
 
277
 
 
278
    first = 1;
 
279
    for (e = APR_BRIGADE_FIRST(bb);
 
280
         e != APR_BRIGADE_SENTINEL(bb);
 
281
         e = APR_BUCKET_NEXT(e))
 
282
    {
 
283
        if (APR_BUCKET_IS_EOS(e)) {
 
284
            break;
 
285
        }
 
286
        rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
 
287
        if (rv != APR_SUCCESS || (len == 0)) {
 
288
            break;
 
289
        }
 
290
        if (first) {
 
291
            apr_file_puts("%stdout\n", f);
 
292
            first = 0;
 
293
        }
 
294
        apr_file_write(f, buf, &len);
 
295
        apr_file_puts("\n", f);
 
296
    }
 
297
 
 
298
    if (apr_file_gets(argsbuffer, HUGE_STRING_LEN, script_err) == APR_SUCCESS) {
 
299
        apr_file_puts("%stderr\n", f);
 
300
        apr_file_puts(argsbuffer, f);
 
301
        while (apr_file_gets(argsbuffer, HUGE_STRING_LEN,
 
302
                             script_err) == APR_SUCCESS) {
 
303
            apr_file_puts(argsbuffer, f);
 
304
        }
 
305
        apr_file_puts("\n", f);
 
306
    }
 
307
 
 
308
    apr_brigade_destroy(bb);
 
309
    apr_file_close(script_err);
 
310
 
 
311
    apr_file_close(f);
 
312
    return ret;
 
313
}
 
314
 
 
315
 
 
316
/* This is the special environment used for running the "exec cmd="
 
317
 *   variety of SSI directives.
 
318
 */
 
319
static void add_ssi_vars(request_rec *r)
 
320
{
 
321
    apr_table_t *e = r->subprocess_env;
 
322
 
 
323
    if (r->path_info && r->path_info[0] != '\0') {
 
324
        request_rec *pa_req;
 
325
 
 
326
        apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool,
 
327
                                                           r->path_info));
 
328
 
 
329
        pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info),
 
330
                                       r, NULL);
 
331
        if (pa_req->filename) {
 
332
            apr_table_setn(e, "PATH_TRANSLATED",
 
333
                           apr_pstrcat(r->pool, pa_req->filename,
 
334
                                       pa_req->path_info, NULL));
 
335
        }
 
336
        ap_destroy_sub_req(pa_req);
 
337
    }
 
338
 
 
339
    if (r->args) {
 
340
        char *arg_copy = apr_pstrdup(r->pool, r->args);
 
341
 
 
342
        apr_table_setn(e, "QUERY_STRING", r->args);
 
343
        ap_unescape_url(arg_copy);
 
344
        apr_table_setn(e, "QUERY_STRING_UNESCAPED",
 
345
                       ap_escape_shell_cmd(r->pool, arg_copy));
 
346
    }
 
347
}
 
348
 
 
349
static void cgi_child_errfn(apr_pool_t *pool, apr_status_t err,
 
350
                            const char *description)
 
351
{
 
352
    apr_file_t *stderr_log;
 
353
    char errbuf[200];
 
354
 
 
355
    apr_file_open_stderr(&stderr_log, pool);
 
356
    /* Escape the logged string because it may be something that
 
357
     * came in over the network.
 
358
     */
 
359
    apr_file_printf(stderr_log,
 
360
                    "(%d)%s: %s\n",
 
361
                    err,
 
362
                    apr_strerror(err, errbuf, sizeof(errbuf)),
 
363
#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
 
364
                    ap_escape_logitem(pool,
 
365
#endif
 
366
                    description
 
367
#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
 
368
                    )
 
369
#endif
 
370
                    );
 
371
}
 
372
 
 
373
static apr_status_t run_cgi_child(apr_file_t **script_out,
 
374
                                  apr_file_t **script_in,
 
375
                                  apr_file_t **script_err,
 
376
                                  const char *command,
 
377
                                  const char * const argv[],
 
378
                                  request_rec *r,
 
379
                                  apr_pool_t *p,
 
380
                                  cgi_exec_info_t *e_info)
 
381
{
 
382
    const char * const *env;
 
383
    apr_procattr_t *procattr;
 
384
    apr_proc_t *procnew;
 
385
    apr_status_t rc = APR_SUCCESS;
 
386
 
 
387
#if defined(RLIMIT_CPU)  || defined(RLIMIT_NPROC) || \
 
388
    defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
 
389
 
 
390
    core_dir_config *conf = ap_get_module_config(r->per_dir_config,
 
391
                                                 &core_module);
 
392
#endif
 
393
 
 
394
#ifdef DEBUG_CGI
 
395
#ifdef OS2
 
396
    /* Under OS/2 need to use device con. */
 
397
    FILE *dbg = fopen("con", "w");
 
398
#else
 
399
    FILE *dbg = fopen("/dev/tty", "w");
 
400
#endif
 
401
    int i;
 
402
#endif
 
403
 
 
404
    RAISE_SIGSTOP(CGI_CHILD);
 
405
#ifdef DEBUG_CGI
 
406
    fprintf(dbg, "Attempting to exec %s as CGI child (argv0 = %s)\n",
 
407
            r->filename, argv[0]);
 
408
#endif
 
409
 
 
410
    env = (const char * const *)ap_create_environment(p, r->subprocess_env);
 
411
 
 
412
#ifdef DEBUG_CGI
 
413
    fprintf(dbg, "Environment: \n");
 
414
    for (i = 0; env[i]; ++i)
 
415
        fprintf(dbg, "'%s'\n", env[i]);
 
416
#endif
 
417
 
 
418
    /* Transmute ourselves into the script.
 
419
     * NB only ISINDEX scripts get decoded arguments.
 
420
     */
 
421
    if (((rc = apr_procattr_create(&procattr, p)) != APR_SUCCESS) ||
 
422
        ((rc = apr_procattr_io_set(procattr,
 
423
                                   e_info->in_pipe,
 
424
                                   e_info->out_pipe,
 
425
                                   e_info->err_pipe)) != APR_SUCCESS) ||
 
426
        ((rc = apr_procattr_dir_set(procattr,
 
427
                        ap_make_dirstr_parent(r->pool,
 
428
                                              r->filename))) != APR_SUCCESS) ||
 
429
#ifdef RLIMIT_CPU
 
430
        ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_CPU,
 
431
                                      conf->limit_cpu)) != APR_SUCCESS) ||
 
432
#endif
 
433
#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
 
434
        ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_MEM,
 
435
                                      conf->limit_mem)) != APR_SUCCESS) ||
 
436
#endif
 
437
#ifdef RLIMIT_NPROC
 
438
        ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC,
 
439
                                      conf->limit_nproc)) != APR_SUCCESS) ||
 
440
#endif
 
441
        ((rc = apr_procattr_cmdtype_set(procattr,
 
442
                                        e_info->cmd_type)) != APR_SUCCESS) ||
 
443
 
 
444
        ((rc = apr_procattr_detach_set(procattr,
 
445
                                        e_info->detached)) != APR_SUCCESS) ||
 
446
        ((rc = apr_procattr_addrspace_set(procattr,
 
447
                                        e_info->addrspace)) != APR_SUCCESS) ||
 
448
        ((rc = apr_procattr_child_errfn_set(procattr, cgi_child_errfn)) != APR_SUCCESS)) {
 
449
        /* Something bad happened, tell the world. */
 
450
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
 
451
                      "couldn't set child process attributes: %s", r->filename);
 
452
    }
 
453
    else {
 
454
        procnew = apr_pcalloc(p, sizeof(*procnew));
 
455
        rc = ap_os_create_privileged_process(r, procnew, command, argv, env,
 
456
                                             procattr, p);
 
457
 
 
458
        if (rc != APR_SUCCESS) {
 
459
            /* Bad things happened. Everyone should have cleaned up. */
 
460
            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, rc, r,
 
461
                          "couldn't create child process: %d: %s", rc,
 
462
                          apr_filepath_name_get(r->filename));
 
463
        }
 
464
        else {
 
465
            apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
 
466
 
 
467
            *script_in = procnew->out;
 
468
            if (!*script_in)
 
469
                return APR_EBADF;
 
470
            apr_file_pipe_timeout_set(*script_in, r->server->timeout);
 
471
 
 
472
            if (e_info->prog_type == RUN_AS_CGI) {
 
473
                *script_out = procnew->in;
 
474
                if (!*script_out)
 
475
                    return APR_EBADF;
 
476
                apr_file_pipe_timeout_set(*script_out, r->server->timeout);
 
477
 
 
478
                *script_err = procnew->err;
 
479
                if (!*script_err)
 
480
                    return APR_EBADF;
 
481
                apr_file_pipe_timeout_set(*script_err, r->server->timeout);
 
482
            }
 
483
        }
 
484
    }
 
485
#ifdef DEBUG_CGI
 
486
    fclose(dbg);
 
487
#endif
 
488
    return (rc);
 
489
}
 
490
 
 
491
 
 
492
static apr_status_t default_build_command(const char **cmd, const char ***argv,
 
493
                                          request_rec *r, apr_pool_t *p,
 
494
                                          cgi_exec_info_t *e_info)
 
495
{
 
496
    int numwords, x, idx;
 
497
    char *w;
 
498
    const char *args = NULL;
 
499
 
 
500
    if (e_info->process_cgi) {
 
501
        *cmd = r->filename;
 
502
        /* Do not process r->args if they contain an '=' assignment
 
503
         */
 
504
        if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) {
 
505
            args = r->args;
 
506
        }
 
507
    }
 
508
 
 
509
    if (!args) {
 
510
        numwords = 1;
 
511
    }
 
512
    else {
 
513
        /* count the number of keywords */
 
514
        for (x = 0, numwords = 2; args[x]; x++) {
 
515
            if (args[x] == '+') {
 
516
                ++numwords;
 
517
            }
 
518
        }
 
519
    }
 
520
    /* Everything is - 1 to account for the first parameter
 
521
     * which is the program name.
 
522
     */
 
523
    if (numwords > APACHE_ARG_MAX - 1) {
 
524
        numwords = APACHE_ARG_MAX - 1;    /* Truncate args to prevent overrun */
 
525
    }
 
526
    *argv = apr_palloc(p, (numwords + 2) * sizeof(char *));
 
527
    (*argv)[0] = *cmd;
 
528
    for (x = 1, idx = 1; x < numwords; x++) {
 
529
        w = ap_getword_nulls(p, &args, '+');
 
530
        ap_unescape_url(w);
 
531
        (*argv)[idx++] = ap_escape_shell_cmd(p, w);
 
532
    }
 
533
    (*argv)[idx] = NULL;
 
534
 
 
535
    return APR_SUCCESS;
 
536
}
 
537
 
 
538
static void discard_script_output(apr_bucket_brigade *bb)
 
539
{
 
540
    apr_bucket *e;
 
541
    const char *buf;
 
542
    apr_size_t len;
 
543
    apr_status_t rv;
 
544
 
 
545
    for (e = APR_BRIGADE_FIRST(bb);
 
546
         e != APR_BRIGADE_SENTINEL(bb);
 
547
         e = APR_BUCKET_NEXT(e))
 
548
    {
 
549
        if (APR_BUCKET_IS_EOS(e)) {
 
550
            break;
 
551
        }
 
552
        rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
 
553
        if (rv != APR_SUCCESS) {
 
554
            break;
 
555
        }
 
556
    }
 
557
}
 
558
 
 
559
#if APR_FILES_AS_SOCKETS
 
560
 
 
561
/* A CGI bucket type is needed to catch any output to stderr from the
 
562
 * script; see PR 22030. */
 
563
static const apr_bucket_type_t bucket_type_cgi;
 
564
 
 
565
struct cgi_bucket_data {
 
566
    apr_pollset_t *pollset;
 
567
    request_rec *r;
 
568
};
 
569
 
 
570
/* Create a CGI bucket using pipes from script stdout 'out'
 
571
 * and stderr 'err', for request 'r'. */
 
572
static apr_bucket *cgi_bucket_create(request_rec *r,
 
573
                                     apr_file_t *out, apr_file_t *err,
 
574
                                     apr_bucket_alloc_t *list)
 
575
{
 
576
    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
 
577
    apr_status_t rv;
 
578
    apr_pollfd_t fd;
 
579
    struct cgi_bucket_data *data = apr_palloc(r->pool, sizeof *data);
 
580
 
 
581
    APR_BUCKET_INIT(b);
 
582
    b->free = apr_bucket_free;
 
583
    b->list = list;
 
584
    b->type = &bucket_type_cgi;
 
585
    b->length = (apr_size_t)(-1);
 
586
    b->start = -1;
 
587
 
 
588
    /* Create the pollset */
 
589
    rv = apr_pollset_create(&data->pollset, 2, r->pool, 0);
 
590
    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
 
591
 
 
592
    fd.desc_type = APR_POLL_FILE;
 
593
    fd.reqevents = APR_POLLIN;
 
594
    fd.p = r->pool;
 
595
    fd.desc.f = out; /* script's stdout */
 
596
    fd.client_data = (void *)1;
 
597
    rv = apr_pollset_add(data->pollset, &fd);
 
598
    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
 
599
 
 
600
    fd.desc.f = err; /* script's stderr */
 
601
    fd.client_data = (void *)2;
 
602
    rv = apr_pollset_add(data->pollset, &fd);
 
603
    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
 
604
 
 
605
    data->r = r;
 
606
    b->data = data;
 
607
    return b;
 
608
}
 
609
 
 
610
/* Create a duplicate CGI bucket using given bucket data */
 
611
static apr_bucket *cgi_bucket_dup(struct cgi_bucket_data *data,
 
612
                                  apr_bucket_alloc_t *list)
 
613
{
 
614
    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
 
615
    APR_BUCKET_INIT(b);
 
616
    b->free = apr_bucket_free;
 
617
    b->list = list;
 
618
    b->type = &bucket_type_cgi;
 
619
    b->length = (apr_size_t)(-1);
 
620
    b->start = -1;
 
621
    b->data = data;
 
622
    return b;
 
623
}
 
624
 
 
625
/* Handle stdout from CGI child.  Duplicate of logic from the _read
 
626
 * method of the real APR pipe bucket implementation. */
 
627
static apr_status_t cgi_read_stdout(apr_bucket *a, apr_file_t *out,
 
628
                                    const char **str, apr_size_t *len)
 
629
{
 
630
    char *buf;
 
631
    apr_status_t rv;
 
632
 
 
633
    *str = NULL;
 
634
    *len = APR_BUCKET_BUFF_SIZE;
 
635
    buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */
 
636
 
 
637
    rv = apr_file_read(out, buf, len);
 
638
 
 
639
    if (rv != APR_SUCCESS && rv != APR_EOF) {
 
640
        apr_bucket_free(buf);
 
641
        return rv;
 
642
    }
 
643
 
 
644
    if (*len > 0) {
 
645
        struct cgi_bucket_data *data = a->data;
 
646
        apr_bucket_heap *h;
 
647
 
 
648
        /* Change the current bucket to refer to what we read */
 
649
        a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free);
 
650
        h = a->data;
 
651
        h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
 
652
        *str = buf;
 
653
        APR_BUCKET_INSERT_AFTER(a, cgi_bucket_dup(data, a->list));
 
654
    }
 
655
    else {
 
656
        apr_bucket_free(buf);
 
657
        a = apr_bucket_immortal_make(a, "", 0);
 
658
        *str = a->data;
 
659
    }
 
660
    return rv;
 
661
}
 
662
 
 
663
/* Read method of CGI bucket: polls on stderr and stdout of the child,
 
664
 * sending any stderr output immediately away to the error log. */
 
665
static apr_status_t cgi_bucket_read(apr_bucket *b, const char **str,
 
666
                                    apr_size_t *len, apr_read_type_e block)
 
667
{
 
668
    struct cgi_bucket_data *data = b->data;
 
669
    apr_interval_time_t timeout;
 
670
    apr_status_t rv;
 
671
    int gotdata = 0;
 
672
 
 
673
    timeout = block == APR_NONBLOCK_READ ? 0 : data->r->server->timeout;
 
674
 
 
675
    do {
 
676
        const apr_pollfd_t *results;
 
677
        apr_int32_t num;
 
678
 
 
679
        rv = apr_pollset_poll(data->pollset, timeout, &num, &results);
 
680
        if (APR_STATUS_IS_TIMEUP(rv)) {
 
681
            if (timeout) {
 
682
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, data->r,
 
683
                              "Timeout waiting for output from CGI script %s",
 
684
                              data->r->filename);
 
685
                return rv;
 
686
            }
 
687
            else {
 
688
                return APR_EAGAIN;
 
689
            }
 
690
        }
 
691
        else if (APR_STATUS_IS_EINTR(rv)) {
 
692
            continue;
 
693
        }
 
694
        else if (rv != APR_SUCCESS) {
 
695
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r,
 
696
                          "poll failed waiting for CGI child");
 
697
            return rv;
 
698
        }
 
699
 
 
700
        for (; num; num--, results++) {
 
701
            if (results[0].client_data == (void *)1) {
 
702
                /* stdout */
 
703
                rv = cgi_read_stdout(b, results[0].desc.f, str, len);
 
704
                if (APR_STATUS_IS_EOF(rv)) {
 
705
                    rv = APR_SUCCESS;
 
706
                }
 
707
                gotdata = 1;
 
708
            } else {
 
709
                /* stderr */
 
710
                apr_status_t rv2 = log_script_err(data->r, results[0].desc.f);
 
711
                if (APR_STATUS_IS_EOF(rv2)) {
 
712
                    apr_pollset_remove(data->pollset, &results[0]);
 
713
                }
 
714
            }
 
715
        }
 
716
 
 
717
    } while (!gotdata);
 
718
 
 
719
    return rv;
 
720
}
 
721
 
 
722
static const apr_bucket_type_t bucket_type_cgi = {
 
723
    "CGI", 5, APR_BUCKET_DATA,
 
724
    apr_bucket_destroy_noop,
 
725
    cgi_bucket_read,
 
726
    apr_bucket_setaside_notimpl,
 
727
    apr_bucket_split_notimpl,
 
728
    apr_bucket_copy_notimpl
 
729
};
 
730
 
 
731
#endif
 
732
 
 
733
static int cgi_handler(request_rec *r)
 
734
{
 
735
    int nph;
 
736
    apr_size_t dbpos = 0;
 
737
    const char *argv0;
 
738
    const char *command;
 
739
    const char **argv;
 
740
    char *dbuf = NULL;
 
741
    apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL;
 
742
    apr_bucket_brigade *bb;
 
743
    apr_bucket *b;
 
744
    int is_included;
 
745
    int seen_eos, child_stopped_reading;
 
746
    apr_pool_t *p;
 
747
    cgi_server_conf *conf;
 
748
    apr_status_t rv;
 
749
    cgi_exec_info_t e_info;
 
750
    conn_rec *c = r->connection;
 
751
 
 
752
    if(strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script"))
 
753
        return DECLINED;
 
754
 
 
755
    is_included = !strcmp(r->protocol, "INCLUDED");
 
756
 
 
757
    p = r->main ? r->main->pool : r->pool;
 
758
 
 
759
    argv0 = apr_filepath_name_get(r->filename);
 
760
    nph = !(strncmp(argv0, "nph-", 4));
 
761
    conf = ap_get_module_config(r->server->module_config, &cgi_module);
 
762
 
 
763
    if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r))
 
764
        return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
 
765
                               "Options ExecCGI is off in this directory");
 
766
    if (nph && is_included)
 
767
        return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
 
768
                               "attempt to include NPH CGI script");
 
769
 
 
770
    if (r->finfo.filetype == 0)
 
771
        return log_scripterror(r, conf, HTTP_NOT_FOUND, 0,
 
772
                               "script not found or unable to stat");
 
773
    if (r->finfo.filetype == APR_DIR)
 
774
        return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
 
775
                               "attempt to invoke directory as script");
 
776
 
 
777
    if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) &&
 
778
        r->path_info && *r->path_info)
 
779
    {
 
780
        /* default to accept */
 
781
        return log_scripterror(r, conf, HTTP_NOT_FOUND, 0,
 
782
                               "AcceptPathInfo off disallows user's path");
 
783
    }
 
784
/*
 
785
    if (!ap_suexec_enabled) {
 
786
        if (!ap_can_exec(&r->finfo))
 
787
            return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
 
788
                                   "file permissions deny server execution");
 
789
    }
 
790
 
 
791
*/
 
792
    ap_add_common_vars(r);
 
793
    ap_add_cgi_vars(r);
 
794
 
 
795
    e_info.process_cgi = 1;
 
796
    e_info.cmd_type    = APR_PROGRAM;
 
797
    e_info.detached    = 0;
 
798
    e_info.in_pipe     = APR_CHILD_BLOCK;
 
799
    e_info.out_pipe    = APR_CHILD_BLOCK;
 
800
    e_info.err_pipe    = APR_CHILD_BLOCK;
 
801
    e_info.prog_type   = RUN_AS_CGI;
 
802
    e_info.bb          = NULL;
 
803
    e_info.ctx         = NULL;
 
804
    e_info.next        = NULL;
 
805
    e_info.addrspace   = 0;
 
806
 
 
807
    /* build the command line */
 
808
    if ((rv = cgi_build_command(&command, &argv, r, p, &e_info)) != APR_SUCCESS) {
 
809
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
810
                      "don't know how to spawn child process: %s",
 
811
                      r->filename);
 
812
        return HTTP_INTERNAL_SERVER_ERROR;
 
813
    }
 
814
 
 
815
    /* run the script in its own process */
 
816
    if ((rv = run_cgi_child(&script_out, &script_in, &script_err,
 
817
                            command, argv, r, p, &e_info)) != APR_SUCCESS) {
 
818
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
819
                      "couldn't spawn child process: %s", r->filename);
 
820
        return HTTP_INTERNAL_SERVER_ERROR;
 
821
    }
 
822
 
 
823
    /* Transfer any put/post args, CERN style...
 
824
     * Note that we already ignore SIGPIPE in the core server.
 
825
     */
 
826
    bb = apr_brigade_create(r->pool, c->bucket_alloc);
 
827
    seen_eos = 0;
 
828
    child_stopped_reading = 0;
 
829
    if (conf->logname) {
 
830
        dbuf = apr_palloc(r->pool, conf->bufbytes + 1);
 
831
        dbpos = 0;
 
832
    }
 
833
    do {
 
834
        apr_bucket *bucket;
 
835
 
 
836
        rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
 
837
                            APR_BLOCK_READ, HUGE_STRING_LEN);
 
838
 
 
839
        if (rv != APR_SUCCESS) {
 
840
            return rv;
 
841
        }
 
842
 
 
843
        for (bucket = APR_BRIGADE_FIRST(bb);
 
844
             bucket != APR_BRIGADE_SENTINEL(bb);
 
845
             bucket = APR_BUCKET_NEXT(bucket))
 
846
        {
 
847
            const char *data;
 
848
            apr_size_t len;
 
849
 
 
850
            if (APR_BUCKET_IS_EOS(bucket)) {
 
851
                seen_eos = 1;
 
852
                break;
 
853
            }
 
854
 
 
855
            /* We can't do much with this. */
 
856
            if (APR_BUCKET_IS_FLUSH(bucket)) {
 
857
                continue;
 
858
            }
 
859
 
 
860
            /* If the child stopped, we still must read to EOS. */
 
861
            if (child_stopped_reading) {
 
862
                continue;
 
863
            }
 
864
 
 
865
            /* read */
 
866
            apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
 
867
 
 
868
            if (conf->logname && dbpos < conf->bufbytes) {
 
869
                int cursize;
 
870
 
 
871
                if ((dbpos + len) > conf->bufbytes) {
 
872
                    cursize = conf->bufbytes - dbpos;
 
873
                }
 
874
                else {
 
875
                    cursize = len;
 
876
                }
 
877
                memcpy(dbuf + dbpos, data, cursize);
 
878
                dbpos += cursize;
 
879
            }
 
880
 
 
881
            /* Keep writing data to the child until done or too much time
 
882
             * elapses with no progress or an error occurs.
 
883
             */
 
884
            rv = apr_file_write_full(script_out, data, len, NULL);
 
885
 
 
886
            if (rv != APR_SUCCESS) {
 
887
                /* silly script stopped reading, soak up remaining message */
 
888
                child_stopped_reading = 1;
 
889
            }
 
890
        }
 
891
        apr_brigade_cleanup(bb);
 
892
    }
 
893
    while (!seen_eos);
 
894
 
 
895
    if (conf->logname) {
 
896
        dbuf[dbpos] = '\0';
 
897
    }
 
898
    /* Is this flush really needed? */
 
899
    apr_file_flush(script_out);
 
900
    apr_file_close(script_out);
 
901
 
 
902
    AP_DEBUG_ASSERT(script_in != NULL);
 
903
 
 
904
    apr_brigade_cleanup(bb);
 
905
 
 
906
#if APR_FILES_AS_SOCKETS
 
907
    apr_file_pipe_timeout_set(script_in, 0);
 
908
    apr_file_pipe_timeout_set(script_err, 0);
 
909
 
 
910
    b = cgi_bucket_create(r, script_in, script_err, c->bucket_alloc);
 
911
#else
 
912
    b = apr_bucket_pipe_create(script_in, c->bucket_alloc);
 
913
#endif
 
914
    APR_BRIGADE_INSERT_TAIL(bb, b);
 
915
    b = apr_bucket_eos_create(c->bucket_alloc);
 
916
    APR_BRIGADE_INSERT_TAIL(bb, b);
 
917
 
 
918
    /* Handle script return... */
 
919
    if (!nph) {
 
920
        const char *location;
 
921
        char sbuf[MAX_STRING_LEN];
 
922
        int ret;
 
923
 
 
924
        if ((ret = ap_scan_script_header_err_brigade(r, bb, sbuf))) {
 
925
            ret = log_script(r, conf, ret, dbuf, sbuf, bb, script_err);
 
926
 
 
927
            /* Set our status. */
 
928
            r->status = ret;
 
929
 
 
930
            /* Pass EOS bucket down the filter chain. */
 
931
            apr_brigade_cleanup(bb);
 
932
            b = apr_bucket_eos_create(c->bucket_alloc);
 
933
            APR_BRIGADE_INSERT_TAIL(bb, b);
 
934
            ap_pass_brigade(r->output_filters, bb);
 
935
 
 
936
            return ret;
 
937
        }
 
938
 
 
939
        location = apr_table_get(r->headers_out, "Location");
 
940
 
 
941
        if (location && r->status == 200) {
 
942
            /* For a redirect whether internal or not, discard any
 
943
             * remaining stdout from the script, and log any remaining
 
944
             * stderr output, as normal. */
 
945
            discard_script_output(bb);
 
946
            apr_brigade_destroy(bb);
 
947
            apr_file_pipe_timeout_set(script_err, r->server->timeout);
 
948
            log_script_err(r, script_err);
 
949
        }
 
950
 
 
951
        if (location && location[0] == '/' && r->status == 200) {
 
952
            /* This redirect needs to be a GET no matter what the original
 
953
             * method was.
 
954
             */
 
955
            r->method = apr_pstrdup(r->pool, "GET");
 
956
            r->method_number = M_GET;
 
957
 
 
958
            /* We already read the message body (if any), so don't allow
 
959
             * the redirected request to think it has one.  We can ignore
 
960
             * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
 
961
             */
 
962
            apr_table_unset(r->headers_in, "Content-Length");
 
963
 
 
964
            ap_internal_redirect_handler(location, r);
 
965
            return OK;
 
966
        }
 
967
        else if (location && r->status == 200) {
 
968
            /* XX Note that if a script wants to produce its own Redirect
 
969
             * body, it now has to explicitly *say* "Status: 302"
 
970
             */
 
971
            return HTTP_MOVED_TEMPORARILY;
 
972
        }
 
973
 
 
974
        rv = ap_pass_brigade(r->output_filters, bb);
 
975
    }
 
976
    else /* nph */ {
 
977
        struct ap_filter_t *cur;
 
978
 
 
979
        /* get rid of all filters up through protocol...  since we
 
980
         * haven't parsed off the headers, there is no way they can
 
981
         * work
 
982
         */
 
983
 
 
984
        cur = r->proto_output_filters;
 
985
        while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) {
 
986
            cur = cur->next;
 
987
        }
 
988
        r->output_filters = r->proto_output_filters = cur;
 
989
 
 
990
        rv = ap_pass_brigade(r->output_filters, bb);
 
991
    }
 
992
 
 
993
    /* don't soak up script output if errors occurred writing it
 
994
     * out...  otherwise, we prolong the life of the script when the
 
995
     * connection drops or we stopped sending output for some other
 
996
     * reason */
 
997
    if (rv == APR_SUCCESS && !r->connection->aborted) {
 
998
        apr_file_pipe_timeout_set(script_err, r->server->timeout);
 
999
        log_script_err(r, script_err);
 
1000
    }
 
1001
 
 
1002
    apr_file_close(script_err);
 
1003
 
 
1004
    return OK;                      /* NOT r->status, even if it has changed. */
 
1005
}
 
1006
 
 
1007
/*============================================================================
 
1008
 *============================================================================
 
1009
 * This is the beginning of the cgi filter code moved from mod_include. This
 
1010
 *   is the code required to handle the "exec" SSI directive.
 
1011
 *============================================================================
 
1012
 *============================================================================*/
 
1013
static apr_status_t include_cgi(include_ctx_t *ctx, ap_filter_t *f,
 
1014
                                apr_bucket_brigade *bb, char *s)
 
1015
{
 
1016
    request_rec *r = f->r;
 
1017
    request_rec *rr = ap_sub_req_lookup_uri(s, r, f->next);
 
1018
    int rr_status;
 
1019
 
 
1020
    if (rr->status != HTTP_OK) {
 
1021
        ap_destroy_sub_req(rr);
 
1022
        return APR_EGENERAL;
 
1023
    }
 
1024
 
 
1025
    /* No hardwired path info or query allowed */
 
1026
    if ((rr->path_info && rr->path_info[0]) || rr->args) {
 
1027
        ap_destroy_sub_req(rr);
 
1028
        return APR_EGENERAL;
 
1029
    }
 
1030
    if (rr->finfo.filetype != APR_REG) {
 
1031
        ap_destroy_sub_req(rr);
 
1032
        return APR_EGENERAL;
 
1033
    }
 
1034
 
 
1035
    /* Script gets parameters of the *document*, for back compatibility */
 
1036
    rr->path_info = r->path_info;       /* hard to get right; see mod_cgi.c */
 
1037
    rr->args = r->args;
 
1038
 
 
1039
    /* Force sub_req to be treated as a CGI request, even if ordinary
 
1040
     * typing rules would have called it something else.
 
1041
     */
 
1042
    ap_set_content_type(rr, CGI_MAGIC_TYPE);
 
1043
 
 
1044
    /* Run it. */
 
1045
    rr_status = ap_run_sub_req(rr);
 
1046
    if (ap_is_HTTP_REDIRECT(rr_status)) {
 
1047
        const char *location = apr_table_get(rr->headers_out, "Location");
 
1048
 
 
1049
        if (location) {
 
1050
            char *buffer;
 
1051
 
 
1052
            location = ap_escape_html(rr->pool, location);
 
1053
            buffer = apr_pstrcat(ctx->pool, "<a href=\"", location, "\">",
 
1054
                                 location, "</a>", NULL);
 
1055
 
 
1056
            APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buffer,
 
1057
                                    strlen(buffer), ctx->pool,
 
1058
                                    f->c->bucket_alloc));
 
1059
        }
 
1060
    }
 
1061
 
 
1062
    ap_destroy_sub_req(rr);
 
1063
 
 
1064
    return APR_SUCCESS;
 
1065
}
 
1066
 
 
1067
static apr_status_t include_cmd(include_ctx_t *ctx, ap_filter_t *f,
 
1068
                                apr_bucket_brigade *bb, const char *command)
 
1069
{
 
1070
    cgi_exec_info_t  e_info;
 
1071
    const char **argv;
 
1072
    apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL;
 
1073
    apr_status_t rv;
 
1074
    request_rec *r = f->r;
 
1075
 
 
1076
    add_ssi_vars(r);
 
1077
 
 
1078
    e_info.process_cgi = 0;
 
1079
    e_info.cmd_type    = APR_SHELLCMD;
 
1080
    e_info.detached    = 0;
 
1081
    e_info.in_pipe     = APR_NO_PIPE;
 
1082
    e_info.out_pipe    = APR_FULL_BLOCK;
 
1083
    e_info.err_pipe    = APR_NO_PIPE;
 
1084
    e_info.prog_type   = RUN_AS_SSI;
 
1085
    e_info.bb          = &bb;
 
1086
    e_info.ctx         = ctx;
 
1087
    e_info.next        = f->next;
 
1088
    e_info.addrspace   = 0;
 
1089
 
 
1090
    if ((rv = cgi_build_command(&command, &argv, r, r->pool,
 
1091
                                &e_info)) != APR_SUCCESS) {
 
1092
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
1093
                      "don't know how to spawn cmd child process: %s",
 
1094
                      r->filename);
 
1095
        return rv;
 
1096
    }
 
1097
 
 
1098
    /* run the script in its own process */
 
1099
    if ((rv = run_cgi_child(&script_out, &script_in, &script_err,
 
1100
                            command, argv, r, r->pool,
 
1101
                            &e_info)) != APR_SUCCESS) {
 
1102
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
1103
                      "couldn't spawn child process: %s", r->filename);
 
1104
        return rv;
 
1105
    }
 
1106
 
 
1107
    APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pipe_create(script_in,
 
1108
                            f->c->bucket_alloc));
 
1109
    ctx->flush_now = 1;
 
1110
 
 
1111
    /* We can't close the pipe here, because we may return before the
 
1112
     * full CGI has been sent to the network.  That's okay though,
 
1113
     * because we can rely on the pool to close the pipe for us.
 
1114
     */
 
1115
    return APR_SUCCESS;
 
1116
}
 
1117
 
 
1118
static apr_status_t handle_exec(include_ctx_t *ctx, ap_filter_t *f,
 
1119
                                apr_bucket_brigade *bb)
 
1120
{
 
1121
    char *tag = NULL;
 
1122
    char *tag_val = NULL;
 
1123
    request_rec *r = f->r;
 
1124
    char *file = r->filename;
 
1125
    char parsed_string[MAX_STRING_LEN];
 
1126
 
 
1127
    if (!ctx->argc) {
 
1128
        ap_log_rerror(APLOG_MARK,
 
1129
                      (ctx->flags & SSI_FLAG_PRINTING)
 
1130
                          ? APLOG_ERR : APLOG_WARNING,
 
1131
                      0, r, "missing argument for exec element in %s",
 
1132
                      r->filename);
 
1133
    }
 
1134
 
 
1135
    if (!(ctx->flags & SSI_FLAG_PRINTING)) {
 
1136
        return APR_SUCCESS;
 
1137
    }
 
1138
 
 
1139
    if (!ctx->argc) {
 
1140
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1141
        return APR_SUCCESS;
 
1142
    }
 
1143
 
 
1144
    if (ctx->flags & SSI_FLAG_NO_EXEC) {
 
1145
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "exec used but not allowed "
 
1146
                      "in %s", r->filename);
 
1147
        SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1148
        return APR_SUCCESS;
 
1149
    }
 
1150
 
 
1151
    while (1) {
 
1152
        cgi_pfn_gtv(ctx, &tag, &tag_val, SSI_VALUE_DECODED);
 
1153
        if (!tag || !tag_val) {
 
1154
            break;
 
1155
        }
 
1156
 
 
1157
        if (!strcmp(tag, "cmd")) {
 
1158
            apr_status_t rv;
 
1159
 
 
1160
            cgi_pfn_ps(ctx, tag_val, parsed_string, sizeof(parsed_string),
 
1161
                       SSI_EXPAND_LEAVE_NAME);
 
1162
 
 
1163
            rv = include_cmd(ctx, f, bb, parsed_string);
 
1164
            if (rv != APR_SUCCESS) {
 
1165
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "execution failure "
 
1166
                              "for parameter \"%s\" to tag exec in file %s",
 
1167
                              tag, r->filename);
 
1168
                SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1169
                break;
 
1170
            }
 
1171
        }
 
1172
        else if (!strcmp(tag, "cgi")) {
 
1173
            apr_status_t rv;
 
1174
 
 
1175
            cgi_pfn_ps(ctx, tag_val, parsed_string, sizeof(parsed_string),
 
1176
                       SSI_EXPAND_DROP_NAME);
 
1177
 
 
1178
            rv = include_cgi(ctx, f, bb, parsed_string);
 
1179
            if (rv != APR_SUCCESS) {
 
1180
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "invalid CGI ref "
 
1181
                              "\"%s\" in %s", tag_val, file);
 
1182
                SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1183
                break;
 
1184
            }
 
1185
        }
 
1186
        else {
 
1187
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unknown parameter "
 
1188
                          "\"%s\" to tag exec in %s", tag, file);
 
1189
            SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
 
1190
            break;
 
1191
        }
 
1192
    }
 
1193
 
 
1194
    return APR_SUCCESS;
 
1195
}
 
1196
 
 
1197
 
 
1198
/*============================================================================
 
1199
 *============================================================================
 
1200
 * This is the end of the cgi filter code moved from mod_include.
 
1201
 *============================================================================
 
1202
 *============================================================================*/
 
1203
 
 
1204
 
 
1205
static int cgi_post_config(apr_pool_t *p, apr_pool_t *plog,
 
1206
                                apr_pool_t *ptemp, server_rec *s)
 
1207
{
 
1208
    cgi_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
 
1209
    cgi_pfn_gtv          = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value);
 
1210
    cgi_pfn_ps           = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string);
 
1211
 
 
1212
    if ((cgi_pfn_reg_with_ssi) && (cgi_pfn_gtv) && (cgi_pfn_ps)) {
 
1213
        /* Required by mod_include filter. This is how mod_cgi registers
 
1214
         *   with mod_include to provide processing of the exec directive.
 
1215
         */
 
1216
        cgi_pfn_reg_with_ssi("exec", handle_exec);
 
1217
    }
 
1218
 
 
1219
    /* This is the means by which unusual (non-unix) os's may find alternate
 
1220
     * means to run a given command (e.g. shebang/registry parsing on Win32)
 
1221
     */
 
1222
    cgi_build_command    = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command);
 
1223
    if (!cgi_build_command) {
 
1224
        cgi_build_command = default_build_command;
 
1225
    }
 
1226
    return OK;
 
1227
}
 
1228
 
 
1229
static void register_hooks(apr_pool_t *p)
 
1230
{
 
1231
    static const char * const aszPre[] = { "mod_include.c", NULL };
 
1232
    ap_hook_handler(cgi_handler, NULL, NULL, APR_HOOK_MIDDLE);
 
1233
    ap_hook_post_config(cgi_post_config, aszPre, NULL, APR_HOOK_REALLY_FIRST);
 
1234
}
 
1235
 
 
1236
module AP_MODULE_DECLARE_DATA cgi_module =
 
1237
{
 
1238
    STANDARD20_MODULE_STUFF,
 
1239
    NULL,                        /* dir config creater */
 
1240
    NULL,                        /* dir merger --- default is to override */
 
1241
    create_cgi_config,           /* server config */
 
1242
    merge_cgi_config,            /* merge server config */
 
1243
    cgi_cmds,                    /* command apr_table_t */
 
1244
    register_hooks               /* register hooks */
 
1245
};