From ea62a3793f9483f0078d2866044df19229decc37 Mon Sep 17 00:00:00 2001 From: doug Date: Fri, 1 Aug 2014 21:51:02 +0000 Subject: [PATCH] Add common and combined access logging to httpd. ok reyk@ --- usr.sbin/httpd/httpd.conf.5 | 15 +++++- usr.sbin/httpd/httpd.h | 11 ++++- usr.sbin/httpd/parse.y | 18 ++++++- usr.sbin/httpd/server_http.c | 91 +++++++++++++++++++++++++++++++++++- 4 files changed, 129 insertions(+), 6 deletions(-) diff --git a/usr.sbin/httpd/httpd.conf.5 b/usr.sbin/httpd/httpd.conf.5 index aa651a0f2a9..c171b208381 100644 --- a/usr.sbin/httpd/httpd.conf.5 +++ b/usr.sbin/httpd/httpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: httpd.conf.5,v 1.12 2014/07/31 14:18:38 reyk Exp $ +.\" $OpenBSD: httpd.conf.5,v 1.13 2014/08/01 21:51:02 doug Exp $ .\" .\" Copyright (c) 2014 Reyk Floeter .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 31 2014 $ +.Dd $Mdocdate: August 1 2014 $ .Dt HTTPD.CONF 5 .Os .Sh NAME @@ -150,6 +150,17 @@ except .Ic listen on and .Ic location . +.It Oo Ic no Oc Ic log Op Ar style +Enable writing an access log to syslog. +The +.Ar style +can be +.Ar common +or +.Ar combined . +It is similar to the Apache and nginx logging format. +If not specified, the default is +.Ar common . .It Ic root Ar directory Set the document root of the server. The diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h index 8ea75db4e95..ad467b4b4f9 100644 --- a/usr.sbin/httpd/httpd.h +++ b/usr.sbin/httpd/httpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.h,v 1.27 2014/08/01 08:34:46 florian Exp $ */ +/* $OpenBSD: httpd.h,v 1.28 2014/08/01 21:51:02 doug Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -310,6 +310,12 @@ SPLAY_HEAD(client_tree, client); "\10\01NODELAY\02NO_NODELAY\03SACK\04NO_SACK" \ "\05SOCKET_BUFFER_SIZE\06IP_TTL\07IP_MINTTL\10NO_SPLICE" +enum log_format { + LOG_FORMAT_NONE = -1, + LOG_FORMAT_COMMON = 0, + LOG_FORMAT_COMBINED, +}; + struct server_config { u_int32_t id; char name[MAXHOSTNAMELEN]; @@ -329,6 +335,8 @@ struct server_config { u_int8_t tcpipttl; u_int8_t tcpipminttl; + enum log_format logformat; + TAILQ_ENTRY(server_config) entry; }; TAILQ_HEAD(serverhosts, server_config); @@ -443,6 +451,7 @@ int server_response(struct httpd *, struct client *); const char * server_http_host(struct sockaddr_storage *, char *, size_t); void server_http_date(char *, size_t); +int server_log_http(struct client *, u_int, size_t); /* server_file.c */ int server_file(struct httpd *, struct client *); diff --git a/usr.sbin/httpd/parse.y b/usr.sbin/httpd/parse.y index 91e0197970f..2ec30bdb753 100644 --- a/usr.sbin/httpd/parse.y +++ b/usr.sbin/httpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.13 2014/07/31 14:18:38 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.14 2014/08/01 21:51:02 doug Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter @@ -128,7 +128,7 @@ typedef struct { %token ALL AUTO DIRECTORY FCGI INDEX LISTEN LOCATION LOG NO ON PORT %token PREFORK ROOT SERVER SOCKET TYPES UPDATES VERBOSE -%token ERROR INCLUDE +%token ERROR INCLUDE COMMON COMBINED %token STRING %token NUMBER %type loglevel @@ -301,6 +301,7 @@ serveroptsl : LISTEN ON STRING port { } | DIRECTORY dirflags | DIRECTORY '{' dirflags_l '}' + | logformat | fastcgi | LOCATION STRING { struct server *s; @@ -442,6 +443,17 @@ dirflags : INDEX STRING { } ; +logformat : LOG COMMON { + srv->srv_conf.logformat = LOG_FORMAT_COMMON; + } + | LOG COMBINED { + srv->srv_conf.logformat = LOG_FORMAT_COMBINED; + } + | NO LOG { + srv->srv_conf.logformat = LOG_FORMAT_NONE; + } + ; + types : TYPES '{' optnl mediaopts_l '}' ; @@ -575,6 +587,8 @@ lookup(char *s) static const struct keywords keywords[] = { { "all", ALL }, { "auto", AUTO }, + { "combined", COMBINED }, + { "common", COMMON }, { "directory", DIRECTORY }, { "fastcgi", FCGI }, { "include", INCLUDE }, diff --git a/usr.sbin/httpd/server_http.c b/usr.sbin/httpd/server_http.c index 7849c61630e..719b9700d6b 100644 --- a/usr.sbin/httpd/server_http.c +++ b/usr.sbin/httpd/server_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_http.c,v 1.25 2014/07/31 18:07:11 reyk Exp $ */ +/* $OpenBSD: server_http.c,v 1.26 2014/08/01 21:51:02 doug Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -772,6 +773,9 @@ server_response_http(struct client *clt, u_int code, if (desc == NULL || (error = server_httperror_byid(code)) == NULL) return (-1); + if (server_log_http(clt, code, size) == -1) + return (-1); + kv_purge(&desc->http_headers); /* Add error codes */ @@ -961,3 +965,88 @@ server_httperror_cmp(const void *a, const void *b) const struct http_error *eb = b; return (ea->error_code - eb->error_code); } + +int +server_log_http(struct client *clt, u_int code, size_t len) +{ + static char tstamp[64]; + static char ip[INET6_ADDRSTRLEN]; + time_t t; + struct tm *tm; + struct server_config *srv_conf; + struct http_descriptor *desc; + struct kv key, *agent, *referrer; + + if ((srv_conf = clt->clt_srv_conf) == NULL) + return (-1); + if ((desc = clt->clt_desc) == NULL) + return (-1); + if (srv_conf->logformat == LOG_FORMAT_NONE) + return (0); + + if ((t = time(NULL)) == -1) + return (-1); + if ((tm = localtime(&t)) == NULL) + return (-1); + if (strftime(tstamp, sizeof(tstamp), "%d/%b/%Y:%H:%M:%S %z", tm) == 0) + return (-1); + + if (clt->clt_ss.ss_family == AF_INET) { + struct sockaddr_in *in = (struct sockaddr_in *)&clt->clt_ss; + if (inet_ntop(AF_INET, &in->sin_addr, ip, sizeof(ip)) == NULL) + return (-1); + } else if (clt->clt_ss.ss_family == AF_INET6) { + struct sockaddr_in6 *in = (struct sockaddr_in6 *)&clt->clt_ss; + if (inet_ntop(AF_INET6, &in->sin6_addr, ip, sizeof(ip)) == NULL) + return (-1); + } else + return (-1); + + /* + * For details on common log format, see: + * https://httpd.apache.org/docs/current/mod/mod_log_config.html + */ + switch (srv_conf->logformat) { + case LOG_FORMAT_COMMON: + log_info("%s %s - - [%s] \"%s %s%s%s%s%s\" %03d %zu", + srv_conf->name, ip, tstamp, + server_httpmethod_byid(desc->http_method), + desc->http_path == NULL ? "" : desc->http_path, + desc->http_query == NULL ? "" : "?", + desc->http_query == NULL ? "" : desc->http_query, + desc->http_version == NULL ? "" : " ", + desc->http_version == NULL ? "" : desc->http_version, + code, len); + break; + + case LOG_FORMAT_COMBINED: + key.kv_key = "Referer"; /* sic */ + if ((referrer = kv_find(&desc->http_headers, &key)) != NULL && + referrer->kv_value == NULL) + referrer = NULL; + + key.kv_key = "User-Agent"; + if ((agent = kv_find(&desc->http_headers, &key)) != NULL && + agent->kv_value == NULL) + agent = NULL; + + log_info("%s %s - - [%s] \"%s %s%s%s%s%s\" %03d %zu" + " \"%s\" \"%s\"", + srv_conf->name, ip, tstamp, + server_httpmethod_byid(desc->http_method), + desc->http_path == NULL ? "" : desc->http_path, + desc->http_query == NULL ? "" : "?", + desc->http_query == NULL ? "" : desc->http_query, + desc->http_version == NULL ? "" : " ", + desc->http_version == NULL ? "" : desc->http_version, + code, len, + referrer == NULL ? "" : referrer->kv_value, + agent == NULL ? "" : agent->kv_value); + break; + + default: + return (-1); + } + + return (0); +} -- 2.20.1