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