1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70#include "libbb.h"
71#include "common_bufsiz.h"
72
73struct globals {
74 bool from_top;
75 bool exitcode;
76} FIX_ALIASING;
77#define G (*(struct globals*)bb_common_bufsiz1)
78#define INIT_G() do { setup_common_bufsiz(); } while (0)
79
80static void tail_xprint_header(const char *fmt, const char *filename)
81{
82 if (fdprintf(STDOUT_FILENO, fmt, filename) < 0)
83 bb_perror_nomsg_and_die();
84}
85
86static ssize_t tail_read(int fd, char *buf, size_t count)
87{
88 ssize_t r;
89
90 r = full_read(fd, buf, count);
91 if (r < 0) {
92 bb_perror_msg(bb_msg_read_error);
93 G.exitcode = EXIT_FAILURE;
94 }
95
96 return r;
97}
98
99#define header_fmt_str "\n==> %s <==\n"
100
101static unsigned eat_num(const char *p)
102{
103 if (*p == '-')
104 p++;
105 else if (*p == '+') {
106 p++;
107 G.from_top = 1;
108 }
109 return xatou_sfx(p, bkm_suffixes);
110}
111
112int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
113int tail_main(int argc, char **argv)
114{
115 unsigned count = 10;
116 unsigned sleep_period = 1;
117 const char *str_c, *str_n;
118
119 char *tailbuf;
120 size_t tailbufsize;
121 unsigned header_threshhold = 1;
122 unsigned nfiles;
123 int i, opt;
124
125 int *fds;
126 const char *fmt;
127 int prev_fd;
128
129 INIT_G();
130
131#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
132
133 if (argv[1] && (argv[1][0] == '+' || argv[1][0] == '-')
134 && isdigit(argv[1][1])
135 ) {
136 count = eat_num(argv[1]);
137 argv++;
138 argc--;
139 }
140#endif
141
142
143 opt = getopt32(argv, IF_FEATURE_FANCY_TAIL("^")
144 "fc:n:"IF_FEATURE_FANCY_TAIL("qs:+vF")
145 IF_FEATURE_FANCY_TAIL("\0" "Ff"),
146 &str_c, &str_n IF_FEATURE_FANCY_TAIL(,&sleep_period)
147 );
148#define FOLLOW (opt & 0x1)
149#define COUNT_BYTES (opt & 0x2)
150
151 if (opt & 0x2) count = eat_num(str_c);
152 if (opt & 0x4) count = eat_num(str_n);
153#if ENABLE_FEATURE_FANCY_TAIL
154
155 if (opt & 0x8) header_threshhold = UINT_MAX;
156
157 if (opt & 0x20) header_threshhold = 0;
158# define FOLLOW_RETRY (opt & 0x40)
159#else
160# define FOLLOW_RETRY 0
161#endif
162 argc -= optind;
163 argv += optind;
164
165
166 fds = xmalloc(sizeof(fds[0]) * (argc + 1));
167 if (!argv[0]) {
168 struct stat statbuf;
169
170 if (fstat(STDIN_FILENO, &statbuf) == 0
171 && S_ISFIFO(statbuf.st_mode)
172 ) {
173 opt &= ~1;
174 }
175 argv[0] = (char *) bb_msg_standard_input;
176 }
177 nfiles = i = 0;
178 do {
179 int fd = open_or_warn_stdin(argv[i]);
180 if (fd < 0 && !FOLLOW_RETRY) {
181 G.exitcode = EXIT_FAILURE;
182 continue;
183 }
184 fds[nfiles] = fd;
185 argv[nfiles++] = argv[i];
186 } while (++i < argc);
187
188 if (!nfiles)
189 bb_error_msg_and_die("no files");
190
191
192 tailbufsize = BUFSIZ;
193 if (!G.from_top && COUNT_BYTES) {
194 if (tailbufsize < count + BUFSIZ) {
195 tailbufsize = count + BUFSIZ;
196 }
197 }
198
199
200
201 tailbuf = NULL;
202
203
204
205 fmt = header_fmt_str + 1;
206 i = 0;
207 do {
208 char *buf;
209 int taillen;
210 int newlines_seen;
211 unsigned seen;
212 int nread;
213 int fd = fds[i];
214
215 if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
216 continue;
217
218 if (nfiles > header_threshhold) {
219 tail_xprint_header(fmt, argv[i]);
220 fmt = header_fmt_str;
221 }
222
223 if (!G.from_top) {
224 off_t current = lseek(fd, 0, SEEK_END);
225 if (current > 0) {
226 unsigned off;
227 if (COUNT_BYTES) {
228
229
230
231 if (count == 0)
232 continue;
233 current -= count;
234 if (current < 0)
235 current = 0;
236 xlseek(fd, current, SEEK_SET);
237 bb_copyfd_size(fd, STDOUT_FILENO, count);
238 continue;
239 }
240#if 1
241
242
243
244
245 off = (count | 0xf);
246 if (off > (INT_MAX / (64*1024)))
247 off = (INT_MAX / (64*1024));
248 current -= off * (64*1024);
249 if (current < 0)
250 current = 0;
251 xlseek(fd, current, SEEK_SET);
252#endif
253 }
254 }
255
256 if (!tailbuf)
257 tailbuf = xmalloc(tailbufsize);
258
259 buf = tailbuf;
260 taillen = 0;
261
262
263 seen = 1;
264 newlines_seen = 0;
265 while ((nread = tail_read(fd, buf, tailbufsize - taillen)) > 0) {
266 if (G.from_top) {
267 int nwrite = nread;
268 if (seen < count) {
269
270 if (COUNT_BYTES) {
271 nwrite -= (count - seen);
272 seen += nread;
273 } else {
274 char *s = buf;
275 do {
276 --nwrite;
277 if (*s++ == '\n' && ++seen == count) {
278 break;
279 }
280 } while (nwrite);
281 }
282 }
283 if (nwrite > 0)
284 xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite);
285 } else if (count) {
286 if (COUNT_BYTES) {
287 taillen += nread;
288 if (taillen > (int)count) {
289 memmove(tailbuf, tailbuf + taillen - count, count);
290 taillen = count;
291 }
292 } else {
293 int k = nread;
294 int newlines_in_buf = 0;
295
296 do {
297 k--;
298 if (buf[k] == '\n') {
299 newlines_in_buf++;
300 }
301 } while (k);
302
303 if (newlines_seen + newlines_in_buf < (int)count) {
304 newlines_seen += newlines_in_buf;
305 taillen += nread;
306 } else {
307 int extra = (buf[nread-1] != '\n');
308 char *s;
309
310 k = newlines_seen + newlines_in_buf + extra - count;
311 s = tailbuf;
312 while (k) {
313 if (*s == '\n') {
314 k--;
315 }
316 s++;
317 }
318 taillen += nread - (s - tailbuf);
319 memmove(tailbuf, s, taillen);
320 newlines_seen = count - extra;
321 }
322 if (tailbufsize < (size_t)taillen + BUFSIZ) {
323 tailbufsize = taillen + BUFSIZ;
324 tailbuf = xrealloc(tailbuf, tailbufsize);
325 }
326 }
327 buf = tailbuf + taillen;
328 }
329 }
330 if (!G.from_top) {
331 xwrite(STDOUT_FILENO, tailbuf, taillen);
332 }
333 } while (++i < nfiles);
334 prev_fd = fds[i-1];
335
336 tailbuf = xrealloc(tailbuf, BUFSIZ);
337
338 fmt = NULL;
339
340 if (FOLLOW) while (1) {
341 sleep(sleep_period);
342
343 i = 0;
344 do {
345 int nread;
346 const char *filename = argv[i];
347 int fd = fds[i];
348
349 if (FOLLOW_RETRY) {
350 struct stat sbuf, fsbuf;
351
352 if (fd < 0
353 || fstat(fd, &fsbuf) < 0
354 || stat(filename, &sbuf) < 0
355 || fsbuf.st_dev != sbuf.st_dev
356 || fsbuf.st_ino != sbuf.st_ino
357 ) {
358 int new_fd;
359
360 if (fd >= 0)
361 close(fd);
362 new_fd = open(filename, O_RDONLY);
363 if (new_fd >= 0) {
364 bb_error_msg("%s has %s; following end of new file",
365 filename, (fd < 0) ? "appeared" : "been replaced"
366 );
367 } else if (fd >= 0) {
368 bb_perror_msg("%s has become inaccessible", filename);
369 }
370 fds[i] = fd = new_fd;
371 }
372 }
373 if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
374 continue;
375 if (nfiles > header_threshhold) {
376 fmt = header_fmt_str;
377 }
378 for (;;) {
379
380 struct stat sbuf;
381
382 if (fstat(fd, &sbuf) == 0 && sbuf.st_size > 0) {
383 off_t current = lseek(fd, 0, SEEK_CUR);
384 if (sbuf.st_size < current)
385 xlseek(fd, 0, SEEK_SET);
386 }
387
388 nread = tail_read(fd, tailbuf, BUFSIZ);
389 if (nread <= 0)
390 break;
391 if (fmt && (fd != prev_fd)) {
392 tail_xprint_header(fmt, filename);
393 fmt = NULL;
394 prev_fd = fd;
395 }
396 xwrite(STDOUT_FILENO, tailbuf, nread);
397 }
398 } while (++i < nfiles);
399 }
400
401 if (ENABLE_FEATURE_CLEAN_UP) {
402 free(fds);
403 free(tailbuf);
404 }
405 return G.exitcode;
406}
407