From ca170d77b595e63bbbaba28ace23cc88e855c41a Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Tue, 15 Feb 2011 20:46:06 +0100 Subject: [PATCH] accesslog: Added logfile locking. --- doc/accesslog.doc | 22 ++++++++++++- src/accesslog.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/doc/accesslog.doc b/doc/accesslog.doc index 39ec14a..1ba3d13 100644 --- a/doc/accesslog.doc +++ b/doc/accesslog.doc @@ -7,7 +7,8 @@ accesslog - Access logger for ashd(7) SYNOPSIS -------- -*accesslog* [*-hFa*] [*-f* 'FORMAT'] [*-p* 'PIDFILE'] 'OUTFILE' 'CHILD' ['ARGS'...] +*accesslog* [*-hFaL*] [*-f* 'FORMAT'] [*-p* 'PIDFILE'] 'OUTFILE' 'CHILD' ['ARGS'...] +*accesslog* *-P* 'LOGFILE' DESCRIPTION ----------- @@ -28,6 +29,11 @@ useful e.g. for log rotation. If the child handler exits, *accesslog* exits as well. +Normally, *accesslog* locks the logfile using *fcntl*(2) to ensure +that only one process writes to a logfile at any time. The *-L* switch +can be used to override that behavior to let several processes share a +logfile, or to use logfiles that cannot be locked for some reason. + OPTIONS ------- @@ -64,6 +70,20 @@ OPTIONS %A - - [%{%d/%b/%Y:%H:%M:%S %z}t] "%m %u %v" - - "%R" "%G" -------- +*-L*:: + + Do not attempt to lock the logfile. Note that this switch + conflicts with the use of the *-P* option. + +*-P* 'LOGFILE':: + + Makes *accesslog* fetch the PID of the process currently + holding the lock on 'LOGFILE', write that to standard output, + and then exit. No further command-line arguments are + processed. This option is useful for sending SIGHUP to + accesslog when rotating logfiles without having to use a PID + file. + FORMAT ------ 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); -- 2.11.0