1/* Copyright (c) 1993-2002 2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) 3 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) 4 * Copyright (c) 1987 Oliver Laumann 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program (see the file COPYING); if not, write to the 18 * Free Software Foundation, Inc., 19 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 20 * 21 **************************************************************** 22 */ 23 24#include <sys/types.h> /* dev_t, ino_t, off_t, ... */ 25#include <sys/stat.h> /* struct stat */ 26#include <fcntl.h> /* O_WRONLY for logfile_reopen */ 27 28 29#include "config.h" 30#include "screen.h" 31#include "extern.h" 32#include "logfile.h" 33 34static void changed_logfile __P((struct logfile *)); 35static struct logfile *lookup_logfile __P((char *)); 36static int stolen_logfile __P((struct logfile *)); 37 38static struct logfile *logroot = NULL; 39 40static void 41changed_logfile(l) 42struct logfile *l; 43{ 44 struct stat o, *s = l->st; 45 46 if (fstat(fileno(l->fp), &o) < 0) /* get trouble later */ 47 return; 48 if (o.st_size > s->st_size) /* aha, appended text */ 49 { 50 s->st_size = o.st_size; /* this should have changed */ 51 s->st_mtime = o.st_mtime; /* only size and mtime */ 52 } 53} 54 55/* 56 * Requires fd to be open and need_fd to be closed. 57 * If possible, need_fd will be open afterwards and refer to 58 * the object originally reffered by fd. fd will be closed then. 59 * Works just like ``fcntl(fd, DUPFD, need_fd); close(fd);'' 60 * 61 * need_fd is returned on success, else -1 is returned. 62 */ 63int 64lf_move_fd(fd, need_fd) 65int need_fd, fd; 66{ 67 int r = -1; 68 69 if (fd == need_fd) 70 return fd; 71 if (fd >=0 && fd < need_fd) 72 r = lf_move_fd(dup(fd), need_fd); 73 close(fd); 74 return r; 75} 76 77static int 78logfile_reopen(name, wantfd, l) 79char *name; 80int wantfd; 81struct logfile *l; 82{ 83 int got_fd; 84 85 close(wantfd); 86 if (((got_fd = open(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) || 87 lf_move_fd(got_fd, wantfd) < 0) 88 { 89 logfclose(l); 90 debug1("logfile_reopen: failed for %s\n", name); 91 return -1; 92 } 93 changed_logfile(l); 94 debug2("logfile_reopen: %d = %s\n", wantfd, name); 95 return 0; 96} 97 98static int (* lf_reopen_fn)() = logfile_reopen; 99 100/* 101 * Whenever logfwrite discoveres that it is required to close and 102 * reopen the logfile, the function registered here is called. 103 * If you do not register anything here, the above logfile_reopen() 104 * will be used instead. 105 * Your function should perform the same steps as logfile_reopen(): 106 * a) close the original filedescriptor without flushing any output 107 * b) open a new logfile for future output on the same filedescriptor number. 108 * c) zero out st_dev, st_ino to tell the stolen_logfile() indcator to 109 * reinitialise itself. 110 * d) return 0 on success. 111 */ 112void 113logreopen_register(fn) 114int (*fn) __P((char *, int, struct logfile *)); 115{ 116 lf_reopen_fn = fn ? fn : logfile_reopen; 117} 118 119/* 120 * If the logfile has been removed, truncated, unlinked or the like, 121 * return nonzero. 122 * The l->st structure initialised by logfopen is updated 123 * on every call. 124 */ 125static int 126stolen_logfile(l) 127struct logfile *l; 128{ 129 struct stat o, *s = l->st; 130 131 o = *s; 132 if (fstat(fileno(l->fp), s) < 0) /* remember that stat failed */ 133 s->st_ino = s->st_dev = 0; 134 ASSERT(s == l->st); 135 if (!o.st_dev && !o.st_ino) /* nothing to compare with */ 136 return 0; 137 138 if ((!s->st_dev && !s->st_ino) || /* stat failed, that's new! */ 139 !s->st_nlink || /* red alert: file unlinked */ 140 (s->st_size < o.st_size) || /* file truncated */ 141 (s->st_mtime != o.st_mtime) || /* file modified */ 142 ((s->st_ctime != o.st_ctime) && /* file changed (moved) */ 143 !(s->st_mtime == s->st_ctime && /* and it was not a change */ 144 o.st_ctime < s->st_ctime))) /* due to delayed nfs write */ 145 { 146 debug1("stolen_logfile: %s stolen!\n", l->name); 147 debug3("st_dev %d, st_ino %d, st_nlink %d\n", 148 (int)s->st_dev, (int)s->st_ino, (int)s->st_nlink); 149 debug2("s->st_size %d, o.st_size %d\n", (int)s->st_size, (int)o.st_size); 150 debug2("s->st_mtime %d, o.st_mtime %d\n", 151 (int)s->st_mtime, (int)o.st_mtime); 152 debug2("s->st_ctime %d, o.st_ctime %d\n", 153 (int)s->st_ctime, (int)o.st_ctime); 154 return -1; 155 } 156 157 debug1("stolen_logfile: %s o.k.\n", l->name); 158 return 0; 159} 160 161static struct logfile * 162lookup_logfile(name) 163char *name; 164{ 165 struct logfile *l; 166 167 for (l = logroot; l; l = l->next) 168 if (!strcmp(name, l->name)) 169 return l; 170 return NULL; 171} 172 173struct logfile * 174logfopen(name, fp) 175char *name; 176FILE *fp; 177{ 178 struct logfile *l; 179 180 if (!fp) 181 { 182 if (!(l = lookup_logfile(name))) 183 return NULL; 184 l->opencount++; 185 return l; 186 } 187 188 if (!(l = (struct logfile *)malloc(sizeof(struct logfile)))) 189 return NULL; 190 if (!(l->st = (struct stat *)malloc(sizeof(struct stat)))) 191 { 192 free((char *)l); 193 return NULL; 194 } 195 196 if (!(l->name = SaveStr(name))) 197 { 198 free((char *)l->st); 199 free((char *)l); 200 return NULL; 201 } 202 l->fp = fp; 203 l->opencount = 1; 204 l->writecount = 0; 205 l->flushcount = 0; 206 changed_logfile(l); 207 208 l->next = logroot; 209 logroot = l; 210 return l; 211} 212 213int 214islogfile(name) 215char *name; 216{ 217 if (!name) 218 return logroot ? 1 : 0; 219 return lookup_logfile(name) ? 1 : 0; 220} 221 222int 223logfclose(l) 224struct logfile *l; 225{ 226 struct logfile **lp; 227 228 for (lp = &logroot; *lp; lp = &(*lp)->next) 229 if (*lp == l) 230 break; 231 232 if (!*lp) 233 return -1; 234 235 if ((--l->opencount) > 0) 236 return 0; 237 if (l->opencount < 0) 238 abort(); 239 240 *lp = l->next; 241 fclose(l->fp); 242 free(l->name); 243 free((char *)l); 244 return 0; 245} 246 247/* 248 * XXX 249 * write and flush both *should* check the file's stat, if it disappeared 250 * or changed, re-open it. 251 */ 252int 253logfwrite(l, buf, n) 254struct logfile *l; 255char *buf; 256int n; 257{ 258 int r; 259 260 if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l)) 261 return -1; 262 r = fwrite(buf, n, 1, l->fp); 263 l->writecount += l->flushcount + 1; 264 l->flushcount = 0; 265 changed_logfile(l); 266 return r; 267} 268 269int 270logfflush(l) 271struct logfile *l; 272{ 273 int r = 0; 274 275 if (!l) 276 for (l = logroot; l; l = l->next) 277 { 278 if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l)) 279 return -1; 280 r |= fflush(l->fp); 281 l->flushcount++; 282 changed_logfile(l); 283 } 284 else 285 { 286 if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l)) 287 return -1; 288 r = fflush(l->fp); 289 l->flushcount++; 290 changed_logfile(l); 291 } 292 return r; 293} 294 295