diff options
Diffstat (limited to '')
-rw-r--r-- | logfile.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/logfile.c b/logfile.c new file mode 100644 index 0000000..c1ffb15 --- /dev/null +++ b/logfile.c @@ -0,0 +1,251 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include <sys/types.h> /* dev_t, ino_t, off_t, ... */ +#include <sys/stat.h> /* struct stat */ +#include <fcntl.h> /* O_WRONLY for logfile_reopen */ + +#include "config.h" +#include "screen.h" +#include "extern.h" +#include "logfile.h" + +static void changed_logfile __P((struct logfile *)); +static struct logfile *lookup_logfile __P((char *)); +static int stolen_logfile __P((struct logfile *)); + +static struct logfile *logroot = NULL; + +static void changed_logfile(struct logfile *l) { + struct stat o, *s = l->st; + + if (fstat(fileno(l->fp), &o) < 0) /* get trouble later */ + return; + if (o.st_size > s->st_size) { /* aha, appended text */ + s->st_size = o.st_size; /* this should have changed */ + s->st_mtime = o.st_mtime; /* only size and mtime */ + } +} + +/* + * Requires fd to be open and need_fd to be closed. + * If possible, need_fd will be open afterwards and refer to + * the object originally reffered by fd. fd will be closed then. + * Works just like ``fcntl(fd, DUPFD, need_fd); close(fd);'' + * + * need_fd is returned on success, else -1 is returned. + */ +int lf_move_fd(int fd, int need_fd) { + int r = -1; + + if (fd == need_fd) + return fd; + if (fd >=0 && fd < need_fd) + r = lf_move_fd(dup(fd), need_fd); + close(fd); + return r; +} + +static int logfile_reopen(char *name, int wantfd, struct logfile *l) { + int got_fd; + + close(wantfd); + if (((got_fd = open(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) || lf_move_fd(got_fd, wantfd) < 0) { + logfclose(l); + debug1("logfile_reopen: failed for %s\n", name); + return -1; + } + changed_logfile(l); + debug2("logfile_reopen: %d = %s\n", wantfd, name); + return 0; +} + +static int (* lf_reopen_fn)() = logfile_reopen; + +/* + * Whenever logfwrite discoveres that it is required to close and + * reopen the logfile, the function registered here is called. + * If you do not register anything here, the above logfile_reopen() + * will be used instead. + * Your function should perform the same steps as logfile_reopen(): + * a) close the original filedescriptor without flushing any output + * b) open a new logfile for future output on the same filedescriptor number. + * c) zero out st_dev, st_ino to tell the stolen_logfile() indcator to + * reinitialise itself. + * d) return 0 on success. + */ +void logreopen_register(fn) +int (*fn) __P((char *, int, struct logfile *)); +{ + lf_reopen_fn = fn ? fn : logfile_reopen; +} + +/* + * If the logfile has been removed, truncated, unlinked or the like, + * return nonzero. + * The l->st structure initialised by logfopen is updated + * on every call. + */ +static int stolen_logfile(struct logfile *l) { + struct stat o, *s = l->st; + + o = *s; + if (fstat(fileno(l->fp), s) < 0) /* remember that stat failed */ + s->st_ino = s->st_dev = 0; + + ASSERT(s == l->st); + if (!o.st_dev && !o.st_ino) /* nothing to compare with */ + return 0; + + if ((!s->st_dev && !s->st_ino) || /* stat failed, that's new! */ + !s->st_nlink || /* red alert: file unlinked */ + (s->st_size < o.st_size) || /* file truncated */ + (s->st_mtime != o.st_mtime) || /* file modified */ + ((s->st_ctime != o.st_ctime) && /* file changed (moved) */ + !(s->st_mtime == s->st_ctime && /* and it was not a change */ + o.st_ctime < s->st_ctime))) /* due to delayed nfs write */ + { + debug1("stolen_logfile: %s stolen!\n", l->name); + debug3("st_dev %d, st_ino %d, st_nlink %d\n", (int)s->st_dev, (int)s->st_ino, (int)s->st_nlink); + debug2("s->st_size %d, o.st_size %d\n", (int)s->st_size, (int)o.st_size); + debug2("s->st_mtime %d, o.st_mtime %d\n", (int)s->st_mtime, (int)o.st_mtime); + debug2("s->st_ctime %d, o.st_ctime %d\n", (int)s->st_ctime, (int)o.st_ctime); + return -1; + } + debug1("stolen_logfile: %s o.k.\n", l->name); + return 0; +} + +static struct logfile *lookup_logfile(char *name) { + struct logfile *l; + + for (l = logroot; l; l = l->next) + if (!strcmp(name, l->name)) + return l; + return NULL; +} + +struct logfile *logfopen(char *name, FILE *fp) { + struct logfile *l; + + if (!fp) { + if (!(l = lookup_logfile(name))) + return NULL; + l->opencount++; + return l; + } + + if (!(l = (struct logfile *)malloc(sizeof(struct logfile)))) + return NULL; + if (!(l->st = (struct stat *)malloc(sizeof(struct stat)))) { + free((char *)l); + return NULL; + } + + if (!(l->name = SaveStr(name))) { + free((char *)l->st); + free((char *)l); + return NULL; + } + l->fp = fp; + l->opencount = 1; + l->writecount = 0; + l->flushcount = 0; + changed_logfile(l); + + l->next = logroot; + logroot = l; + return l; +} + +int islogfile(char *name) { + if (!name) + return logroot ? 1 : 0; + return lookup_logfile(name) ? 1 : 0; +} + +int logfclose(struct logfile *l) { + struct logfile **lp; + + for (lp = &logroot; *lp; lp = &(*lp)->next) + if (*lp == l) + break; + + if (!*lp) + return -1; + + if ((--l->opencount) > 0) + return 0; + if (l->opencount < 0) + abort(); + + *lp = l->next; + fclose(l->fp); + free(l->name); + free((char *)l); + return 0; +} + +/* + * XXX + * write and flush both *should* check the file's stat, if it disappeared + * or changed, re-open it. + */ +int logfwrite(struct logfile *l, char *buf, int n) { + int r; + + if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l)) + return -1; + r = fwrite(buf, n, 1, l->fp); + l->writecount += l->flushcount + 1; + l->flushcount = 0; + changed_logfile(l); + return r; +} + +int logfflush(struct logfile *l) { + int r = 0; + + if (!l) + for (l = logroot; l; l = l->next) { + if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l)) + return -1; + r |= fflush(l->fp); + l->flushcount++; + changed_logfile(l); + } + else { + if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l)) + return -1; + r = fflush(l->fp); + l->flushcount++; + changed_logfile(l); + } + return r; +} + |