Goto sanos source index
//
// hlog.c
//
// HTTP Logging
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <os.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <time.h>
#include <httpd.h>
#define MAX_LOGLINE_SIZE (32 * 1024)
char *logfieldnames[] = {
"date",
"time",
"time-taken",
"c-ip",
"s-ip",
"s-port",
"cs-uri",
"cs-uri-stem",
"cs-uri-query",
"cs-method",
"cs-bytes",
"cs(user-agent)",
"cs(referer)",
"cs(cookie)",
"cs(host)",
"cs-username",
"cs-protocol",
"sc-bytes",
"sc-status",
NULL
};
int parse_log_columns(struct httpd_server *server, char *fields) {
char *p;
char *q;
int n;
char str[128];
server->nlogcolumns = 0;
p = fields;
if (!p) return 0;
while (*p && server->nlogcolumns < HTTP_NLOGCOLUMNS - 1) {
while (*p == ' ') p++;
q = str;
while (*p && *p != ' ') *q++ = *p++;
*q = 0;
n = 0;
while (logfieldnames[n] && stricmp(str, logfieldnames[n]) != 0) n++;
if (logfieldnames[n]) server->logcoumns[server->nlogcolumns++] = n;
}
return 0;
}
int write_log(struct httpd_server *server, char *data, int len, struct tm *tm) {
int year = tm->tm_year + 1900;
int mon = tm->tm_mon + 1;
int day = tm->tm_mday;
if (year != server->logyear || mon != server->logmon || day != server->logday) {
char logfn[MAXPATH];
char buf[1024];
int n;
if (server->logfd >= 0) close(server->logfd);
sprintf(logfn, "%s/web%04d%02d%02d.log", server->logdir, year, mon, day);
server->logfd = open(logfn, O_CREAT | O_APPEND, S_IREAD | S_IWRITE);
if (server->logfd < 0) return -1;
sprintf(buf, "#Server: %s\r\n", server->swname);
write(server->logfd, buf, strlen(buf));
write(server->logfd, "#Version: 1.0\r\n", 15);
write(server->logfd, "#Fields: ", 9);
for (n = 0; n < server->nlogcolumns; n++) {
if (n > 0) write(server->logfd, " ", 1);
write(server->logfd, logfieldnames[server->logcoumns[n]], strlen(logfieldnames[server->logcoumns[n]]));
}
write(server->logfd, "\r\n", 2);
sprintf(buf, "#Date: %04d-%02d-%02d %02d:%02d:%02d\r\n", year, mon, day, tm->tm_hour, tm->tm_min, tm->tm_sec);
write(server->logfd, buf, strlen(buf));
server->logyear = year;
server->logmon = mon;
server->logday = day;
}
return write(server->logfd, data, len);
}
int log_request(struct httpd_request *req) {
char line[MAX_LOGLINE_SIZE];
char *p = line;
char *end = line + MAX_LOGLINE_SIZE;
struct httpd_connection *conn = req->conn;
struct httpd_response *rsp = conn->rsp;
time_t now = time(0);
struct tm *tm = gmtime(&now);
char buf[32];
char *value;
int n;
int field;
if (req->conn->server->nlogcolumns == 0 || req->conn->server->logdir == NULL) return 0;
for (n = 0; n < req->conn->server->nlogcolumns; n++) {
field = req->conn->server->logcoumns[n];
value = buf;
switch (field) {
case HTTP_LOG_DATE:
strftime(buf, sizeof(buf), "%Y-%m-%d", tm);
break;
case HTTP_LOG_TIME:
strftime(buf, sizeof(buf), "%H:%M:%S", tm);
break;
case HTTP_LOG_TIME_TAKEN:
value = "0";
break;
case HTTP_LOG_C_IP:
strcpy(buf, inet_ntoa(conn->client_addr.sa_in.sin_addr));
break;
case HTTP_LOG_S_IP:
strcpy(buf, inet_ntoa(conn->server_addr.sa_in.sin_addr));
break;
case HTTP_LOG_S_PORT:
sprintf(buf, "%d", ntohs(conn->server_addr.sa_in.sin_port));
break;
case HTTP_LOG_CS_URI:
case HTTP_LOG_CS_URI_STEM:
value = req->decoded_url;
break;
case HTTP_LOG_CS_URI_QUERY:
value = req->query;
break;
case HTTP_LOG_CS_METHOD:
value = req->method;
break;
case HTTP_LOG_CS_BYTES:
if (req->content_length > 0)
sprintf(buf, "%d", req->content_length);
else
value = NULL;
break;
case HTTP_LOG_CS_USER_AGENT:
value = req->user_agent;
break;
case HTTP_LOG_CS_REFERER:
value = req->referer;
break;
case HTTP_LOG_CS_COOKIE:
value = req->cookie;
break;
case HTTP_LOG_CS_HOST:
value = req->host;
break;
case HTTP_LOG_CS_USERNAME:
value = req->username;
break;
case HTTP_LOG_CS_PROTOCOL:
value = req->protocol;
break;
case HTTP_LOG_SC_BYTES:
if (rsp->content_length > 0)
sprintf(buf, "%d", rsp->content_length);
else
value = NULL;
break;
case HTTP_LOG_SC_STATUS:
sprintf(buf, "%d", rsp->status);
break;
}
if (!value || !*value) value = "-";
if (n > 0) {
if (p == end) {
errno = EBUF;
return -1;
}
*p++ = ' ';
}
while (*value) {
if (p == end) {
errno = EBUF;
return -1;
}
if (*value == ' ') {
*p++ = '+';
} else {
*p++ = *value;
}
value++;
}
}
if (p == end) {
errno = EBUF;
return -1;
}
*p++ = '\r';
if (p == end) {
errno = EBUF;
return -1;
}
*p++ = '\n';
if (p == end) {
errno = EBUF;
return -1;
}
*p++ = 0;
enter(&conn->server->srvlock);
write_log(conn->server, line, p - line - 1, tm);
leave(&conn->server->srvlock);
return p - line - 1;
}