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