Blame modules/fcgid/fcgid_bridge.c

Packit Service f9aed3
/*
Packit Service f9aed3
 * Licensed to the Apache Software Foundation (ASF) under one or more
Packit Service f9aed3
 * contributor license agreements.  See the NOTICE file distributed with
Packit Service f9aed3
 * this work for additional information regarding copyright ownership.
Packit Service f9aed3
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit Service f9aed3
 * (the "License"); you may not use this file except in compliance with
Packit Service f9aed3
 * the License.  You may obtain a copy of the License at
Packit Service f9aed3
 *
Packit Service f9aed3
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit Service f9aed3
 *
Packit Service f9aed3
 * Unless required by applicable law or agreed to in writing, software
Packit Service f9aed3
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit Service f9aed3
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit Service f9aed3
 * See the License for the specific language governing permissions and
Packit Service f9aed3
 * limitations under the License.
Packit Service f9aed3
 */
Packit Service f9aed3
Packit Service f9aed3
#include "httpd.h"
Packit Service f9aed3
#include "http_request.h"
Packit Service f9aed3
#include "apr_strings.h"
Packit Service f9aed3
#include "apr_portable.h"
Packit Service f9aed3
#include "apr_pools.h"
Packit Service f9aed3
#include "apr_file_io.h"
Packit Service f9aed3
#include "util_script.h"
Packit Service f9aed3
#include "fcgid_bridge.h"
Packit Service f9aed3
#include "fcgid_pm.h"
Packit Service f9aed3
#include "fcgid_proctbl.h"
Packit Service f9aed3
#include "fcgid_proc.h"
Packit Service f9aed3
#include "fcgid_conf.h"
Packit Service f9aed3
#include "fcgid_spawn_ctl.h"
Packit Service f9aed3
#include "fcgid_protocol.h"
Packit Service f9aed3
#include "fcgid_bucket.h"
Packit Service f9aed3
#define FCGID_APPLY_TRY_COUNT 2
Packit Service f9aed3
#define FCGID_REQUEST_COUNT 32
Packit Service f9aed3
#define FCGID_BRIGADE_CLEAN_STEP 32
Packit Service f9aed3
Packit Service f9aed3
static fcgid_procnode *apply_free_procnode(request_rec *r,
Packit Service f9aed3
                                           fcgid_command * command)
Packit Service f9aed3
{
Packit Service f9aed3
    /* Scan idle list, find a node match inode, deviceid and groupid
Packit Service f9aed3
       If there is no one there, return NULL */
Packit Service f9aed3
    fcgid_procnode *previous_node, *current_node, *next_node;
Packit Service f9aed3
    fcgid_procnode *busy_list_header, *proc_table;
Packit Service f9aed3
    apr_ino_t inode = command->inode;
Packit Service f9aed3
    apr_dev_t deviceid = command->deviceid;
Packit Service f9aed3
    uid_t uid = command->uid;
Packit Service f9aed3
    gid_t gid = command->gid;
Packit Service f9aed3
    const char *cmdline = command->cmdline;
Packit Service f9aed3
Packit Service f9aed3
    proc_table = proctable_get_table_array();
Packit Service f9aed3
    previous_node = proctable_get_idle_list();
Packit Service f9aed3
    busy_list_header = proctable_get_busy_list();
Packit Service f9aed3
Packit Service f9aed3
    proctable_lock(r);
Packit Service f9aed3
    current_node = &proc_table[previous_node->next_index];
Packit Service f9aed3
    while (current_node != proc_table) {
Packit Service f9aed3
        next_node = &proc_table[current_node->next_index];
Packit Service f9aed3
Packit Service f9aed3
        if (current_node->inode == inode
Packit Service f9aed3
            && current_node->deviceid == deviceid
Packit Service f9aed3
            && !strcmp(current_node->cmdline, cmdline)
Packit Service f9aed3
            && current_node->vhost_id == command->vhost_id
Packit Service f9aed3
            && current_node->uid == uid && current_node->gid == gid) {
Packit Service f9aed3
            /* Unlink from idle list */
Packit Service f9aed3
            previous_node->next_index = current_node->next_index;
Packit Service f9aed3
Packit Service f9aed3
            /* Link to busy list */
Packit Service f9aed3
            current_node->next_index = busy_list_header->next_index;
Packit Service f9aed3
            busy_list_header->next_index = current_node - proc_table;
Packit Service f9aed3
Packit Service f9aed3
            proctable_unlock(r);
Packit Service f9aed3
            return current_node;
Packit Service f9aed3
        }
Packit Service f9aed3
        else
Packit Service f9aed3
            previous_node = current_node;
Packit Service f9aed3
Packit Service f9aed3
        current_node = next_node;
Packit Service f9aed3
    }
Packit Service f9aed3
    proctable_unlock(r);
Packit Service f9aed3
Packit Service f9aed3
    /* Found nothing */
Packit Service f9aed3
    return NULL;
Packit Service f9aed3
}
Packit Service f9aed3
Packit Service f9aed3
static void
Packit Service f9aed3
return_procnode(request_rec *r,
Packit Service f9aed3
                fcgid_procnode *procnode, int communicate_error)
