From 048ac11565aca7457873f5ec299101ae0e794e16 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sun, 3 Oct 2010 07:35:34 +0200 Subject: [PATCH] Added a simple access logger. --- src/.gitignore | 1 + src/Makefile.am | 2 +- src/accesslog.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 src/accesslog.c diff --git a/src/.gitignore b/src/.gitignore index 4489a47..8075a53 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -6,3 +6,4 @@ /userplex /htls /callscgi +/accesslog diff --git a/src/Makefile.am b/src/Makefile.am index cac55a3..f7aecb1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ SUBDIRS = dirplex -bin_PROGRAMS = htparser sendfile callcgi patplex userplex htls callscgi +bin_PROGRAMS = htparser sendfile callcgi patplex userplex htls callscgi accesslog noinst_PROGRAMS = debugsink htparser_SOURCES = htparser.c htparser.h plaintcp.c ssl-gnutls.c diff --git a/src/accesslog.c b/src/accesslog.c new file mode 100644 index 0000000..4961483 --- /dev/null +++ b/src/accesslog.c @@ -0,0 +1,253 @@ +/* + ashd - A Sane HTTP Daemon + Copyright (C) 2008 Fredrik Tolf + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include + +#define DEFFORMAT "%{%Y-%m-%d %H:%M:%S}t %m %u %A \"%G\"" + +static int ch; +static FILE *out; +static int flush = 1; +static char *format; +static time_t now; + +static void qputs(char *s, FILE *o) +{ + for(; *s; s++) { + if(*s == '\"') { + fputs("\\\"", o); + } else if(*s == '\\') { + fputs("\\\\", o); + } else if(*s == '\n') { + fputs("\\n", o); + } else if(*s == '\t') { + fputs("\\t", o); + } else if((*s < 32) || (*s >= 128)) { + fprintf(o, "\\x%02x", *s); + } else { + fputc(*s, o); + } + } +} + +static void logitem(struct hthead *req, char o, char *d) +{ + char *h, *p; + char buf[1024]; + struct timeval tv; + + switch(o) { + case '%': + putc('%', out); + break; + case 'h': + if((h = getheader(req, d)) == NULL) { + putc('-', out); + } else { + qputs(h, out); + } + break; + case 'u': + qputs(req->url, out); + break; + case 'U': + strcpy(buf, req->url); + if((p = strchr(buf, '?')) != NULL) + *p = 0; + qputs(buf, out); + break; + case 'm': + qputs(req->method, out); + break; + case 'r': + qputs(req->rest, out); + break; + case 'v': + qputs(req->ver, out); + break; + case 't': + if(!*d) + d = "%a, %d %b %Y %H:%M:%S %z"; + strftime(buf, sizeof(buf), d, localtime(&now)); + qputs(buf, out); + break; + case 'T': + if(!*d) + d = "%a, %d %b %Y %H:%M:%S %z"; + strftime(buf, sizeof(buf), d, gmtime(&now)); + qputs(buf, out); + break; + case 's': + gettimeofday(&tv, NULL); + fprintf(out, "%06i", (int)tv.tv_usec); + break; + case 'A': + logitem(req, 'h', "X-Ash-Address"); + break; + case 'H': + logitem(req, 'h', "Host"); + break; + case 'R': + logitem(req, 'h', "Referer"); + break; + case 'G': + logitem(req, 'h', "User-Agent"); + break; + } +} + +static void logreq(struct hthead *req) +{ + char *p, *p2; + char d[strlen(format)]; + char o; + + p = format; + while(*p) { + if(*p == '%') { + p++; + if(*p == '{') { + p++; + if((p2 = strchr(p, '}')) == NULL) + continue; + memcpy(d, p, p2 - p); + d[p2 - p] = 0; + p = p2 + 1; + } else { + d[0] = 0; + } + o = *p++; + if(o == 0) + break; + logitem(req, o, d); + } else { + fputc(*p++, out); + } + } + fputc('\n', out); + if(flush) + fflush(out); +} + +static void serve(struct hthead *req, int fd) +{ + now = time(NULL); + if(sendreq(ch, req, fd)) { + flog(LOG_ERR, "accesslog: could not pass request to child: %s", strerror(errno)); + exit(1); + } + logreq(req); +} + +static void usage(FILE *out) +{ + fprintf(out, "usage: accesslog [-hFa] [-f FORMAT] [-o OUTFILE] CHILD [ARGS...]\n"); +} + +int main(int argc, char **argv) +{ + int c, ret; + struct hthead *req; + int fd; + struct pollfd pfd[2]; + char *outfile; + + optarg = NULL; + while((c = getopt(argc, argv, "+hFaf:o:")) >= 0) { + switch(c) { + case 'h': + usage(stdout); + exit(0); + case 'o': + outfile = optarg; + break; + case 'F': + flush = 0; + break; + case 'f': + format = optarg; + break; + case 'a': + format = "%A - - [%{%d/%b/%Y:%H:%M:%S %z}t] \"%m %u %v\" - - \"%R\" \"%G\""; + break; + default: + usage(stderr); + exit(1); + } + } + if(optind >= argc) { + usage(stderr); + exit(1); + } + if(format == NULL) + format = DEFFORMAT; + if(outfile != NULL) { + if((out = fopen(outfile, "a")) == NULL) { + flog(LOG_ERR, "accesslog: could not open %s for logging: %s", outfile, strerror(errno)); + exit(1); + } + } else { + out = stdout; + } + if((ch = stdmkchild(argv + optind, NULL, NULL)) < 0) { + flog(LOG_ERR, "accesslog: could fork child: %s", strerror(errno)); + exit(1); + } + while(1) { + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = 0; + pfd[0].events = POLLIN; + pfd[1].fd = ch; + pfd[1].events = POLLHUP; + if((ret = poll(pfd, 2, -1)) < 0) { + if(errno != EINTR) { + flog(LOG_ERR, "accesslog: error in poll: %s", strerror(errno)); + exit(1); + } + } + if(pfd[0].revents) { + if((fd = recvreq(0, &req)) < 0) { + if(errno == 0) + break; + flog(LOG_ERR, "accesslog: error in recvreq: %s", strerror(errno)); + exit(1); + } + serve(req, fd); + freehthead(req); + close(fd); + } + if(pfd[1].revents & POLLHUP) + break; + } + return(0); +} -- 2.11.0