1 /* vi: set sw=4 ts=4: */
3 * Mini tail implementation for busybox
5 * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 /* BB_AUDIT SUSv3 compliant (need fancy for -c) */
11 /* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
12 /* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
14 /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
16 * Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
17 * Bugs fixed (although I may have forgotten one or two... it was pretty bad)
18 * 1) mixing printf/write without fflush()ing stdout
19 * 2) no check that any open files are present
20 * 3) optstring had -q taking an arg
21 * 4) no error checking on write in some cases, and a warning even then
22 * 5) q and s interaction bug
23 * 6) no check for lseek error
24 * 7) lseek attempted when count==0 even if arg was +0 (from top)
27 //usage:#define tail_trivial_usage
28 //usage: "[OPTIONS] [FILE]..."
29 //usage:#define tail_full_usage "\n\n"
30 //usage: "Print last 10 lines of each FILE (or stdin) to stdout.\n"
31 //usage: "With more than one FILE, precede each with a filename header.\n"
32 //usage: "\n -f Print data as file grows"
33 //usage: IF_FEATURE_FANCY_TAIL(
34 //usage: "\n -s SECONDS Wait SECONDS between reads with -f"
36 //usage: "\n -n N[kbm] Print last N lines"
37 //usage: IF_FEATURE_FANCY_TAIL(
38 //usage: "\n -c N[kbm] Print last N bytes"
39 //usage: "\n -q Never print headers"
40 //usage: "\n -v Always print headers"
42 //usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)."
43 //usage: "\nIf N starts with a '+', output begins with the Nth item from the start"
44 //usage: "\nof each file, not from the end."
47 //usage:#define tail_example_usage
48 //usage: "$ tail -n 1 /etc/resolv.conf\n"
49 //usage: "nameserver 10.0.0.1\n"
53 static const struct suffix_mult tail_suffixes
[] = {
64 #define G (*(struct globals*)&bb_common_bufsiz1)
65 #define INIT_G() do { } while (0)
67 static void tail_xprint_header(const char *fmt
, const char *filename
)
69 if (fdprintf(STDOUT_FILENO
, fmt
, filename
) < 0)
70 bb_perror_nomsg_and_die();
73 static ssize_t
tail_read(int fd
, char *buf
, size_t count
)
79 /* /proc files report zero st_size, don't lseek them. */
80 if (fstat(fd
, &sbuf
) == 0 && sbuf
.st_size
> 0) {
81 current
= lseek(fd
, 0, SEEK_CUR
);
82 if (sbuf
.st_size
< current
)
83 xlseek(fd
, 0, SEEK_SET
);
86 r
= full_read(fd
, buf
, count
);
88 bb_perror_msg(bb_msg_read_error
);
89 G
.exitcode
= EXIT_FAILURE
;
95 #define header_fmt_str "\n==> %s <==\n"
97 static unsigned eat_num(const char *p
)
101 else if (*p
== '+') {
105 return xatou_sfx(p
, tail_suffixes
);
108 int tail_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
109 int tail_main(int argc
, char **argv
)
112 unsigned sleep_period
= 1;
113 const char *str_c
, *str_n
;
117 unsigned header_threshhold
= 1;
126 #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
127 /* Allow legacy syntax of an initial numeric option without -n. */
128 if (argv
[1] && (argv
[1][0] == '+' || argv
[1][0] == '-')
129 && isdigit(argv
[1][1])
131 count
= eat_num(argv
[1]);
137 /* -s NUM, -F imlies -f */
138 IF_FEATURE_FANCY_TAIL(opt_complementary
= "s+:Ff";)
139 opt
= getopt32(argv
, "fc:n:" IF_FEATURE_FANCY_TAIL("qs:vF"),
140 &str_c
, &str_n
IF_FEATURE_FANCY_TAIL(,&sleep_period
));
141 #define FOLLOW (opt & 0x1)
142 #define COUNT_BYTES (opt & 0x2)
143 //if (opt & 0x1) // -f
144 if (opt
& 0x2) count
= eat_num(str_c
); // -c
145 if (opt
& 0x4) count
= eat_num(str_n
); // -n
146 #if ENABLE_FEATURE_FANCY_TAIL
147 /* q: make it impossible for nfiles to be > header_threshhold */
148 if (opt
& 0x8) header_threshhold
= UINT_MAX
; // -q
149 //if (opt & 0x10) // -s
150 if (opt
& 0x20) header_threshhold
= 0; // -v
151 # define FOLLOW_RETRY (opt & 0x40)
153 # define FOLLOW_RETRY 0
158 /* open all the files */
159 fds
= xmalloc(sizeof(fds
[0]) * (argc
+ 1));
163 if (fstat(STDIN_FILENO
, &statbuf
) == 0
164 && S_ISFIFO(statbuf
.st_mode
)
166 opt
&= ~1; /* clear FOLLOW */
168 argv
[0] = (char *) bb_msg_standard_input
;
172 int fd
= open_or_warn_stdin(argv
[i
]);
173 if (fd
< 0 && !FOLLOW_RETRY
) {
174 G
.exitcode
= EXIT_FAILURE
;
178 argv
[nfiles
++] = argv
[i
];
179 } while (++i
< argc
);
182 bb_error_msg_and_die("no files");
184 /* prepare the buffer */
185 tailbufsize
= BUFSIZ
;
186 if (!G
.from_top
&& COUNT_BYTES
) {
187 if (tailbufsize
< count
+ BUFSIZ
) {
188 tailbufsize
= count
+ BUFSIZ
;
191 /* tail -c1024m REGULAR_FILE doesn't really need 1G mem block.
192 * (In fact, it doesn't need ANY memory). So delay allocation.
198 fmt
= header_fmt_str
+ 1; /* skip leading newline in the header on the first output */
208 if (ENABLE_FEATURE_FANCY_TAIL
&& fd
< 0)
209 continue; /* may happen with -F */
211 if (nfiles
> header_threshhold
) {
212 tail_xprint_header(fmt
, argv
[i
]);
213 fmt
= header_fmt_str
;
217 off_t current
= lseek(fd
, 0, SEEK_END
);
221 /* Optimizing count-bytes case if the file is seekable.
222 * Beware of backing up too far.
223 * Also we exclude files with size 0 (because of /proc/xxx) */
225 continue; /* showing zero bytes is easy :) */
229 xlseek(fd
, current
, SEEK_SET
);
230 bb_copyfd_size(fd
, STDOUT_FILENO
, count
);
233 #if 1 /* This is technically incorrect for *LONG* strings, but very useful */
234 /* Optimizing count-lines case if the file is seekable.
235 * We assume the lines are <64k.
236 * (Users complain that tail takes too long
237 * on multi-gigabyte files) */
238 off
= (count
| 0xf); /* for small counts, be more paranoid */
239 if (off
> (INT_MAX
/ (64*1024)))
240 off
= (INT_MAX
/ (64*1024));
241 current
-= off
* (64*1024);
244 xlseek(fd
, current
, SEEK_SET
);
250 tailbuf
= xmalloc(tailbufsize
);
254 /* "We saw 1st line/byte".
255 * Used only by +N code ("start from Nth", 1-based): */
258 while ((nread
= tail_read(fd
, buf
, tailbufsize
- taillen
)) > 0) {
262 /* We need to skip a few more bytes/lines */
264 nwrite
-= (count
- seen
);
270 if (*s
++ == '\n' && ++seen
== count
) {
277 xwrite(STDOUT_FILENO
, buf
+ nread
- nwrite
, nwrite
);
281 if (taillen
> (int)count
) {
282 memmove(tailbuf
, tailbuf
+ taillen
- count
, count
);
287 int newlines_in_buf
= 0;
289 do { /* count '\n' in last read */
291 if (buf
[k
] == '\n') {
296 if (newlines_seen
+ newlines_in_buf
< (int)count
) {
297 newlines_seen
+= newlines_in_buf
;
300 int extra
= (buf
[nread
-1] != '\n');
303 k
= newlines_seen
+ newlines_in_buf
+ extra
- count
;
311 taillen
+= nread
- (s
- tailbuf
);
312 memmove(tailbuf
, s
, taillen
);
313 newlines_seen
= count
- extra
;
315 if (tailbufsize
< (size_t)taillen
+ BUFSIZ
) {
316 tailbufsize
= taillen
+ BUFSIZ
;
317 tailbuf
= xrealloc(tailbuf
, tailbufsize
);
320 buf
= tailbuf
+ taillen
;
322 } /* while (tail_read() > 0) */
324 xwrite(STDOUT_FILENO
, tailbuf
, taillen
);
326 } while (++i
< nfiles
);
328 tailbuf
= xrealloc(tailbuf
, BUFSIZ
);
332 if (FOLLOW
) while (1) {
338 const char *filename
= argv
[i
];
342 struct stat sbuf
, fsbuf
;
345 || fstat(fd
, &fsbuf
) < 0
346 || stat(filename
, &sbuf
) < 0
347 || fsbuf
.st_dev
!= sbuf
.st_dev
348 || fsbuf
.st_ino
!= sbuf
.st_ino
354 new_fd
= open(filename
, O_RDONLY
);
356 bb_error_msg("%s has %s; following end of new file",
357 filename
, (fd
< 0) ? "appeared" : "been replaced"
359 } else if (fd
>= 0) {
360 bb_perror_msg("%s has become inaccessible", filename
);
362 fds
[i
] = fd
= new_fd
;
365 if (ENABLE_FEATURE_FANCY_TAIL
&& fd
< 0)
367 if (nfiles
> header_threshhold
) {
368 fmt
= header_fmt_str
;
370 while ((nread
= tail_read(fd
, tailbuf
, BUFSIZ
)) > 0) {
372 tail_xprint_header(fmt
, filename
);
375 xwrite(STDOUT_FILENO
, tailbuf
, nread
);
377 } while (++i
< nfiles
);
380 if (ENABLE_FEATURE_CLEAN_UP
) {