/* 
 *  Copyright 2007 IAC Search & Media.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 *  Provides timers for the entire request cylce in Apache, including the time
 *  spent logging.
 *
 *  Configuration is currently done at compile time.  Change the variables 
 *  bellow:
 */

#define TIMER_LOG_PATH "/var/tmp/httpd_timer_log"
#define TIMER_MAX_LINE_LEN (256)

/*
 * mod_timer works by using several hooks in apache:
 *      - process_connection: Start Connection timer.
 *      - process_request: Start Request timer.
 *      - request_rec->p cleanup: End Request timer.
 *      - conn_rec->p cleanup: End Connection timer.
 */

/*
 * mod_timer writes to a log file with the following format:
 *      c:(remote ip):(remote port):(time of start):(duration)\n
 *      r:(remote ip):(remote port):(time of start):(duration)\n
 * Where 'c' means a connection and 'r' means a request.
 * Time of start is expressed as an apr_time_t.
 * Duration is expressed in microns.
 *  For example:
 *      r:1.2.3.4:4331:1:134485
 *      r:1.2.3.4:4331:2:445
 *      c:1.2.3.4:4331:1:134930
 * This would be a keep alive request where two requests were made inside a
 * connection.
 */


#include "httpd.h"
#include "http_core.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_log.h"

static int g_logfile = -1;

typedef struct timer_point_t {
    apr_sockaddr_t *client;
    apr_time_t start;
} timer_point_t;

static apr_status_t timer_end(timer_point_t *t, char endtype)
{
    /* yes, this is evil. */
    if (g_logfile >= 0) {
        int len;
        char buf[TIMER_MAX_LINE_LEN];
        apr_time_t end = apr_time_now();

        len = apr_snprintf(buf, sizeof(buf), "%c:%pI:%"APR_TIME_T_FMT":%"APR_TIME_T_FMT"\n",
                           endtype, t->client, t->start, (end - t->start));

        if (len > 0) {
            write(g_logfile, buf, len);
        }
    }

    return APR_SUCCESS;
}

static apr_status_t timer_end_conn(void *baton)
{
    timer_point_t *t = (timer_point_t*)baton;
    return timer_end(t, 'c');
}

static apr_status_t timer_end_req(void *baton)
{
    timer_point_t *t = (timer_point_t*)baton;
    return timer_end(t, 'r');
}


int timer_process_conn(conn_rec *c)
{
    if (g_logfile < 0) {
        return DECLINED;
    }
    
    timer_point_t *t = apr_palloc(c->pool, sizeof(timer_point_t));

    t->start = apr_time_now();
    t->client = c->remote_addr;

    apr_pool_cleanup_register(c->pool, t, timer_end_conn,
                              apr_pool_cleanup_null);

    return DECLINED;
}

static int timer_create_req(request_rec *r)
{
    if (g_logfile < 0) {
        return DECLINED;
    }

    timer_point_t *t = apr_palloc(r->pool, sizeof(timer_point_t));
    
    t->start = apr_time_now();
    t->client = r->connection->remote_addr;
    
    apr_pool_cleanup_register(r->pool, t, timer_end_req,
                              apr_pool_cleanup_null);
    
    return DECLINED;
}

void timer_child_init(apr_pool_t *p, server_rec *s)
{
    g_logfile = open(TIMER_LOG_PATH, O_WRONLY|O_APPEND|O_CREAT, 0644);

    if (g_logfile < 0) {
        /* XXXXX: hack/abuse/killme */
        apr_status_t rv =  errno;
        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
                     "[mod_timer] - Failed to open '%s' for writing!!", 
                     TIMER_LOG_PATH);
    }
}

static void timer_register_hooks(apr_pool_t * p)
{

    ap_hook_child_init(timer_child_init,
                       NULL, NULL, APR_HOOK_FIRST);

    ap_hook_process_connection(timer_process_conn, 
                                NULL, NULL, APR_HOOK_FIRST);

    ap_hook_create_request(timer_create_req,
                           NULL, NULL, APR_HOOK_FIRST);
}

/* XXXXX: make the logging path configurable. */
static const command_rec timer_cmds[] = {
    {NULL}
};

module AP_MODULE_DECLARE_DATA timer_module = {
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    timer_cmds,
    timer_register_hooks,
};

