X-Git-Url: http://www.dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Faccesslog.c;h=02e5f1ad1a2e3149553f88ad00c337ebc39129c8;hb=470938bdc9149ae9c7befd0cd983f46fcc056192;hp=ac324d5fcfe7d4d8578235c23881e5ab319a8f18;hpb=f99bcc64b0dbb3159fec8c64bea42c9ac870a22b;p=ashd.git diff --git a/src/accesslog.c b/src/accesslog.c index ac324d5..02e5f1a 100644 --- a/src/accesslog.c +++ b/src/accesslog.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #ifdef HAVE_CONFIG_H #include @@ -39,9 +41,9 @@ static int ch; static char *outname = NULL; static FILE *out; -static int flush = 1; +static int flush = 1, locklog = 1; static char *format; -static time_t now; +static struct timeval now; static volatile int reopen = 0; static void qputs(char *s, FILE *o) @@ -67,7 +69,6 @@ static void logitem(struct hthead *req, char o, char *d) { char *h, *p; char buf[1024]; - struct timeval tv; switch(o) { case '%': @@ -101,18 +102,17 @@ static void logitem(struct hthead *req, char o, char *d) case 't': if(!*d) d = "%a, %d %b %Y %H:%M:%S %z"; - strftime(buf, sizeof(buf), d, localtime(&now)); + strftime(buf, sizeof(buf), d, localtime(&now.tv_sec)); qputs(buf, out); break; case 'T': if(!*d) d = "%a, %d %b %Y %H:%M:%S %z"; - strftime(buf, sizeof(buf), d, gmtime(&now)); + strftime(buf, sizeof(buf), d, gmtime(&now.tv_sec)); qputs(buf, out); break; case 's': - gettimeofday(&tv, NULL); - fprintf(out, "%06i", (int)tv.tv_usec); + fprintf(out, "%06i", (int)now.tv_usec); break; case 'A': logitem(req, 'h', "X-Ash-Address"); @@ -164,7 +164,7 @@ static void logreq(struct hthead *req) static void serve(struct hthead *req, int fd) { - now = time(NULL); + gettimeofday(&now, NULL); if(sendreq(ch, req, fd)) { flog(LOG_ERR, "accesslog: could not pass request to child: %s", strerror(errno)); exit(1); @@ -178,25 +178,102 @@ static void sighandler(int sig) reopen = 1; } +static int lockfile(FILE *file) +{ + struct flock ld; + + memset(&ld, 0, sizeof(ld)); + ld.l_type = F_WRLCK; + ld.l_whence = SEEK_SET; + ld.l_start = 0; + ld.l_len = 0; + return(fcntl(fileno(file), F_SETLK, &ld)); +} + +static void fetchpid(char *filename) +{ + int fd, ret; + struct flock ld; + + if((fd = open(filename, O_WRONLY)) < 0) { + fprintf(stderr, "accesslog: %s: %s\n", filename, strerror(errno)); + exit(1); + } + memset(&ld, 0, sizeof(ld)); + ld.l_type = F_WRLCK; + ld.l_whence = SEEK_SET; + ld.l_start = 0; + ld.l_len = 0; + ret = fcntl(fd, F_GETLK, &ld); + close(fd); + if(ret) { + fprintf(stderr, "accesslog: %s: %s\n", filename, strerror(errno)); + exit(1); + } + if(ld.l_type == F_UNLCK) { + fprintf(stderr, "accesslog: %s: not locked\n", filename); + exit(1); + } + printf("%i\n", (int)ld.l_pid); +} + static void reopenlog(void) { FILE *new; + struct stat olds, news; if(outname == NULL) { flog(LOG_WARNING, "accesslog: received SIGHUP but logging to stdout, so ignoring"); return; } + if(locklog) { + if(fstat(fileno(out), &olds)) { + flog(LOG_ERR, "accesslog: could not stat current logfile(?!): %s", strerror(errno)); + return; + } + if(!stat(outname, &news)) { + if((olds.st_dev == news.st_dev) && (olds.st_ino == news.st_ino)) { + /* + * This needs to be ignored, because if the same logfile + * is opened and then closed, the lock is lost. To quote + * the Linux fcntl(2) manpage: "This is bad." No kidding. + * + * Technically, there is a race condition here when the + * file has been stat'ed but not yet opened, where the old + * log file, having been previously renamed, changes name + * back to the name accesslog knows and is thus reopened + * regardlessly, but I think that might fit under the + * idiom "pathological case". It should, at least, not be + * a security problem. + */ + flog(LOG_INFO, "accesslog: received SIGHUP, but logfile has not changed, so ignoring"); + return; + } + } + } if((new = fopen(outname, "a")) == NULL) { flog(LOG_WARNING, "accesslog: could not reopen log file `%s' on SIGHUP: %s", outname, strerror(errno)); return; } + if(locklog) { + if(lockfile(new)) { + if((errno == EAGAIN) || (errno == EACCES)) { + flog(LOG_ERR, "accesslog: logfile is already locked; reverting to current log", strerror(errno)); + fclose(new); + return; + } else { + flog(LOG_WARNING, "accesslog: could not lock logfile, so no lock will be held: %s", strerror(errno)); + } + } + } fclose(out); out = new; } static void usage(FILE *out) { - fprintf(out, "usage: accesslog [-hFa] [-f FORMAT] OUTFILE CHILD [ARGS...]\n"); + fprintf(out, "usage: accesslog [-hFaL] [-f FORMAT] [-p PIDFILE] OUTFILE CHILD [ARGS...]\n"); + fprintf(out, " accesslog -P LOGFILE\n"); } int main(int argc, char **argv) @@ -205,9 +282,11 @@ int main(int argc, char **argv) struct hthead *req; int fd; struct pollfd pfd[2]; + char *pidfile; + FILE *pidout; - optarg = NULL; - while((c = getopt(argc, argv, "+hFaf:")) >= 0) { + pidfile = NULL; + while((c = getopt(argc, argv, "+hFaLf:p:P:")) >= 0) { switch(c) { case 'h': usage(stdout); @@ -215,9 +294,18 @@ int main(int argc, char **argv) case 'F': flush = 0; break; + case 'L': + locklog = 0; + break; case 'f': format = optarg; break; + case 'P': + fetchpid(optarg); + exit(0); + case 'p': + pidfile = optarg; + break; case 'a': format = "%A - - [%{%d/%b/%Y:%H:%M:%S %z}t] \"%m %u %v\" - - \"%R\" \"%G\""; break; @@ -244,11 +332,37 @@ int main(int argc, char **argv) exit(1); } } + if(locklog) { + if(lockfile(out)) { + if((errno == EAGAIN) || (errno == EACCES)) { + flog(LOG_ERR, "accesslog: logfile is already locked", strerror(errno)); + exit(1); + } else { + flog(LOG_WARNING, "accesslog: could not lock logfile: %s", strerror(errno)); + } + } + } + fcntl(fileno(out), F_SETFD, FD_CLOEXEC); if((ch = stdmkchild(argv + optind + 1, NULL, NULL)) < 0) { - flog(LOG_ERR, "accesslog: could fork child: %s", strerror(errno)); + flog(LOG_ERR, "accesslog: could not fork child: %s", strerror(errno)); exit(1); } signal(SIGHUP, sighandler); + if(pidfile) { + if(!strcmp(pidfile, "-")) { + if(!outname) { + flog(LOG_ERR, "accesslog: cannot derive PID file name without an output file"); + exit(1); + } + pidfile = sprintf2("%s.pid", outname); + } + if((pidout = fopen(pidfile, "w")) == NULL) { + flog(LOG_ERR, "accesslog: could not open PID file %s for writing: %s", pidfile); + exit(1); + } + fprintf(pidout, "%i\n", (int)getpid()); + fclose(pidout); + } while(1) { if(reopen) { reopenlog(); @@ -279,5 +393,7 @@ int main(int argc, char **argv) if(pfd[1].revents & POLLHUP) break; } + if(pidfile != NULL) + unlink(pidfile); return(0); }