/* 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 * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA * **************************************************************** */ #include /* dev_t, ino_t, off_t, ... */ #include /* struct stat */ #include /* 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; }