mod_cgi.c revision 7e4aef77225797dab322016e8f609a90f75c7f3e
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * Licensed under the Apache License, Version 2.0 (the "License");
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * you may not use this file except in compliance with the License.
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * You may obtain a copy of the License at
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * http://www.apache.org/licenses/LICENSE-2.0
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * Unless required by applicable law or agreed to in writing, software
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * distributed under the License is distributed on an "AS IS" BASIS,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * See the License for the specific language governing permissions and
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * limitations under the License.
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * http_script: keeps all script-related ramblings together.
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * Compliant to CGI/1.1 spec
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * Adapted by rst from original NCSA code by Rob McCool
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * custom error responses, and DOCUMENT_ROOT because we found it useful.
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * It also adds SERVER_ADMIN - useful for scripts to know who to mail when
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering#include "apr_thread_proc.h" /* for RLIMIT stuff */
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgi_pfn_reg_with_ssi;
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgi_pfn_gtv;
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgi_pfn_ps;
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering/* Read and discard the data in the brigade produced by a CGI script */
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic void discard_script_output(apr_bucket_brigade *bb);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * in ScriptAliased directories, which means we need to know if this
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * request came through ScriptAlias or not... so the Alias module
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * leaves a note for us.
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic int is_scriptaliased(request_rec *r)
a8eaaee72a2f06e0fb64fb71de3b71ecba31dafbJan Engelhardt const char *t = apr_table_get(r->notes, "alias-forced-type");
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering return t && (!strcasecmp(t, "cgi-script"));
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt/* Configuration stuff */
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringtypedef struct {
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic void *create_cgi_config(apr_pool_t *p, server_rec *s)
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering (cgi_server_conf *) apr_pcalloc(p, sizeof(cgi_server_conf));
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic void *merge_cgi_config(apr_pool_t *p, void *basev, void *overridesv)
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt cgi_server_conf *base = (cgi_server_conf *) basev,
4f76ef0423a30ee672891056aeb5df2422947e1dThomas Hindoe Paaboel Andersen *overrides = (cgi_server_conf *) overridesv;
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering return overrides->logname ? overrides : base;
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg)
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering cgi_server_conf *conf = ap_get_module_config(s->module_config,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering conf->logname = ap_server_root_relative(cmd->pool, arg);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering return apr_pstrcat(cmd->pool, "Invalid ScriptLog path ",
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic const char *set_scriptlog_length(cmd_parms *cmd, void *dummy,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering cgi_server_conf *conf = ap_get_module_config(s->module_config,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering cgi_server_conf *conf = ap_get_module_config(s->module_config,
7732f92bad5f24a4bd03bb357af46da56b0ac94dLennart PoetteringAP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF,
7732f92bad5f24a4bd03bb357af46da56b0ac94dLennart Poettering "the name of a log for script debugging info"),
7732f92bad5f24a4bd03bb357af46da56b0ac94dLennart PoetteringAP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF,
7732f92bad5f24a4bd03bb357af46da56b0ac94dLennart Poettering "the maximum length (in bytes) of the script debug log"),
7732f92bad5f24a4bd03bb357af46da56b0ac94dLennart PoetteringAP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF,
7732f92bad5f24a4bd03bb357af46da56b0ac94dLennart Poettering "the maximum size (in bytes) to record of a POST request"),
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic int log_scripterror(request_rec *r, cgi_server_conf * conf, int ret,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering int log_flags = rv ? APLOG_ERR : APLOG_ERR;
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering ap_log_rerror(APLOG_MARK, log_flags, rv, r,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering /* XXX Very expensive mainline case! Open, then getfileinfo! */
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering APR_FINFO_SIZE, r->pool) == APR_SUCCESS) &&
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering r->args ? "?" : "", r->args ? r->args : "", r->protocol);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering apr_file_printf(f, "%%%% %d %s\n", ret, r->filename);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering apr_file_printf(f, "%%error\n%s\n", error);
5f932eb9af7a5e4723855bcd776c2acaa2a31932Lennart Poettering/* Soak up stderr from a script and redirect it to the error log.
5f932eb9af7a5e4723855bcd776c2acaa2a31932Lennart Poetteringstatic apr_status_t log_script_err(request_rec *r, apr_file_t *script_err)
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN,
524f3e5c9d1eb2fba3d0b65d1790018163ba0b20Zbigniew Jędrzejewski-Szmek ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic int log_script(request_rec *r, cgi_server_conf * conf, int ret,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering char *dbuf, const char *sbuf, apr_bucket_brigade *bb,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering /* XXX Very expensive mainline case! Open, then getfileinfo! */
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering APR_FINFO_SIZE, r->pool) == APR_SUCCESS) &&
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering /* Soak up script output */
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering r->args ? "?" : "", r->args ? r->args : "", r->protocol);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering /* "%% 500 /usr/local/apache/cgi-bin" */
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering apr_file_printf(f, "%%%% %d %s\n", ret, r->filename);
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering if ((r->method_number == M_POST || r->method_number == M_PUT) &&
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering hdrs_arr = apr_table_elts(r->err_headers_out);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering if (apr_file_gets(argsbuffer, HUGE_STRING_LEN, script_err) == APR_SUCCESS) {
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering while (apr_file_gets(argsbuffer, HUGE_STRING_LEN,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering/* This is the special environment used for running the "exec cmd="
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * variety of SSI directives.
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering if (r->path_info && r->path_info[0] != '\0') {
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info),
f6d6bad1461a8f545a80955fadd7ee0c10db15bbLennart Poettering char *arg_copy = apr_pstrdup(r->pool, r->args);
f6d6bad1461a8f545a80955fadd7ee0c10db15bbLennart Poettering apr_table_setn(e, "QUERY_STRING", r->args);
f6d6bad1461a8f545a80955fadd7ee0c10db15bbLennart Poettering apr_table_setn(e, "QUERY_STRING_UNESCAPED",
f6d6bad1461a8f545a80955fadd7ee0c10db15bbLennart Poetteringstatic void cgi_child_errfn(apr_pool_t *pool, apr_status_t err,
f6d6bad1461a8f545a80955fadd7ee0c10db15bbLennart Poettering /* Escape the logged string because it may be something that
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering * came in over the network.
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt "(%d)%s: %s\n",
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering apr_strerror(err, errbuf, sizeof(errbuf)),
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poetteringstatic apr_status_t run_cgi_child(apr_file_t **script_out,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering const char * const argv[],
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering const char * const *env;
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering core_dir_config *conf = ap_get_module_config(r->per_dir_config,
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering /* Under OS/2 need to use device con. */
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering fprintf(dbg, "Attempting to exec %s as CGI child (argv0 = %s)\n",
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering env = (const char * const *)ap_create_environment(p, r->subprocess_env);
f757855e81fc0bc116de372220096e532afb5cb8Lennart Poettering for (i = 0; env[i]; ++i)
#ifdef RLIMIT_CPU
#ifdef RLIMIT_NPROC
procattr, p);
if (!*script_in)
return APR_EBADF;
if (!*script_out)
return APR_EBADF;
if (!*script_err)
return APR_EBADF;
#ifdef DEBUG_CGI
return (rc);
if (!args) {
++numwords;
ap_unescape_url(w);
return APR_SUCCESS;
apr_bucket *e;
const char *buf;
e = APR_BUCKET_NEXT(e))
if (APR_BUCKET_IS_EOS(e)) {
struct cgi_bucket_data {
request_rec *r;
APR_BUCKET_INIT(b);
data->r = r;
APR_BUCKET_INIT(b);
char *buf;
return rv;
if (*len > 0) {
apr_bucket_heap *h;
h = a->data;
return rv;
int gotdata = 0;
if (timeout) {
return rv;
return APR_EAGAIN;
return rv;
} while (!gotdata);
return rv;
int nph;
const char *argv0;
const char *command;
const char **argv;
apr_bucket *b;
int is_included;
apr_pool_t *p;
return DECLINED;
return DECLINED;
ap_add_cgi_vars(r);
r->filename);
return HTTP_INTERNAL_SERVER_ERROR;
return HTTP_INTERNAL_SERVER_ERROR;
seen_eos = 0;
dbpos = 0;
return rv;
const char *data;
if (child_stopped_reading) {
int cursize;
while (!seen_eos);
if (!nph) {
const char *location;
int ret;
return OK;
return HTTP_MOVED_TEMPORARILY;
request_rec *r = f->r;
int rr_status;
return APR_EGENERAL;
return APR_EGENERAL;
return APR_EGENERAL;
if (location) {
char *buffer;
f->c->bucket_alloc));
return APR_SUCCESS;
const char **argv;
request_rec *r = f->r;
add_ssi_vars(r);
r->filename);
return rv;
return rv;
f->c->bucket_alloc));
return APR_SUCCESS;
request_rec *r = f->r;
r->filename);
return APR_SUCCESS;
return APR_SUCCESS;
return APR_SUCCESS;
return APR_SUCCESS;
if (!cgi_build_command) {
return OK;