Packit Service f9aed3
{
Packit Service f9aed3
    fcgid_procnode *previous_node, *current_node, *next_node;
Packit Service f9aed3
    fcgid_procnode *proc_table = proctable_get_table_array();
Packit Service f9aed3
    fcgid_procnode *error_list_header = proctable_get_error_list();
Packit Service f9aed3
    fcgid_procnode *idle_list_header = proctable_get_idle_list();
Packit Service f9aed3
    fcgid_procnode *busy_list_header = proctable_get_busy_list();
Packit Service f9aed3
Packit Service f9aed3
    proctable_lock(r);
Packit Service f9aed3
Packit Service f9aed3
    /* Unlink the node from busy list first */
Packit Service f9aed3
    previous_node = busy_list_header;
Packit Service f9aed3
    current_node = &proc_table[previous_node->next_index];
Packit Service f9aed3
    while (current_node != proc_table) {
Packit Service f9aed3
        next_node = &proc_table[current_node->next_index];
Packit Service f9aed3
        if (current_node == procnode) {
Packit Service f9aed3
            /* Unlink from busy list */
Packit Service f9aed3
            previous_node->next_index = current_node->next_index;
Packit Service f9aed3
            break;
Packit Service f9aed3
        }
Packit Service f9aed3
        else
Packit Service f9aed3
            previous_node = current_node;
Packit Service f9aed3
        current_node = next_node;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    /* Return to error list or idle list */
Packit Service f9aed3
    if (communicate_error) {
Packit Service f9aed3
        /* Link to error list */
Packit Service f9aed3
        procnode->next_index = error_list_header->next_index;
Packit Service f9aed3
        error_list_header->next_index = procnode - proc_table;
Packit Service f9aed3
    }
Packit Service f9aed3
    else {
Packit Service f9aed3
        /* Link to idle list */
Packit Service f9aed3
        procnode->next_index = idle_list_header->next_index;
Packit Service f9aed3
        idle_list_header->next_index = procnode - proc_table;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    proctable_unlock(r);
Packit Service f9aed3
}
Packit Service f9aed3
Packit Service f9aed3
static int count_busy_processes(request_rec *r, fcgid_command *command)
Packit Service f9aed3
{
Packit Service f9aed3
    int result = 0;
Packit Service f9aed3
    fcgid_procnode *previous_node, *current_node, *next_node;
Packit Service f9aed3
    fcgid_procnode *proc_table = proctable_get_table_array();
Packit Service f9aed3
    fcgid_procnode *busy_list_header = proctable_get_busy_list();
Packit Service f9aed3
Packit Service f9aed3
    proctable_lock(r);
Packit Service f9aed3
Packit Service f9aed3
    previous_node = busy_list_header;
Packit Service f9aed3
    current_node = &proc_table[previous_node->next_index];
Packit Service f9aed3
    while (current_node != proc_table) {
Packit Service f9aed3
        if (current_node->inode == command->inode
Packit Service f9aed3
            && current_node->deviceid == command->deviceid
Packit Service f9aed3
            && !strcmp(current_node->cmdline, command->cmdline)
Packit Service f9aed3
            && current_node->vhost_id == command->vhost_id
Packit Service f9aed3
            && current_node->uid == command->uid
Packit Service f9aed3
            && current_node->gid == command->gid) {
Packit Service f9aed3
            result++;
Packit Service f9aed3
        }
Packit Service f9aed3
        next_node = &proc_table[current_node->next_index];
Packit Service f9aed3
        current_node = next_node;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    proctable_unlock(r);
Packit Service f9aed3
Packit Service f9aed3
    return result;
Packit Service f9aed3
}
Packit Service f9aed3
Packit Service f9aed3
apr_status_t bucket_ctx_cleanup(void *thectx)
Packit Service f9aed3
{
Packit Service f9aed3
    /* Cleanup jobs:
Packit Service f9aed3
       1. Free bucket buffer
Packit Service f9aed3
       2. Return procnode
Packit Service f9aed3
       NOTE: ipc will be clean when request pool cleanup, so I don't need to close it here
Packit Service f9aed3
     */
Packit Service f9aed3
    fcgid_bucket_ctx *ctx = (fcgid_bucket_ctx *) thectx;
Packit Service f9aed3
    request_rec *r = ctx->ipc.request;
Packit Service f9aed3
Packit Service f9aed3
    /* Free bucket buffer */
Packit Service f9aed3
    if (ctx->buffer) {
Packit Service f9aed3
        apr_bucket_destroy(ctx->buffer);
Packit Service f9aed3
        ctx->buffer = NULL;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    /* proc_close_ipc() and ipc_handle_cleanup() do their own sanity
Packit Service f9aed3
     * checks, but we'll do our own anyway
Packit Service f9aed3
     */
Packit Service f9aed3
    if (ctx->ipc.ipc_handle_info) {
Packit Service f9aed3
        proc_close_ipc(&ctx->ipc);
Packit Service f9aed3
        ctx->ipc.ipc_handle_info = NULL;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    if (ctx->procnode) {
Packit Service f9aed3
        ++ctx->procnode->requests_handled;
Packit Service f9aed3
Packit Service f9aed3
        /* Return procnode
Packit Service f9aed3
           I will return this slot to idle(or error) list
Packit Service f9aed3
         */
Packit Service f9aed3
        if (ctx->procnode->diewhy == FCGID_DIE_BUSY_TIMEOUT) {
Packit Service f9aed3
            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
Packit Service f9aed3
                          "mod_fcgid: %s took longer than busy timeout "
Packit Service f9aed3
                          "(%d secs)",
Packit Service f9aed3
                          r->uri,
Packit Service f9aed3
                          ctx->procnode->cmdopts.busy_timeout);
Packit Service f9aed3
            return_procnode(r, ctx->procnode, 1 /* busy timeout */ );
Packit Service f9aed3
        }
Packit Service f9aed3
        else if (ctx->has_error) {
Packit Service f9aed3
            ctx->procnode->diewhy = FCGID_DIE_COMM_ERROR;
Packit Service f9aed3
            return_procnode(r, ctx->procnode, 1 /* communication error */ );
Packit Service f9aed3
        }
Packit Service f9aed3
        else if (ctx->procnode->cmdopts.max_requests_per_process
Packit Service f9aed3
                 && ctx->procnode->requests_handled >=
Packit Service f9aed3
                 ctx->procnode->cmdopts.max_requests_per_process) {
Packit Service f9aed3
            ctx->procnode->diewhy = FCGID_DIE_LIFETIME_EXPIRED;
Packit Service f9aed3
            return_procnode(r, ctx->procnode, 1 /* handled all requests */ );
Packit Service f9aed3
        }
Packit Service f9aed3
        else
Packit Service f9aed3
            return_procnode(r, ctx->procnode, 0 /* communication ok */ );
Packit Service f9aed3
Packit Service f9aed3
        ctx->procnode = NULL;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    return APR_SUCCESS;
Packit Service f9aed3
}
Packit Service f9aed3
Packit Service f9aed3
static int getsfunc_fcgid_BRIGADE(char *buf, int len, void *arg)
Packit Service f9aed3
{
Packit Service f9aed3
    apr_bucket_brigade *bb = (apr_bucket_brigade *) arg;
Packit Service f9aed3
    const char *dst_end = buf + len - 1;        /* leave room for terminating null */
Packit Service f9aed3
    char *dst = buf;
Packit Service f9aed3
    apr_bucket *e = APR_BRIGADE_FIRST(bb);
Packit Service f9aed3
    apr_status_t rv;
Packit Service f9aed3
    int done = 0;
Packit Service f9aed3
    int getLF = 0;
Packit Service f9aed3
    int getColon = 0;
Packit Service f9aed3
Packit Service f9aed3
    while ((dst < dst_end) && !done && e != APR_BRIGADE_SENTINEL(bb)) {
Packit Service f9aed3
        const char *bucket_data;
Packit Service f9aed3
        apr_size_t bucket_data_len;
Packit Service f9aed3
        const char *src;
Packit Service f9aed3
        const char *src_end;
Packit Service f9aed3
        apr_bucket *next;
Packit Service f9aed3
Packit Service f9aed3
        rv = apr_bucket_read(e, &bucket_data, &bucket_data_len,
Packit Service f9aed3
                             APR_BLOCK_READ);
Packit Service f9aed3
        if (rv != APR_SUCCESS) {
Packit Service f9aed3
            return 0;
Packit Service f9aed3
        }
Packit Service f9aed3
Packit Service f9aed3
        /* Move on to next bucket if it's fastcgi header bucket */
Packit Service f9aed3
        if (e->type == &ap_bucket_type_fcgid_header
Packit Service f9aed3
            || e->type == &apr_bucket_type_immortal) {
Packit Service f9aed3
            next = APR_BUCKET_NEXT(e);
Packit Service f9aed3
            apr_bucket_delete(e);
Packit Service f9aed3
            e = next;
Packit Service f9aed3
            if (getLF) {
Packit Service f9aed3
                done = 1;
Packit Service f9aed3
            }
Packit Service f9aed3
            continue;
Packit Service f9aed3
        }
Packit Service f9aed3
Packit Service f9aed3
        if (bucket_data_len == 0)
Packit Service f9aed3
            return 0;
Packit Service f9aed3
Packit Service f9aed3
        /* Base on RFC2616 section 4.2 */
Packit Service f9aed3
        src = bucket_data;
Packit Service f9aed3
        src_end = bucket_data + bucket_data_len;
Packit Service f9aed3
        while ((src < src_end) && (dst < dst_end) && !done) {
Packit Service f9aed3
            if (*src == ':')
Packit Service f9aed3
                getColon = 1;
Packit Service f9aed3
Packit Service f9aed3
            if (getLF && ((*src != ' ' && *src != '\t') || !getColon)) {
Packit Service f9aed3
                done = 1;
Packit Service f9aed3
                getColon = 0;
Packit Service f9aed3
                break;
Packit Service f9aed3
            }
Packit Service f9aed3
            else if (getLF && (*src == ' ' || *src == '\t')) {
Packit Service f9aed3
                *dst++ = '\r';
Packit Service f9aed3
                *dst++ = '\n';
Packit Service f9aed3
                getLF = 0;
Packit Service f9aed3
            }
Packit Service f9aed3
Packit Service f9aed3
            if (*src == '\n') {
Packit Service f9aed3
                getLF = 1;
Packit Service f9aed3
            }
Packit Service f9aed3
            else if (*src != '\r') {
Packit Service f9aed3
                *dst++ = *src;
Packit Service f9aed3
            }
Packit Service f9aed3
            src++;
Packit Service f9aed3
        }
Packit Service f9aed3
Packit Service f9aed3
        if (src < src_end) {
Packit Service f9aed3
            apr_bucket_split(e, src - bucket_data);
Packit Service f9aed3
        }
Packit Service f9aed3
        next = APR_BUCKET_NEXT(e);
Packit Service f9aed3
        apr_bucket_delete(e);
Packit Service f9aed3
        e = next;
Packit Service f9aed3
    }
Packit Service f9aed3
    *dst = 0;
Packit Service f9aed3
    return done;
Packit Service f9aed3
}
Packit Service f9aed3
Packit Service f9aed3
static int
Packit Service f9aed3
handle_request_ipc(request_rec *r, int role,
Packit Service f9aed3
                   apr_bucket_brigade *output_brigade,
Packit Service f9aed3
                   fcgid_bucket_ctx *bucket_ctx, const char **location_ptr)
Packit Service f9aed3
{
Packit Service f9aed3
    int cond_status;
Packit Service f9aed3
    apr_status_t rv;
Packit Service f9aed3
    apr_bucket_brigade *brigade_stdout;
Packit Service f9aed3
    char sbuf[MAX_STRING_LEN];
Packit Service f9aed3
    const char *location;
Packit Service f9aed3
Packit Service f9aed3
    /* Write output_brigade to fastcgi server */
Packit Service f9aed3
    if ((rv = proc_write_ipc(&bucket_ctx->ipc,
Packit Service f9aed3
                             output_brigade)) != APR_SUCCESS) {
Packit Service f9aed3
        bucket_ctx->has_error = 1;
Packit Service f9aed3
        return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    /* Create brigade */
Packit Service f9aed3
    brigade_stdout =
Packit Service f9aed3
        apr_brigade_create(r->pool, r->connection->bucket_alloc);
Packit Service f9aed3
    APR_BRIGADE_INSERT_TAIL(brigade_stdout,
Packit Service f9aed3
                            ap_bucket_fcgid_header_create(r->connection->
Packit Service f9aed3
                                                          bucket_alloc,
Packit Service f9aed3
                                                          bucket_ctx));
Packit Service f9aed3
Packit Service f9aed3
    /* Check the script header first; return immediately on error. */
Packit Service f9aed3
    if ((cond_status =
Packit Service f9aed3
         ap_scan_script_header_err_core(r, sbuf, getsfunc_fcgid_BRIGADE,
Packit Service f9aed3
                                        brigade_stdout))) {
Packit Service f9aed3
        /*
Packit Service f9aed3
         * cond_status could be HTTP_NOT_MODIFIED in the case that the FCGI
Packit Service f9aed3
         * script does not set an explicit status and ap_meets_conditions,
Packit Service f9aed3
         * which is called by ap_scan_script_header_err_brigade, detects that
Packit Service f9aed3
         * the conditions of the requests are met and the response is
Packit Service f9aed3
         * not modified.
Packit Service f9aed3
         * In this case set r->status and return OK in order to prevent
Packit Service f9aed3
         * running through the error processing stack as this would
Packit Service f9aed3
         * break with mod_cache, if the conditions had been set by
Packit Service f9aed3
         * mod_cache itself to validate a stale entity.
Packit Service f9aed3
         * BTW: We circumvent the error processing stack anyway if the
Packit Service f9aed3
         * FCGI script set an explicit status code (whatever it is) and
Packit Service f9aed3
         * the only possible values for cond_status here are:
Packit Service f9aed3
         *
Packit Service f9aed3
         * HTTP_NOT_MODIFIED          (set by ap_meets_conditions)
Packit Service f9aed3
         * HTTP_PRECONDITION_FAILED   (set by ap_meets_conditions)
Packit Service f9aed3
         * HTTP_GATEWAY_TIME_OUT      (script timed out, returned no headers)
Packit Service f9aed3
         * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the
Packit Service f9aed3
         * processing of the response of the FCGI script, e.g broken headers
Packit Service f9aed3
         * or a crashed FCGI process).
Packit Service f9aed3
         */
Packit Service f9aed3
        if (cond_status == HTTP_NOT_MODIFIED) {
Packit Service f9aed3
            /* We need to remove our fcgid_filter before returning this
Packit Service f9aed3
             * status and code; otherwise, when ap_process_async_request()
Packit Service f9aed3
             * invokes ap_finalize_request_protocol() and that calls
Packit Service f9aed3
             * ap_pass_brigade(), fcgid_filter notices it has an empty
Packit Service f9aed3
             * brigade and returns without calling ap_pass_brigade() itself,
Packit Service f9aed3
             * which incorrectly circumvents the standard output filters.
Packit Service f9aed3
             */
Packit Service f9aed3
            ap_remove_output_filter(r->output_filters);
Packit Service f9aed3
Packit Service f9aed3
            r->status = cond_status;
Packit Service f9aed3
            return OK;
Packit Service f9aed3
        }
Packit Service f9aed3
Packit Service f9aed3
        return cond_status;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    if (role == FCGI_AUTHORIZER) {
Packit Service f9aed3
        return cond_status;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    /* Check redirect */
Packit Service f9aed3
    location = apr_table_get(r->headers_out, "Location");
Packit Service f9aed3
Packit Service f9aed3
    if (location && location[0] == '/' && r->status == 200) {
Packit Service f9aed3
        /* This redirect needs to be a GET no matter what the original
Packit Service f9aed3
         * method was.
Packit Service f9aed3
         */
Packit Service f9aed3
        r->method = apr_pstrdup(r->pool, "GET");
Packit Service f9aed3
        r->method_number = M_GET;
Packit Service f9aed3
Packit Service f9aed3
        /* We already read the message body (if any), so don't allow
Packit Service f9aed3
         * the redirected request to think it has one.  We can ignore
Packit Service f9aed3
         * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
Packit Service f9aed3
         */
Packit Service f9aed3
        apr_table_unset(r->headers_in, "Content-Length");
Packit Service f9aed3
Packit Service f9aed3
        /* Setting this Location header value causes handle_request() to
Packit Service f9aed3
         * invoke ap_internal_redirect_handler(); that calls
Packit Service f9aed3
         * internal_internal_redirect() which sets the new sub-request's
Packit Service f9aed3
         * r->output_filters back to r->proto_output_filters before
Packit Service f9aed3
         * running the sub-request's handler.  Because we return here
Packit Service f9aed3
         * without invoking ap_pass_brigade(), our fcgid_filter is ignored.
Packit Service f9aed3
         */
Packit Service f9aed3
        *location_ptr = location;
Packit Service f9aed3
        return OK;
Packit Service f9aed3
    }
Packit Service f9aed3
    else if (location && r->status == 200) {
Packit Service f9aed3
        /* XX Note that if a script wants to produce its own Redirect
Packit Service f9aed3
         * body, it now has to explicitly *say* "Status: 302"
Packit Service f9aed3
         */
Packit Service f9aed3
Packit Service f9aed3
        /* This return code causes ap_process_async_request() to invoke
Packit Service f9aed3
         * ap_die(); that calls ap_send_error_response(), which resets
Packit Service f9aed3
         * r->output_filters back to r->proto_output_filters, thus removing
Packit Service f9aed3
         * our fcgid_filter from the output chain before making a final call
Packit Service f9aed3
         * to ap_finalize_request_protocol(), which passes the brigade to
Packit Service f9aed3
         * the standard output filters.
Packit Service f9aed3
         */
Packit Service f9aed3
        return HTTP_MOVED_TEMPORARILY;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    /* Now pass any remaining response body data to output filters */
Packit Service f9aed3
    if ((rv = ap_pass_brigade(r->output_filters,
Packit Service f9aed3
                              brigade_stdout)) != APR_SUCCESS) {
Packit Service f9aed3
        if (!APR_STATUS_IS_ECONNABORTED(rv)) {
Packit Service f9aed3
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r,
Packit Service f9aed3
                          "mod_fcgid: ap_pass_brigade failed in "
Packit Service f9aed3
                          "handle_request_ipc function");
Packit Service f9aed3
        }
Packit Service f9aed3
Packit Service f9aed3
        return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    return cond_status;
Packit Service f9aed3
}
Packit Service f9aed3
Packit Service f9aed3
static int
Packit Service f9aed3
handle_request(request_rec * r, int role, fcgid_cmd_conf *cmd_conf,
Packit Service f9aed3
               apr_bucket_brigade * output_brigade)
Packit Service f9aed3
{
Packit Service f9aed3
    fcgid_command fcgi_request;
Packit Service f9aed3
    fcgid_bucket_ctx *bucket_ctx;
Packit Service f9aed3
    int i, j, cond_status;
Packit Service f9aed3
    const char *location = NULL;
Packit Service f9aed3
Packit Service f9aed3
    bucket_ctx = apr_pcalloc(r->pool, sizeof(*bucket_ctx));
Packit Service f9aed3
Packit Service f9aed3
    bucket_ctx->ipc.request = r;
Packit Service f9aed3
    apr_pool_cleanup_register(r->pool, bucket_ctx,
Packit Service f9aed3
                              bucket_ctx_cleanup, apr_pool_cleanup_null);
Packit Service f9aed3
    procmgr_init_spawn_cmd(&fcgi_request, r, cmd_conf);
Packit Service f9aed3
Packit Service f9aed3
    /* Try to get a connected ipc handle */
Packit Service f9aed3
    for (i = 0; i < FCGID_REQUEST_COUNT; i++) {
Packit Service f9aed3
        /* Apply a free process slot, send a spawn request if I can't get one */
Packit Service f9aed3
        for (j = 0; j < FCGID_APPLY_TRY_COUNT; j++) {
Packit Service f9aed3
            bucket_ctx->ipc.connect_timeout =
Packit Service f9aed3
                fcgi_request.cmdopts.ipc_connect_timeout;
Packit Service f9aed3
            bucket_ctx->ipc.communation_timeout =
Packit Service f9aed3
                fcgi_request.cmdopts.ipc_comm_timeout;
Packit Service f9aed3
Packit Service f9aed3
            /* Apply a process slot */
Packit Service f9aed3
            bucket_ctx->procnode = apply_free_procnode(r, &fcgi_request);
Packit Service f9aed3
            if (bucket_ctx->procnode)
Packit Service f9aed3
                break;
Packit Service f9aed3
Packit Service f9aed3
            /* Avoid sleeping the very first time through if there are no
Packit Service f9aed3
               busy processes; the problem is just that we haven't spawned
Packit Service f9aed3
               anything yet, so waiting is pointless */
Packit Service f9aed3
            if (i > 0 || j > 0 || count_busy_processes(r, &fcgi_request)) {
Packit Service f9aed3
                apr_sleep(apr_time_from_sec(1));
Packit Service f9aed3
Packit Service f9aed3
                bucket_ctx->procnode = apply_free_procnode(r, &fcgi_request);
Packit Service f9aed3
                if (bucket_ctx->procnode)
Packit Service f9aed3
                    break;
Packit Service f9aed3
            }
Packit Service f9aed3
Packit Service f9aed3
            /* Send a spawn request if I can't get a process slot */
Packit Service f9aed3
            procmgr_send_spawn_cmd(&fcgi_request, r);
Packit Service f9aed3
        }
Packit Service f9aed3
Packit Service f9aed3
        /* Connect to the fastcgi server */
Packit Service f9aed3
        if (bucket_ctx->procnode) {
Packit Service f9aed3
            if (proc_connect_ipc(bucket_ctx->procnode,
Packit Service f9aed3
                                 &bucket_ctx->ipc) != APR_SUCCESS) {
Packit Service f9aed3
                proc_close_ipc(&bucket_ctx->ipc);
Packit Service f9aed3
                bucket_ctx->procnode->diewhy = FCGID_DIE_CONNECT_ERROR;
Packit Service f9aed3
                return_procnode(r, bucket_ctx->procnode, 1 /* has error */ );
Packit Service f9aed3
                bucket_ctx->procnode = NULL;
Packit Service f9aed3
            }
Packit Service f9aed3
            else
Packit Service f9aed3
                break;
Packit Service f9aed3
        }
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    /* Now I get a connected ipc handle */
Packit Service f9aed3
    if (!bucket_ctx->procnode) {
Packit Service f9aed3
        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
Packit Service f9aed3
                      "mod_fcgid: can't apply process slot for %s",
Packit Service f9aed3
                      cmd_conf->cmdline);
Packit Service f9aed3
        return HTTP_SERVICE_UNAVAILABLE;
Packit Service f9aed3
    }
Packit Service f9aed3
    bucket_ctx->active_time = bucket_ctx->procnode->last_active_time =
Packit Service f9aed3
        apr_time_now();
Packit Service f9aed3
    bucket_ctx->procnode->diewhy = FCGID_DIE_KILLSELF;
Packit Service f9aed3
Packit Service f9aed3
    cond_status = handle_request_ipc(r, role, output_brigade,
Packit Service f9aed3
                                     bucket_ctx, &location);
Packit Service f9aed3
Packit Service f9aed3
    /* Release the process ASAP.  This may already have been done in
Packit Service f9aed3
     * ap_pass_brigade() by fcgid_header_bucket_read(), but not in the
Packit Service f9aed3
     * case where handle_request_ipc() returned early without reading
Packit Service f9aed3
     * the body of the HTTP response.  This could be because of an error,
Packit Service f9aed3
     * or because of a role or a status code which permits us to ignore
Packit Service f9aed3
     * the message body.
Packit Service f9aed3
     *
Packit Service f9aed3
     * As an example, when handling a request in the FCGI_AUTHORIZER role,
Packit Service f9aed3
     * we don't read through to the end of the response from the process,
Packit Service f9aed3
     * we just read the HTTP headers.  That means each phase of the
Packit Service f9aed3
     * request handling sequence (e.g., authentication, authorization, etc.)
Packit Service f9aed3
     * will require its own process unless we make sure to always release
Packit Service f9aed3
     * any process we acquired regardless of whether we're reading the
Packit Service f9aed3
     * response body.
Packit Service f9aed3
     *
Packit Service f9aed3
     * As another example, if we perform or cause an internal redirection
Packit Service f9aed3
     * (for instance, by returning an error code that invokes a script
Packit Service f9aed3
     * handler in ap_die() because of an ErrorDocument configuration), then
Packit Service f9aed3
     * we must also release the process we acquired here so that it is
Packit Service f9aed3
     * potentially available during the next handling phase.
Packit Service f9aed3
     */
Packit Service f9aed3
Packit Service f9aed3
    apr_pool_cleanup_run(r->pool, bucket_ctx, bucket_ctx_cleanup);
Packit Service f9aed3
Packit Service f9aed3
    /* Perform internal redirection if necessary */
Packit Service f9aed3
    if (location) {
Packit Service f9aed3
        ap_internal_redirect_handler(location, r);
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service f9aed3
    /* Return condition status */
Packit Service f9aed3
    return cond_status;
Packit Service f9aed3
}
Packit Service f9aed3
Packit Service f9aed3
static int add_request_body(request_rec *r, apr_pool_t *request_pool,
Packit Service af40fa
                            apr_bucket_brigade *output_brigade)
Packit Service f9aed3
{
Packit Service f9aed3
    apr_bucket *bucket_input, *bucket_header;
Packit Service f9aed3
    apr_file_t *fd = NULL;
Packit Service f9aed3
    apr_off_t cur_pos = 0, request_size = 0;
Packit Service f9aed3
    apr_status_t rv;
Packit Service f9aed3
    FCGI_Header *stdin_request_header;
Packit Service f9aed3
    fcgid_server_conf *sconf = ap_get_module_config(r->server->module_config,
Packit Service f9aed3
                                                    &fcgid_module);
Packit Service f9aed3
    int seen_eos = 0;
Packit Service f9aed3
Packit Service f9aed3
    /* Stdin header and body */
Packit Service f9aed3
    /* I have to read all the request into memory before sending it
Packit Service f9aed3
       to fastcgi application server, this prevents slow clients from
Packit Service f9aed3
       keeping the server in processing too long.
Packit Service f9aed3
       But sometimes it's not acceptable (think about uploading a large attachment)
Packit Service f9aed3
       Request will be stored in tmp file if the size larger than max_mem_request_len
Packit Service f9aed3
     */
Packit Service f9aed3
Packit Service f9aed3
    apr_bucket_brigade *input_brigade = apr_brigade_create(request_pool,
Packit Service f9aed3
                                                           r->connection->
Packit Service f9aed3
                                                           bucket_alloc);
Packit Service f9aed3
    apr_bucket_brigade *tmp_brigade = apr_brigade_create(request_pool,
Packit Service f9aed3
                                                           r->connection->
Packit Service f9aed3
                                                           bucket_alloc);
Packit Service f9aed3
Packit Service f9aed3
    do {
Packit Service f9aed3
        int loop_counter = 0;
Packit Service f9aed3
Packit Service f9aed3
        if ((rv = ap_get_brigade(r->input_filters, input_brigade,
Packit Service f9aed3
                                 AP_MODE_READBYTES,
Packit Service f9aed3
                                 APR_BLOCK_READ,
Packit Service f9aed3
                                 HUGE_STRING_LEN)) != APR_SUCCESS) {
Packit Service f9aed3
            ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r,
Packit Service f9aed3
                          "mod_fcgid: can't get data from http client");
Packit Service f9aed3
            apr_brigade_destroy(output_brigade);
Packit Service f9aed3
            apr_brigade_destroy(tmp_brigade);
Packit Service f9aed3
            apr_brigade_destroy(input_brigade);
Packit Service f9aed3
            return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
        }
Packit Service f9aed3
Packit Service f9aed3
	
Packit Service f9aed3
Packit Service f9aed3
        while ((bucket_input = APR_BRIGADE_FIRST(input_brigade)) != APR_BRIGADE_SENTINEL(input_brigade)) {
Packit Service f9aed3
            const char *data;
Packit Service f9aed3
            apr_size_t len;
Packit Service f9aed3
            apr_bucket *bucket_stdin;
Packit Service f9aed3
Packit Service f9aed3
            ++loop_counter;
Packit Service f9aed3
            if ((loop_counter % FCGID_BRIGADE_CLEAN_STEP) == 0) {
Packit Service f9aed3
                apr_brigade_cleanup(tmp_brigade);
Packit Service f9aed3
            }
Packit Service f9aed3
            APR_BUCKET_REMOVE(bucket_input);
Packit Service f9aed3
            APR_BRIGADE_INSERT_TAIL(tmp_brigade, bucket_input);
Packit Service f9aed3
Packit Service f9aed3
            if (APR_BUCKET_IS_EOS(bucket_input)) {
Packit Service f9aed3
                seen_eos = 1;
Packit Service f9aed3
                break;
Packit Service f9aed3
            }
Packit Service f9aed3
Packit Service f9aed3
            if (APR_BUCKET_IS_METADATA(bucket_input))
Packit Service f9aed3
                continue;
Packit Service f9aed3
Packit Service f9aed3
            if ((rv = apr_bucket_read(bucket_input, &data, &len,
Packit Service f9aed3
                                      APR_BLOCK_READ)) != APR_SUCCESS) {
Packit Service f9aed3
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r,
Packit Service f9aed3
                              "mod_fcgid: can't read request from HTTP client");
Packit Service f9aed3
                apr_brigade_destroy(input_brigade);
Packit Service f9aed3
                apr_brigade_destroy(tmp_brigade);
Packit Service f9aed3
                apr_brigade_destroy(output_brigade);
Packit Service f9aed3
                return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
            }
Packit Service f9aed3
Packit Service f9aed3
            /* Append a header, and the the bucket */
Packit Service f9aed3
            stdin_request_header = apr_bucket_alloc(sizeof(FCGI_Header),
Packit Service f9aed3
                                                    r->connection->
Packit Service f9aed3
                                                    bucket_alloc);
Packit Service f9aed3
            bucket_header =
Packit Service f9aed3
                apr_bucket_heap_create((const char *) stdin_request_header,
Packit Service f9aed3
                                       sizeof(*stdin_request_header),
Packit Service f9aed3
                                       apr_bucket_free,
Packit Service f9aed3
                                       r->connection->bucket_alloc);
Packit Service f9aed3
Packit Service f9aed3
            request_size += len;
Packit Service f9aed3
            if (request_size > sconf->max_request_len) {
Packit Service f9aed3
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
Packit Service f9aed3
                              "mod_fcgid: HTTP request length %" APR_OFF_T_FMT
Packit Service f9aed3
                              " (so far) exceeds MaxRequestLen (%"
Packit Service f9aed3
                              APR_OFF_T_FMT ")", request_size,
Packit Service f9aed3
                              sconf->max_request_len);
Packit Service f9aed3
                return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
            }
Packit Service f9aed3
Packit Service f9aed3
            if (request_size > sconf->max_mem_request_len) {
Packit Service f9aed3
                apr_size_t wrote_len;
Packit Service f9aed3
                static const char *fd_key = "fcgid_fd";
Packit Service f9aed3
Packit Service f9aed3
                if (fd == NULL) {
Packit Service f9aed3
                    void *tmp;
Packit Service f9aed3
                    apr_pool_userdata_get(&tmp, fd_key, r->connection->pool);
Packit Service f9aed3
                    fd = tmp;
Packit Service f9aed3
Packit Service f9aed3
                    if (fd != NULL) {
Packit Service f9aed3
                        if ((rv = apr_file_trunc(fd, 0)) != APR_SUCCESS) {
Packit Service f9aed3
                            ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r,
Packit Service f9aed3
                                          "mod_fcgid: can't truncate existing "
Packit Service f9aed3
                                          "temporary file");
Packit Service f9aed3
                            return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
                        }
Packit Service f9aed3
                    }
Packit Service f9aed3
                }
Packit Service f9aed3
Packit Service f9aed3
                if (fd == NULL) {
Packit Service f9aed3
                    const char *tempdir = NULL;
Packit Service f9aed3
                    char *template;
Packit Service f9aed3
Packit Service f9aed3
                    rv = apr_temp_dir_get(&tempdir, r->pool);
Packit Service f9aed3
                    if (rv != APR_SUCCESS) {
Packit Service f9aed3
                        ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r,
Packit Service f9aed3
                                      "mod_fcgid: can't get tmp dir");
Packit Service f9aed3
                        return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
                    }
Packit Service f9aed3
Packit Service f9aed3
                    apr_filepath_merge(&template, tempdir,
Packit Service f9aed3
                                       "fcgid.tmp.XXXXXX",
Packit Service f9aed3
                                       APR_FILEPATH_NATIVE, r->pool);
Packit Service f9aed3
                    rv = apr_file_mktemp(&fd, template, 0,
Packit Service f9aed3
                                         r->connection->pool);
Packit Service f9aed3
                    if (rv != APR_SUCCESS) {
Packit Service f9aed3
                        ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r,
Packit Service f9aed3
                                      "mod_fcgid: can't open tmp file fot stdin request");
Packit Service f9aed3
                        return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
                    }
Packit Service f9aed3
                    apr_pool_userdata_set((const void *) fd, fd_key,
Packit Service f9aed3
                                          apr_pool_cleanup_null,
Packit Service f9aed3
                                          r->connection->pool);
Packit Service f9aed3
                }
Packit Service f9aed3
Packit Service f9aed3
                /* Write request to tmp file */
Packit Service f9aed3
                if ((rv =
Packit Service f9aed3
                     apr_file_write_full(fd, (const void *) data, len,
Packit Service f9aed3
                                         &wrote_len)) != APR_SUCCESS
Packit Service f9aed3
                    || len != wrote_len) {
Packit Service f9aed3
                    ap_log_rerror(APLOG_MARK, APLOG_WARNING,
Packit Service f9aed3
                                  rv, r,
Packit Service f9aed3
                                  "mod_fcgid: can't write tmp file for stdin request");
Packit Service f9aed3
                    return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
                }
Packit Service f9aed3
                /* Create file bucket */
Packit Service f9aed3
                bucket_stdin =
Packit Service f9aed3
                    apr_bucket_file_create(fd, cur_pos, len, r->pool,
Packit Service f9aed3
                                           r->connection->bucket_alloc);
Packit Service f9aed3
                cur_pos += len;
Packit Service f9aed3
            }
Packit Service f9aed3
            else {
Packit Service f9aed3
                if (APR_BUCKET_IS_HEAP(bucket_input))
Packit Service f9aed3
                    apr_bucket_copy(bucket_input, &bucket_stdin);
Packit Service f9aed3
                else {
Packit Service f9aed3
                    /* mod_ssl have a bug? */
Packit Service f9aed3
                    char *pcopydata =
Packit Service f9aed3
                        apr_bucket_alloc(len, r->connection->bucket_alloc);
Packit Service f9aed3
                    memcpy(pcopydata, data, len);
Packit Service f9aed3
                    bucket_stdin =
Packit Service f9aed3
                        apr_bucket_heap_create(pcopydata, len,
Packit Service f9aed3
                                               apr_bucket_free,
Packit Service f9aed3
                                               r->connection->bucket_alloc);
Packit Service f9aed3
                }
Packit Service f9aed3
            }
Packit Service f9aed3
Packit Service f9aed3
            if (!init_header(FCGI_STDIN, 1, len, 0, stdin_request_header)) {
Packit Service f9aed3
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
Packit Service f9aed3
                              "mod_fcgid: header overflow");
Packit Service f9aed3
                apr_brigade_destroy(input_brigade);
Packit Service f9aed3
                apr_brigade_destroy(tmp_brigade);
Packit Service f9aed3
                apr_brigade_destroy(output_brigade);
Packit Service f9aed3
                return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
            }
Packit Service f9aed3
            APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_header);
Packit Service f9aed3
            APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_stdin);
Packit Service f9aed3
        }
Packit Service f9aed3
Packit Service f9aed3
        apr_brigade_cleanup(input_brigade);
Packit Service f9aed3
        apr_brigade_cleanup(tmp_brigade);
Packit Service f9aed3
    }
Packit Service f9aed3
    while (!seen_eos);
Packit Service f9aed3
Packit Service f9aed3
    apr_brigade_destroy(input_brigade);
Packit Service f9aed3
    apr_brigade_destroy(tmp_brigade);
Packit Service f9aed3
Packit Service f9aed3
    /* Append an empty body stdin header */
Packit Service f9aed3
    stdin_request_header = apr_bucket_alloc(sizeof(FCGI_Header),
Packit Service f9aed3
                                            r->connection->bucket_alloc);
Packit Service f9aed3
    bucket_header =
Packit Service f9aed3
        apr_bucket_heap_create((const char *) stdin_request_header,
Packit Service f9aed3
                               sizeof(*stdin_request_header),
Packit Service f9aed3
                               apr_bucket_free, r->connection->bucket_alloc);
Packit Service f9aed3
    if (!init_header(FCGI_STDIN, 1, 0, 0, stdin_request_header)) {
Packit Service f9aed3
        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
Packit Service f9aed3
                      "mod_fcgid: header overflow");
Packit Service f9aed3
        return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
    }
Packit Service f9aed3
    APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_header);
