X-Git-Url: http://www.dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Faccesslog.c;h=3a978e052fed76b6e5be8b4d010e611034d7e422;hb=9e70ef793ddcf51edc0f6489eb6f70762380afc0;hp=35a1e32a6e3d173b498b13a7455d558cca9dd6b1;hpb=472abd3cd62e50a4e9d2bfc5368547e6325ed31b;p=ashd.git diff --git a/src/accesslog.c b/src/accesslog.c index 35a1e32..3a978e0 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,103 @@ 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; } + fcntl(fileno(new), F_SETFD, FD_CLOEXEC); + 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"); + fprintf(out, " accesslog -P LOGFILE\n"); } int main(int argc, char **argv) @@ -208,7 +287,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 +295,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; @@ -242,11 +327,23 @@ int main(int argc, char **argv) outname = argv[optind]; if(outname == NULL) { out = stdout; + locklog = 0; } else { if((out = fopen(argv[optind], "a")) == NULL) { flog(LOG_ERR, "accesslog: could not open %s for logging: %s", argv[optind], strerror(errno)); exit(1); } + fcntl(fileno(out), F_SETFD, FD_CLOEXEC); + } + 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));