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/>.
39 #define DEFFORMAT "%{%Y-%m-%d %H:%M:%S}t %m %u %A \"%G\""
42 static char *outname = NULL;
44 static int flush = 1, locklog = 1;
46 static struct timeval now;
47 static volatile int reopen = 0;
49 static void qputs(char *sp, FILE *o)
51 unsigned char *s = (unsigned char *)sp;
56 } else if(*s == '\\') {
58 } else if(*s == '\n') {
60 } else if(*s == '\t') {
62 } else if((*s < 32) || (*s >= 128)) {
63 fprintf(o, "\\x%02x", (int)*s);
70 static void logitem(struct hthead *req, char o, char *d)
80 if((h = getheader(req, d)) == NULL) {
90 strcpy(buf, req->url);
91 if((p = strchr(buf, '?')) != NULL)
96 qputs(req->method, out);
99 qputs(req->rest, out);
102 qputs(req->ver, out);
106 d = "%a, %d %b %Y %H:%M:%S %z";
107 strftime(buf, sizeof(buf), d, localtime(&now.tv_sec));
112 d = "%a, %d %b %Y %H:%M:%S %z";
113 strftime(buf, sizeof(buf), d, gmtime(&now.tv_sec));
117 fprintf(out, "%06i", (int)now.tv_usec);
120 logitem(req, 'h', "X-Ash-Address");
123 logitem(req, 'h', "Host");
126 logitem(req, 'h', "Referer");
129 logitem(req, 'h', "User-Agent");
134 static void logreq(struct hthead *req)
137 char d[strlen(format)];
146 if((p2 = strchr(p, '}')) == NULL)
148 memcpy(d, p, p2 - p);
167 static void serve(struct hthead *req, int fd)
169 gettimeofday(&now, NULL);
170 if(sendreq(ch, req, fd)) {
171 flog(LOG_ERR, "accesslog: could not pass request to child: %s", strerror(errno));
177 static void sighandler(int sig)
183 static int lockfile(FILE *file)
187 memset(&ld, 0, sizeof(ld));
189 ld.l_whence = SEEK_SET;
192 return(fcntl(fileno(file), F_SETLK, &ld));
195 static void fetchpid(char *filename)
200 if((fd = open(filename, O_WRONLY)) < 0) {
201 fprintf(stderr, "accesslog: %s: %s\n", filename, strerror(errno));
204 memset(&ld, 0, sizeof(ld));
206 ld.l_whence = SEEK_SET;
209 ret = fcntl(fd, F_GETLK, &ld);
212 fprintf(stderr, "accesslog: %s: %s\n", filename, strerror(errno));
215 if(ld.l_type == F_UNLCK) {
216 fprintf(stderr, "accesslog: %s: not locked\n", filename);
219 printf("%i\n", (int)ld.l_pid);
222 static void reopenlog(void)
225 struct stat olds, news;
227 if(outname == NULL) {
228 flog(LOG_WARNING, "accesslog: received SIGHUP but logging to stdout, so ignoring");
232 if(fstat(fileno(out), &olds)) {
233 flog(LOG_ERR, "accesslog: could not stat current logfile(?!): %s", strerror(errno));
236 if(!stat(outname, &news)) {
237 if((olds.st_dev == news.st_dev) && (olds.st_ino == news.st_ino)) {
239 * This needs to be ignored, because if the same logfile
240 * is opened and then closed, the lock is lost. To quote
241 * the Linux fcntl(2) manpage: "This is bad." No kidding.
243 * Technically, there is a race condition here when the
244 * file has been stat'ed but not yet opened, where the old
245 * log file, having been previously renamed, changes name
246 * back to the name accesslog knows and is thus reopened
247 * regardlessly, but I think that might fit under the
248 * idiom "pathological case". It should, at least, not be
249 * a security problem.
251 flog(LOG_INFO, "accesslog: received SIGHUP, but logfile has not changed, so ignoring");
256 if((new = fopen(outname, "a")) == NULL) {
257 flog(LOG_WARNING, "accesslog: could not reopen log file `%s' on SIGHUP: %s", outname, strerror(errno));
260 fcntl(fileno(new), F_SETFD, FD_CLOEXEC);
263 if((errno == EAGAIN) || (errno == EACCES)) {
264 flog(LOG_ERR, "accesslog: logfile is already locked; reverting to current log", strerror(errno));
268 flog(LOG_WARNING, "accesslog: could not lock logfile, so no lock will be held: %s", strerror(errno));
276 static void usage(FILE *out)
278 fprintf(out, "usage: accesslog [-hFaL] [-f FORMAT] [-p PIDFILE] OUTFILE CHILD [ARGS...]\n");
279 fprintf(out, " accesslog -P LOGFILE\n");
282 int main(int argc, char **argv)
287 struct pollfd pfd[2];
292 while((c = getopt(argc, argv, "+hFaLf:p:P:")) >= 0) {
313 format = "%A - - [%{%d/%b/%Y:%H:%M:%S %z}t] \"%m %u %v\" - - \"%R\" \"%G\"";
320 if(argc - optind < 2) {
326 if(!strcmp(argv[optind], "-"))
329 outname = argv[optind];
330 if(outname == NULL) {
334 if((out = fopen(argv[optind], "a")) == NULL) {
335 flog(LOG_ERR, "accesslog: could not open %s for logging: %s", argv[optind], strerror(errno));
338 fcntl(fileno(out), F_SETFD, FD_CLOEXEC);
342 if((errno == EAGAIN) || (errno == EACCES)) {
343 flog(LOG_ERR, "accesslog: logfile is already locked", strerror(errno));
346 flog(LOG_WARNING, "accesslog: could not lock logfile: %s", strerror(errno));
350 if((ch = stdmkchild(argv + optind + 1, NULL, NULL)) < 0) {
351 flog(LOG_ERR, "accesslog: could not fork child: %s", strerror(errno));
354 signal(SIGHUP, sighandler);
356 if(!strcmp(pidfile, "-")) {
358 flog(LOG_ERR, "accesslog: cannot derive PID file name without an output file");
361 pidfile = sprintf2("%s.pid", outname);
363 if((pidout = fopen(pidfile, "w")) == NULL) {
364 flog(LOG_ERR, "accesslog: could not open PID file %s for writing: %s", pidfile);
367 fprintf(pidout, "%i\n", (int)getpid());
375 memset(pfd, 0, sizeof(pfd));
377 pfd[0].events = POLLIN;
379 pfd[1].events = POLLHUP;
380 if((ret = poll(pfd, 2, -1)) < 0) {
382 flog(LOG_ERR, "accesslog: error in poll: %s", strerror(errno));
387 if((fd = recvreq(0, &req)) < 0) {
390 flog(LOG_ERR, "accesslog: error in recvreq: %s", strerror(errno));
397 if(pfd[1].revents & POLLHUP)