Packit Service f9aed3
Packit Service f9aed3
    return 0;
Packit Service f9aed3
}
Packit Service f9aed3
Packit Service f9aed3
int bridge_request(request_rec * r, int role, fcgid_cmd_conf *cmd_conf)
Packit Service f9aed3
{
Packit Service af40fa
    apr_bucket_brigade *output_brigade;
Packit Service f9aed3
    apr_bucket *bucket_eos;
Packit Service af40fa
    char **envp = ap_create_environment(r->pool,
Packit Service af40fa
                                        r->subprocess_env);
Packit Service f9aed3
    int rc;
Packit Service f9aed3
Packit Service f9aed3
    /* Create brigade for the request to fastcgi server */
Packit Service f9aed3
    output_brigade =
Packit Service f9aed3
        apr_brigade_create(r->pool, r->connection->bucket_alloc);
Packit Service f9aed3
Packit Service af40fa
    /* Build the begin request and environ request, append them to output_brigade */
Packit Service f9aed3
    if (!build_begin_block
Packit Service f9aed3
        (role, r, r->connection->bucket_alloc, output_brigade)
Packit Service f9aed3
        || !build_env_block(r, envp, r->connection->bucket_alloc,
Packit Service f9aed3
                            output_brigade)) {
Packit Service f9aed3
        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
Packit Service f9aed3
                      "mod_fcgid: can't build begin or env request");
Packit Service f9aed3
        return HTTP_INTERNAL_SERVER_ERROR;
Packit Service f9aed3
    }
Packit Service f9aed3
Packit Service af40fa
    if (role == FCGI_RESPONDER) {
Packit Service af40fa
        rc = add_request_body(r, r->pool, output_brigade);
Packit Service af40fa
        if (rc) {
Packit Service af40fa
            return rc;
Packit Service af40fa
        }
Packit Service af40fa
    }
Packit Service f9aed3
Packit Service f9aed3
    /* The eos bucket now */
Packit Service f9aed3
    bucket_eos = apr_bucket_eos_create(r->connection->bucket_alloc);
Packit Service f9aed3
    APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_eos);
Packit Service f9aed3
Packit Service f9aed3
    /* Bridge the request */
Packit Service f9aed3
    return handle_request(r, role, cmd_conf, output_brigade);
Packit Service f9aed3
}