Upload Tizen:Base source
[framework/base/util-linux-ng.git] / text-utils / tailf.c
1 /* tailf.c -- tail a log file and then follow it
2  * Created: Tue Jan  9 15:49:21 1996 by faith@acm.org
3  * Copyright 1996, 2003 Rickard E. Faith (faith@acm.org)
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * less -F and tail -f cause a disk access every five seconds.  This
24  * program avoids this problem by waiting for the file size to change.
25  * Hence, the file is not accessed, and the access time does not need to be
26  * flushed back to disk.  This is sort of a "stealth" tail.
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <malloc.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <err.h>
40 #ifdef HAVE_INOTIFY_INIT
41 #include <sys/inotify.h>
42 #endif
43 #include "nls.h"
44
45 #define DEFAULT_LINES  10
46
47 static void
48 tailf(const char *filename, int lines)
49 {
50         char *buf, *p;
51         int  head = 0;
52         int  tail = 0;
53         FILE *str;
54         int  i;
55
56         if (!(str = fopen(filename, "r")))
57                 err(EXIT_FAILURE, _("cannot open \"%s\" for read"), filename);
58
59         buf = malloc(lines * BUFSIZ);
60         p = buf;
61         while (fgets(p, BUFSIZ, str)) {
62                 if (++tail >= lines) {
63                         tail = 0;
64                         head = 1;
65                 }
66                 p = buf + (tail * BUFSIZ);
67         }
68
69         if (head) {
70                 for (i = tail; i < lines; i++)
71                         fputs(buf + (i * BUFSIZ), stdout);
72                 for (i = 0; i < tail; i++)
73                         fputs(buf + (i * BUFSIZ), stdout);
74         } else {
75                 for (i = head; i < tail; i++)
76                         fputs(buf + (i * BUFSIZ), stdout);
77         }
78
79         fflush(stdout);
80         free(buf);
81         fclose(str);
82 }
83
84 static void
85 roll_file(const char *filename, off_t *size)
86 {
87         char buf[BUFSIZ];
88         int fd;
89         struct stat st;
90
91         if (!(fd = open(filename, O_RDONLY)))
92                 err(EXIT_FAILURE, _("cannot open \"%s\" for read"), filename);
93
94         if (fstat(fd, &st) == -1)
95                 err(EXIT_FAILURE, _("cannot stat \"%s\""), filename);
96
97         if (st.st_size == *size) {
98                 close(fd);
99                 return;
100         }
101
102         if (lseek(fd, *size, SEEK_SET) != (off_t)-1) {
103                 ssize_t rc, wc;
104
105                 while ((rc = read(fd, buf, sizeof(buf))) > 0) {
106                         wc = write(STDOUT_FILENO, buf, rc);
107                         if (rc != wc)
108                                 warnx(_("incomplete write to \"%s\" (written %zd, expected %zd)\n"),
109                                         filename, wc, rc);
110                 }
111                 fflush(stdout);
112         }
113         close(fd);
114         *size = st.st_size;
115 }
116
117 static void
118 watch_file(const char *filename, off_t *size)
119 {
120         do {
121                 roll_file(filename, size);
122                 usleep(250000);
123         } while(1);
124 }
125
126
127 #ifdef HAVE_INOTIFY_INIT
128
129 #define EVENTS          (IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)
130 #define NEVENTS         4
131
132 static int
133 watch_file_inotify(const char *filename, off_t *size)
134 {
135         char buf[ NEVENTS * sizeof(struct inotify_event) ];
136         int fd, ffd, e;
137         ssize_t len;
138
139         fd = inotify_init();
140         if (fd == -1)
141                 return 0;
142
143         ffd = inotify_add_watch(fd, filename, EVENTS);
144         if (ffd == -1) {
145                 if (errno == ENOSPC)
146                         errx(EXIT_FAILURE, _("%s: cannot add inotify watch "
147                                 "(limit of inotify watches was reached)."),
148                                 filename);
149
150                 err(EXIT_FAILURE, _("%s: cannot add inotify watch."), filename);
151         }
152
153         while (ffd >= 0) {
154                 len = read(fd, buf, sizeof(buf));
155                 if (len < 0 && (errno == EINTR || errno == EAGAIN))
156                         continue;
157                 if (len < 0)
158                         err(EXIT_FAILURE,
159                                 _("%s: cannot read inotify events"), filename);
160
161                 for (e = 0; e < len; ) {
162                         struct inotify_event *ev = (struct inotify_event *) &buf[e];
163
164                         if (ev->mask & IN_MODIFY)
165                                 roll_file(filename, size);
166                         else {
167                                 close(ffd);
168                                 ffd = -1;
169                                 break;
170                         }
171                         e += sizeof(struct inotify_event) + ev->len;
172                 }
173         }
174         close(fd);
175         return 1;
176 }
177
178 #endif /* HAVE_INOTIFY_INIT */
179
180 int main(int argc, char **argv)
181 {
182         const char *filename;
183         int lines = DEFAULT_LINES;
184         struct stat st;
185         off_t size = 0;
186
187         setlocale(LC_ALL, "");
188         bindtextdomain(PACKAGE, LOCALEDIR);
189         textdomain(PACKAGE);
190
191         argc--;
192         argv++;
193
194         for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
195                 if (!strcmp(*argv, "-n") || !strcmp(*argv, "--lines")) {
196                         argc--; argv++;
197                         if (argc > 0 && (lines = atoi(argv[0])) <= 0)
198                                 errx(EXIT_FAILURE, _("invalid number of lines"));
199                 }
200                 else if (isdigit(argv[0][1])) {
201                         if ((lines = atoi(*argv + 1)) <= 0)
202                                 errx(EXIT_FAILURE, _("invalid number of lines"));
203                 }
204                 else
205                         errx(EXIT_FAILURE, _("invalid option"));
206         }
207
208         if (argc != 1)
209                 errx(EXIT_FAILURE, _("usage: tailf [-n N | -N] logfile"));
210
211         filename = argv[0];
212
213         if (stat(filename, &st) != 0)
214                 err(EXIT_FAILURE, _("cannot stat \"%s\""), filename);
215
216         size = st.st_size;;
217         tailf(filename, lines);
218
219 #ifdef HAVE_INOTIFY_INIT
220         if (!watch_file_inotify(filename, &size))
221 #endif
222                 watch_file(filename, &size);
223
224         return EXIT_SUCCESS;
225 }
226