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