summaryrefslogtreecommitdiffstats
path: root/logfile.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--logfile.c251
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;
+}
+