X-Git-Url: http://www.dolda2000.com/gitweb/?p=ashd.git;a=blobdiff_plain;f=src%2Faccesslog.c;h=5b0c7081152bb0abda657efc0db3eb7cf7a73cc6;hp=35a1e32a6e3d173b498b13a7455d558cca9dd6b1;hb=ca170d77b595e63bbbaba28ace23cc88e855c41a;hpb=472abd3cd62e50a4e9d2bfc5368547e6325ed31b diff --git a/src/accesslog.c b/src/accesslog.c index 35a1e32..5b0c708 100644 --- a/src/accesslog.c +++ b/src/accesslog.c @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef HAVE_CONFIG_H #include @@ -40,7 +41,7 @@ static int ch; static char *outname = NULL; static FILE *out; -static int flush = 1; +static int flush = 1, locklog = 1; static char *format; static struct timeval now; static volatile int reopen = 0; @@ -177,25 +178,101 @@ 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] [-p PIDFILE] OUTFILE CHILD [ARGS...]\n"); + fprintf(out, "usage: accesslog [-hFaL] [-f FORMAT] [-p PIDFILE] OUTFILE CHILD [ARGS...]\n"); } int main(int argc, char **argv) @@ -208,7 +285,7 @@ int main(int argc, char **argv) FILE *pidout; pidfile = NULL; - while((c = getopt(argc, argv, "+hFaf:p:")) >= 0) { + while((c = getopt(argc, argv, "+hFaLf:p:P:")) >= 0) { switch(c) { case 'h': usage(stdout); @@ -216,9 +293,15 @@ 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; @@ -248,6 +331,16 @@ 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)); + } + } + } if((ch = stdmkchild(argv + optind + 1, NULL, NULL)) < 0) { flog(LOG_ERR, "accesslog: could not fork child: %s", strerror(errno)); exit(1);