1 /* $Id: xtail.c,v 2.5 2000/06/04 09:09:03 chip Exp $ */
2
3 #include "config.h"
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <stdio.h>
7 #include <signal.h>
8 #ifdef HAVE_UNISTD_H
9 # include <unistd.h>
10 #endif
11 #ifdef HAVE_STDLIB_H
12 # include <stdlib.h>
13 #endif
14 #define INTERN
15 #include "xtail.h"
16
17 int sigcaught = 0;
18 RETSIGTYPE sigcatcher(int sig);
19
20
main(int argc,char * argv[])21 int main(int argc, char *argv[])
22 {
23 int open_files_only, already_open, iteration, i;
24 struct entry_descrip *entryp;
25 struct stat sbuf;
26
27 /*
28 * Initialize.
29 */
30 Progname = basename(argv[0]);
31 List_file = new_entry_list(ENTRY_LIST_CHUNK);
32 List_dir = new_entry_list(ENTRY_LIST_CHUNK);
33 List_zap = new_entry_list(ENTRY_LIST_CHUNK);
34 Sorted = FALSE;
35 Reset_status = FALSE;
36 Debug = FALSE;
37 sigcatcher(0);
38
39
40 /*
41 * Place all of the entries onto lists.
42 */
43 for (i = 1 ; i < argc ; ++i) {
44
45 if (i == 1 && strcmp(argv[i], "-D") == 0) {
46 Debug = TRUE;
47 continue;
48 }
49
50 /*
51 * Temporarily throw this entry onto the end of the zapped list.
52 */
53 entryp = new_entry(List_zap, argv[i]);
54
55 /*
56 * Stat the file and get it to its proper place.
57 */
58 switch (stat_entry(List_zap, last_entry(List_zap), &sbuf)) {
59
60 case ENTRY_FILE: /* move entry to file list */
61 move_entry(List_file, List_zap, last_entry(List_zap));
62 entryp->size = sbuf.st_size;
63 entryp->mtime = sbuf.st_mtime;
64 break;
65
66 case ENTRY_DIR: /* move entry to dir list */
67 move_entry(List_dir, List_zap, last_entry(List_zap));
68 entryp->size = sbuf.st_size;
69 entryp->mtime = sbuf.st_mtime;
70 if (scan_directory(entryp->name) != 0) {
71 message(MSSG_OPEN, entryp);
72 rmv_entry(List_dir, last_entry(List_dir));
73 }
74 break;
75
76 case ENTRY_ZAP: /* keep entry on zap list */
77 break;
78
79 case ENTRY_SPECIAL: /* entry is a special file */
80 message(MSSG_NOTAFIL, entryp);
81 rmv_entry(List_zap, last_entry(List_zap));
82 break;
83
84 default: /* stat error */
85 message(MSSG_STAT, entryp);
86 rmv_entry(List_zap, last_entry(List_zap));
87 break;
88
89 }
90
91 }
92
93 /*
94 * Make sure we are watching something reasonable.
95 */
96 if (List_file->num_entries == 0) {
97 if (List_dir->num_entries == 0 && List_zap->num_entries == 0) {
98 (void) fprintf(stderr, "%s: no valid entries specified\n", Progname);
99 (void) exit(1);
100 }
101 (void) puts("\n*** warning - no files are being watched ***");
102 }
103
104
105 /*
106 * From this point on we want to reset the status of an entry any
107 * time we move it around to another list.
108 */
109 Reset_status = TRUE;
110
111
112 /*
113 * Force a check of everything first time through the loop.
114 */
115 iteration = CHECK_COUNT;
116
117
118 /*
119 * Loop forever.
120 */
121 for (;;) {
122
123 /*
124 * Once every CHECK_COUNT iterations check everything.
125 * All other times only look at the opened files.
126 */
127 open_files_only = (++iteration < CHECK_COUNT);
128 if (!open_files_only)
129 iteration = 0;
130
131
132 /*
133 * Make sure that the most recently modified files are open.
134 */
135 if (!Sorted)
136 fixup_open_files();
137
138
139 /*
140 * Display what we are watching if a SIGINT was caught.
141 */
142 if (sigcaught) {
143 show_status();
144 sigcatcher(0);
145 }
146
147
148 /*
149 * Go through all of the files looking for changes.
150 */
151 Dprintf(stderr, ">>> checking files list (%s)\n",
152 (open_files_only ? "open files only" : "all files"));
153 for (i = 0 ; i < List_file->num_entries ; ++i) {
154
155 entryp = List_file->list[i];
156 already_open = (entryp->fd > 0) ;
157
158 /*
159 * Ignore closed files except every CHECK_COUNT iterations.
160 */
161 if (!already_open && open_files_only)
162 continue;
163
164 /*
165 * Get the status of this file.
166 */
167 switch (stat_entry(List_file, i, &sbuf)) {
168 case ENTRY_FILE: /* got status OK */
169 break;
170 case ENTRY_DIR: /* huh??? it's now a dir */
171 move_entry(List_dir, List_file, i--);
172 continue;
173 case ENTRY_ZAP: /* entry has been deleted */
174 message(MSSG_ZAPPED, entryp);
175 move_entry(List_zap, List_file, i--);
176 continue;
177 case ENTRY_SPECIAL: /* entry is a special file */
178 message(MSSG_NOTAFIL, entryp);
179 rmv_entry(List_file, i--);
180 continue;
181 default: /* stat error */
182 message(MSSG_STAT, entryp);
183 rmv_entry(List_file, i--);
184 continue;
185 }
186
187
188 /*
189 * See if an opened file has been deleted.
190 */
191 if (already_open && sbuf.st_nlink == 0) {
192 message(MSSG_ZAPPED, entryp);
193 move_entry(List_zap, List_file, i--);
194 continue;
195 }
196
197 /*
198 * If nothing has changed then continue on.
199 */
200 if (entryp->size==sbuf.st_size && entryp->mtime==sbuf.st_mtime)
201 continue;
202
203 /*
204 * If the file isn't already open, then do so.
205 * Note -- it is important that we call "fixup_open_files()"
206 * at the end of the loop to make sure too many files don't
207 * stay opened.
208 */
209 if (!already_open && open_entry(List_file, i) != 0) {
210 --i;
211 continue;
212 }
213
214 /*
215 * See if the file has been truncated.
216 */
217 if (sbuf.st_size < entryp->size) {
218 message(MSSG_TRUNC, entryp);
219 entryp->size = 0;
220 }
221
222 /*
223 * Seek to where the changes begin.
224 */
225 if (lseek(entryp->fd, entryp->size, 0) < 0) {
226 message(MSSG_SEEK, entryp);
227 rmv_entry(List_file, i--);
228 continue;
229 }
230
231 /*
232 * Dump the recently added info.
233 */
234 {
235 int nb;
236 static char buf[BUFSIZ];
237 message(MSSG_BANNER, entryp);
238 while ((nb = read(entryp->fd, buf, sizeof(buf))) > 0) {
239 (void) fwrite(buf, sizeof(char), (unsigned) nb, stdout);
240 entryp->size += nb;
241 }
242 if (nb < 0) {
243 message(MSSG_READ, entryp);
244 rmv_entry(List_file, i--);
245 continue;
246 }
247 }
248
249 /*
250 * Update the modification time.
251 */
252 entryp->mtime = sbuf.st_mtime;
253
254 /*
255 * Since we've changed the mtime, the list might no longer be
256 * sorted. However if this entry is already at the top of the
257 * list then it's OK.
258 */
259 if (i != 0)
260 Sorted = FALSE;
261
262 /*
263 * If we've just opened the file then force a resort now to
264 * prevent too many files from being opened.
265 */
266 if (!already_open)
267 fixup_open_files();
268
269 }
270
271
272 /*
273 * Go through list of nonexistent entries to see if any have appeared.
274 * This is done only once every CHECK_COUNT iterations.
275 */
276 if (!open_files_only) {
277 Dprintf(stderr, ">>> checking zapped list\n");
278 for (i = 0 ; i < List_zap->num_entries ; ++i) {
279 entryp = List_zap->list[i];
280 switch (stat_entry(List_zap, i, &sbuf)) {
281 case ENTRY_FILE: /* entry has appeared as a file */
282 message(MSSG_CREATED, entryp);
283 move_entry(List_file, List_zap, i--);
284 break;
285 case ENTRY_DIR: /* entry has appeared as a dir */
286 message(MSSG_CREATED, entryp);
287 move_entry(List_dir, List_zap, i--);
288 break;
289 case ENTRY_ZAP: /* entry still doesn't exist */
290 break;
291 case ENTRY_SPECIAL: /* entry is a special file */
292 message(MSSG_NOTAFIL, entryp);
293 rmv_entry(List_zap, i--);
294 break;
295 default: /* error - entry removed */
296 message(MSSG_STAT, entryp);
297 rmv_entry(List_zap, i--);
298 break;
299 }
300 }
301 }
302
303
304 /*
305 * Go through the list of dirs to see if any new files were created.
306 * This is done only once every CHECK_COUNT iterations.
307 */
308 if (!open_files_only) {
309 Dprintf(stderr, ">>> checking directory list\n");
310 for (i = 0 ; !open_files_only && i < List_dir->num_entries ; ++i) {
311 entryp = List_dir->list[i];
312 switch (stat_entry(List_dir, i, &sbuf)) {
313 case ENTRY_DIR: /* got status OK */
314 break;
315 case ENTRY_FILE: /* huh??? it's now a reg file */
316 move_entry(List_file, List_dir, i--);
317 continue;
318 case ENTRY_ZAP: /* entry has been deleted */
319 message(MSSG_ZAPPED, entryp);
320 move_entry(List_zap, List_dir, i--);
321 continue;
322 case ENTRY_SPECIAL: /* entry is a special file */
323 message(MSSG_NOTAFIL, entryp);
324 rmv_entry(List_dir, i--);
325 continue;
326 default: /* stat error */
327 message(MSSG_STAT, entryp);
328 rmv_entry(List_dir, i--);
329 continue;
330 }
331 if (entryp->mtime == sbuf.st_mtime)
332 continue;
333 if (scan_directory(entryp->name) != 0) {
334 message(MSSG_OPEN, entryp);
335 rmv_entry(List_dir, i--);
336 }
337 entryp->mtime = sbuf.st_mtime;
338 }
339 }
340
341
342 /*
343 * End of checking loop.
344 */
345 {
346 extern unsigned sleep();
347 (void) fflush(stdout);
348 (void) sleep(SLEEP_TIME);
349 }
350
351 }
352
353 /*NOTREACHED*/
354
355 }
356
357
sigcatcher(int sig)358 RETSIGTYPE sigcatcher(int sig)
359 {
360 if (sig == SIGQUIT)
361 (void) exit(0);
362 sigcaught = sig;
363 #ifdef STATUS_ENAB
364 (void) signal(SIGINT, sigcatcher);
365 (void) signal(SIGQUIT, sigcatcher);
366 #endif
367 }
368
369