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