2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
38 #define DEFFORMAT "%{%Y-%m-%d %H:%M:%S}t %m %u %A \"%G\""
41 static char *outname = NULL;
45 static struct timeval now;
46 static volatile int reopen = 0;
48 static void qputs(char *s, FILE *o)
53 } else if(*s == '\\') {
55 } else if(*s == '\n') {
57 } else if(*s == '\t') {
59 } else if((*s < 32) || (*s >= 128)) {
60 fprintf(o, "\\x%02x", *s);
67 static void logitem(struct hthead *req, char o, char *d)
77 if((h = getheader(req, d)) == NULL) {
87 strcpy(buf, req->url);
88 if((p = strchr(buf, '?')) != NULL)
93 qputs(req->method, out);
96 qputs(req->rest, out);
103 d = "%a, %d %b %Y %H:%M:%S %z";
104 strftime(buf, sizeof(buf), d, localtime(&now.tv_sec));
109 d = "%a, %d %b %Y %H:%M:%S %z";
110 strftime(buf, sizeof(buf), d, gmtime(&now.tv_sec));
114 fprintf(out, "%06i", (int)now.tv_usec);
117 logitem(req, 'h', "X-Ash-Address");
120 logitem(req, 'h', "Host");
123 logitem(req, 'h', "Referer");
126 logitem(req, 'h', "User-Agent");
131 static void logreq(struct hthead *req)
134 char d[strlen(format)];
143 if((p2 = strchr(p, '}')) == NULL)
145 memcpy(d, p, p2 - p);
164 static void serve(struct hthead *req, int fd)
166 gettimeofday(&now, NULL);
167 if(sendreq(ch, req, fd)) {
168 flog(LOG_ERR, "accesslog: could not pass request to child: %s", strerror(errno));
174 static void sighandler(int sig)
180 static void reopenlog(void)
184 if(outname == NULL) {
185 flog(LOG_WARNING, "accesslog: received SIGHUP but logging to stdout, so ignoring");
188 if((new = fopen(outname, "a")) == NULL) {
189 flog(LOG_WARNING, "accesslog: could not reopen log file `%s' on SIGHUP: %s", outname, strerror(errno));
196 static void usage(FILE *out)
198 fprintf(out, "usage: accesslog [-hFa] [-f FORMAT] [-p PIDFILE] OUTFILE CHILD [ARGS...]\n");
201 int main(int argc, char **argv)
206 struct pollfd pfd[2];
211 while((c = getopt(argc, argv, "+hFaf:p:")) >= 0) {
226 format = "%A - - [%{%d/%b/%Y:%H:%M:%S %z}t] \"%m %u %v\" - - \"%R\" \"%G\"";
233 if(argc - optind < 2) {
239 if(!strcmp(argv[optind], "-"))
242 outname = argv[optind];
243 if(outname == NULL) {
246 if((out = fopen(argv[optind], "a")) == NULL) {
247 flog(LOG_ERR, "accesslog: could not open %s for logging: %s", argv[optind], strerror(errno));
251 if((ch = stdmkchild(argv + optind + 1, NULL, NULL)) < 0) {
252 flog(LOG_ERR, "accesslog: could not fork child: %s", strerror(errno));
255 signal(SIGHUP, sighandler);
257 if(!strcmp(pidfile, "-")) {
259 flog(LOG_ERR, "accesslog: cannot derive PID file name without an output file");
262 pidfile = sprintf2("%s.pid", outname);
264 if((pidout = fopen(pidfile, "w")) == NULL) {
265 flog(LOG_ERR, "accesslog: could not open PID file %s for writing: %s", pidfile);
268 fprintf(pidout, "%i\n", (int)getpid());
276 memset(pfd, 0, sizeof(pfd));
278 pfd[0].events = POLLIN;
280 pfd[1].events = POLLHUP;
281 if((ret = poll(pfd, 2, -1)) < 0) {
283 flog(LOG_ERR, "accesslog: error in poll: %s", strerror(errno));
288 if((fd = recvreq(0, &req)) < 0) {
291 flog(LOG_ERR, "accesslog: error in recvreq: %s", strerror(errno));
298 if(pfd[1].revents & POLLHUP)