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