source: MondoRescue/branches/3.3/mindi-busybox/coreutils/tail.c@ 3647

Last change on this file since 3647 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 9.6 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini tail implementation for busybox
4 *
5 * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
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 */
13
14/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
15 *
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)
25 */
26
27//kbuild:lib-$(CONFIG_TAIL) += tail.o
28
29//usage:#define tail_trivial_usage
30//usage: "[OPTIONS] [FILE]..."
31//usage:#define tail_full_usage "\n\n"
32//usage: "Print last 10 lines of each FILE (or stdin) to stdout.\n"
33//usage: "With more than one FILE, precede each with a filename header.\n"
34//usage: "\n -f Print data as file grows"
35//usage: "\n -c [+]N[kbm] Print last N bytes"
36//usage: "\n -n N[kbm] Print last N lines"
37//usage: "\n -n +N[kbm] Start on Nth line and print the rest"
38//usage: IF_FEATURE_FANCY_TAIL(
39//usage: "\n -q Never print headers"
40//usage: "\n -s SECONDS Wait SECONDS between reads with -f"
41//usage: "\n -v Always print headers"
42//usage: "\n -F Same as -f, but keep retrying"
43//usage: "\n"
44//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)."
45//usage: )
46//usage:
47//usage:#define tail_example_usage
48//usage: "$ tail -n 1 /etc/resolv.conf\n"
49//usage: "nameserver 10.0.0.1\n"
50
51#include "libbb.h"
52#include "common_bufsiz.h"
53
54struct globals {
55 bool from_top;
56 bool exitcode;
57} FIX_ALIASING;
58#define G (*(struct globals*)bb_common_bufsiz1)
59#define INIT_G() do { setup_common_bufsiz(); } while (0)
60
61static void tail_xprint_header(const char *fmt, const char *filename)
62{
63 if (fdprintf(STDOUT_FILENO, fmt, filename) < 0)
64 bb_perror_nomsg_and_die();
65}
66
67static ssize_t tail_read(int fd, char *buf, size_t count)
68{
69 ssize_t r;
70
71 r = full_read(fd, buf, count);
72 if (r < 0) {
73 bb_perror_msg(bb_msg_read_error);
74 G.exitcode = EXIT_FAILURE;
75 }
76
77 return r;
78}
79
80#define header_fmt_str "\n==> %s <==\n"
81
82static unsigned eat_num(const char *p)
83{
84 if (*p == '-')
85 p++;
86 else if (*p == '+') {
87 p++;
88 G.from_top = 1;
89 }
90 return xatou_sfx(p, bkm_suffixes);
91}
92
93int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
94int tail_main(int argc, char **argv)
95{
96 unsigned count = 10;
97 unsigned sleep_period = 1;
98 const char *str_c, *str_n;
99
100 char *tailbuf;
101 size_t tailbufsize;
102 unsigned header_threshhold = 1;
103 unsigned nfiles;
104 int i, opt;
105
106 int *fds;
107 const char *fmt;
108 int prev_fd;
109
110 INIT_G();
111
112#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
113 /* Allow legacy syntax of an initial numeric option without -n. */
114 if (argv[1] && (argv[1][0] == '+' || argv[1][0] == '-')
115 && isdigit(argv[1][1])
116 ) {
117 count = eat_num(argv[1]);
118 argv++;
119 argc--;
120 }
121#endif
122
123 /* -s NUM, -F imlies -f */
124 IF_FEATURE_FANCY_TAIL(opt_complementary = "s+:Ff";)
125 opt = getopt32(argv, "fc:n:" IF_FEATURE_FANCY_TAIL("qs:vF"),
126 &str_c, &str_n IF_FEATURE_FANCY_TAIL(,&sleep_period));
127#define FOLLOW (opt & 0x1)
128#define COUNT_BYTES (opt & 0x2)
129 //if (opt & 0x1) // -f
130 if (opt & 0x2) count = eat_num(str_c); // -c
131 if (opt & 0x4) count = eat_num(str_n); // -n
132#if ENABLE_FEATURE_FANCY_TAIL
133 /* q: make it impossible for nfiles to be > header_threshhold */
134 if (opt & 0x8) header_threshhold = UINT_MAX; // -q
135 //if (opt & 0x10) // -s
136 if (opt & 0x20) header_threshhold = 0; // -v
137# define FOLLOW_RETRY (opt & 0x40)
138#else
139# define FOLLOW_RETRY 0
140#endif
141 argc -= optind;
142 argv += optind;
143
144 /* open all the files */
145 fds = xmalloc(sizeof(fds[0]) * (argc + 1));
146 if (!argv[0]) {
147 struct stat statbuf;
148
149 if (fstat(STDIN_FILENO, &statbuf) == 0
150 && S_ISFIFO(statbuf.st_mode)
151 ) {
152 opt &= ~1; /* clear FOLLOW */
153 }
154 argv[0] = (char *) bb_msg_standard_input;
155 }
156 nfiles = i = 0;
157 do {
158 int fd = open_or_warn_stdin(argv[i]);
159 if (fd < 0 && !FOLLOW_RETRY) {
160 G.exitcode = EXIT_FAILURE;
161 continue;
162 }
163 fds[nfiles] = fd;
164 argv[nfiles++] = argv[i];
165 } while (++i < argc);
166
167 if (!nfiles)
168 bb_error_msg_and_die("no files");
169
170 /* prepare the buffer */
171 tailbufsize = BUFSIZ;
172 if (!G.from_top && COUNT_BYTES) {
173 if (tailbufsize < count + BUFSIZ) {
174 tailbufsize = count + BUFSIZ;
175 }
176 }
177 /* tail -c1024m REGULAR_FILE doesn't really need 1G mem block.
178 * (In fact, it doesn't need ANY memory). So delay allocation.
179 */
180 tailbuf = NULL;
181
182 /* tail the files */
183
184 fmt = header_fmt_str + 1; /* skip leading newline in the header on the first output */
185 i = 0;
186 do {
187 char *buf;
188 int taillen;
189 int newlines_seen;
190 unsigned seen;
191 int nread;
192 int fd = fds[i];
193
194 if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
195 continue; /* may happen with -F */
196
197 if (nfiles > header_threshhold) {
198 tail_xprint_header(fmt, argv[i]);
199 fmt = header_fmt_str;
200 }
201
202 if (!G.from_top) {
203 off_t current = lseek(fd, 0, SEEK_END);
204 if (current > 0) {
205 unsigned off;
206 if (COUNT_BYTES) {
207 /* Optimizing count-bytes case if the file is seekable.
208 * Beware of backing up too far.
209 * Also we exclude files with size 0 (because of /proc/xxx) */
210 if (count == 0)
211 continue; /* showing zero bytes is easy :) */
212 current -= count;
213 if (current < 0)
214 current = 0;
215 xlseek(fd, current, SEEK_SET);
216 bb_copyfd_size(fd, STDOUT_FILENO, count);
217 continue;
218 }
219#if 1 /* This is technically incorrect for *LONG* strings, but very useful */
220 /* Optimizing count-lines case if the file is seekable.
221 * We assume the lines are <64k.
222 * (Users complain that tail takes too long
223 * on multi-gigabyte files) */
224 off = (count | 0xf); /* for small counts, be more paranoid */
225 if (off > (INT_MAX / (64*1024)))
226 off = (INT_MAX / (64*1024));
227 current -= off * (64*1024);
228 if (current < 0)
229 current = 0;
230 xlseek(fd, current, SEEK_SET);
231#endif
232 }
233 }
234
235 if (!tailbuf)
236 tailbuf = xmalloc(tailbufsize);
237
238 buf = tailbuf;
239 taillen = 0;
240 /* "We saw 1st line/byte".
241 * Used only by +N code ("start from Nth", 1-based): */
242 seen = 1;
243 newlines_seen = 0;
244 while ((nread = tail_read(fd, buf, tailbufsize - taillen)) > 0) {
245 if (G.from_top) {
246 int nwrite = nread;
247 if (seen < count) {
248 /* We need to skip a few more bytes/lines */
249 if (COUNT_BYTES) {
250 nwrite -= (count - seen);
251 seen += nread;
252 } else {
253 char *s = buf;
254 do {
255 --nwrite;
256 if (*s++ == '\n' && ++seen == count) {
257 break;
258 }
259 } while (nwrite);
260 }
261 }
262 if (nwrite > 0)
263 xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite);
264 } else if (count) {
265 if (COUNT_BYTES) {
266 taillen += nread;
267 if (taillen > (int)count) {
268 memmove(tailbuf, tailbuf + taillen - count, count);
269 taillen = count;
270 }
271 } else {
272 int k = nread;
273 int newlines_in_buf = 0;
274
275 do { /* count '\n' in last read */
276 k--;
277 if (buf[k] == '\n') {
278 newlines_in_buf++;
279 }
280 } while (k);
281
282 if (newlines_seen + newlines_in_buf < (int)count) {
283 newlines_seen += newlines_in_buf;
284 taillen += nread;
285 } else {
286 int extra = (buf[nread-1] != '\n');
287 char *s;
288
289 k = newlines_seen + newlines_in_buf + extra - count;
290 s = tailbuf;
291 while (k) {
292 if (*s == '\n') {
293 k--;
294 }
295 s++;
296 }
297 taillen += nread - (s - tailbuf);
298 memmove(tailbuf, s, taillen);
299 newlines_seen = count - extra;
300 }
301 if (tailbufsize < (size_t)taillen + BUFSIZ) {
302 tailbufsize = taillen + BUFSIZ;
303 tailbuf = xrealloc(tailbuf, tailbufsize);
304 }
305 }
306 buf = tailbuf + taillen;
307 }
308 } /* while (tail_read() > 0) */
309 if (!G.from_top) {
310 xwrite(STDOUT_FILENO, tailbuf, taillen);
311 }
312 } while (++i < nfiles);
313 prev_fd = fds[i-1];
314
315 tailbuf = xrealloc(tailbuf, BUFSIZ);
316
317 fmt = NULL;
318
319 if (FOLLOW) while (1) {
320 sleep(sleep_period);
321
322 i = 0;
323 do {
324 int nread;
325 const char *filename = argv[i];
326 int fd = fds[i];
327
328 if (FOLLOW_RETRY) {
329 struct stat sbuf, fsbuf;
330
331 if (fd < 0
332 || fstat(fd, &fsbuf) < 0
333 || stat(filename, &sbuf) < 0
334 || fsbuf.st_dev != sbuf.st_dev
335 || fsbuf.st_ino != sbuf.st_ino
336 ) {
337 int new_fd;
338
339 if (fd >= 0)
340 close(fd);
341 new_fd = open(filename, O_RDONLY);
342 if (new_fd >= 0) {
343 bb_error_msg("%s has %s; following end of new file",
344 filename, (fd < 0) ? "appeared" : "been replaced"
345 );
346 } else if (fd >= 0) {
347 bb_perror_msg("%s has become inaccessible", filename);
348 }
349 fds[i] = fd = new_fd;
350 }
351 }
352 if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
353 continue;
354 if (nfiles > header_threshhold) {
355 fmt = header_fmt_str;
356 }
357 for (;;) {
358 /* tail -f keeps following files even if they are truncated */
359 struct stat sbuf;
360 /* /proc files report zero st_size, don't lseek them */
361 if (fstat(fd, &sbuf) == 0 && sbuf.st_size > 0) {
362 off_t current = lseek(fd, 0, SEEK_CUR);
363 if (sbuf.st_size < current)
364 xlseek(fd, 0, SEEK_SET);
365 }
366
367 nread = tail_read(fd, tailbuf, BUFSIZ);
368 if (nread <= 0)
369 break;
370 if (fmt && (fd != prev_fd)) {
371 tail_xprint_header(fmt, filename);
372 fmt = NULL;
373 prev_fd = fd;
374 }
375 xwrite(STDOUT_FILENO, tailbuf, nread);
376 }
377 } while (++i < nfiles);
378 } /* while (1) */
379
380 if (ENABLE_FEATURE_CLEAN_UP) {
381 free(fds);
382 free(tailbuf);
383 }
384 return G.exitcode;
385}
Note: See TracBrowser for help on using the repository browser.