tail.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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. /* BB_AUDIT SUSv3 compliant (need fancy for -c) */
  10. /* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
  11. /* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
  12. /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
  13. *
  14. * Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
  15. * Bugs fixed (although I may have forgotten one or two... it was pretty bad)
  16. * 1) mixing printf/write without fflush()ing stdout
  17. * 2) no check that any open files are present
  18. * 3) optstring had -q taking an arg
  19. * 4) no error checking on write in some cases, and a warning even then
  20. * 5) q and s interaction bug
  21. * 6) no check for lseek error
  22. * 7) lseek attempted when count==0 even if arg was +0 (from top)
  23. */
  24. //usage:#define tail_trivial_usage
  25. //usage: "[OPTIONS] [FILE]..."
  26. //usage:#define tail_full_usage "\n\n"
  27. //usage: "Print last 10 lines of each FILE (or stdin) to stdout.\n"
  28. //usage: "With more than one FILE, precede each with a filename header.\n"
  29. //usage: "\n -f Print data as file grows"
  30. //usage: IF_FEATURE_FANCY_TAIL(
  31. //usage: "\n -s SECONDS Wait SECONDS between reads with -f"
  32. //usage: )
  33. //usage: "\n -n N[kbm] Print last N lines"
  34. //usage: IF_FEATURE_FANCY_TAIL(
  35. //usage: "\n -c N[kbm] Print last N bytes"
  36. //usage: "\n -q Never print headers"
  37. //usage: "\n -v Always print headers"
  38. //usage: "\n"
  39. //usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)."
  40. //usage: "\nIf N starts with a '+', output begins with the Nth item from the start"
  41. //usage: "\nof each file, not from the end."
  42. //usage: )
  43. //usage:
  44. //usage:#define tail_example_usage
  45. //usage: "$ tail -n 1 /etc/resolv.conf\n"
  46. //usage: "nameserver 10.0.0.1\n"
  47. #include "libbb.h"
  48. static const struct suffix_mult tail_suffixes[] = {
  49. { "b", 512 },
  50. { "k", 1024 },
  51. { "m", 1024*1024 },
  52. { "", 0 }
  53. };
  54. struct globals {
  55. bool from_top;
  56. bool exitcode;
  57. } FIX_ALIASING;
  58. #define G (*(struct globals*)&bb_common_bufsiz1)
  59. #define INIT_G() do { } while (0)
  60. static void tail_xprint_header(const char *fmt, const char *filename)
  61. {
  62. if (fdprintf(STDOUT_FILENO, fmt, filename) < 0)
  63. bb_perror_nomsg_and_die();
  64. }
  65. static ssize_t tail_read(int fd, char *buf, size_t count)
  66. {
  67. ssize_t r;
  68. off_t current;
  69. struct stat sbuf;
  70. /* /proc files report zero st_size, don't lseek them. */
  71. if (fstat(fd, &sbuf) == 0 && sbuf.st_size > 0) {
  72. current = lseek(fd, 0, SEEK_CUR);
  73. if (sbuf.st_size < current)
  74. xlseek(fd, 0, SEEK_SET);
  75. }
  76. r = full_read(fd, buf, count);
  77. if (r < 0) {
  78. bb_perror_msg(bb_msg_read_error);
  79. G.exitcode = EXIT_FAILURE;
  80. }
  81. return r;
  82. }
  83. #define header_fmt_str "\n==> %s <==\n"
  84. static unsigned eat_num(const char *p)
  85. {
  86. if (*p == '-')
  87. p++;
  88. else if (*p == '+') {
  89. p++;
  90. G.from_top = 1;
  91. }
  92. return xatou_sfx(p, tail_suffixes);
  93. }
  94. int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  95. int tail_main(int argc, char **argv)
  96. {
  97. unsigned count = 10;
  98. unsigned sleep_period = 1;
  99. const char *str_c, *str_n;
  100. char *tailbuf;
  101. size_t tailbufsize;
  102. unsigned header_threshhold = 1;
  103. unsigned nfiles;
  104. int i, opt;
  105. int *fds;
  106. const char *fmt;
  107. INIT_G();
  108. #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
  109. /* Allow legacy syntax of an initial numeric option without -n. */
  110. if (argv[1] && (argv[1][0] == '+' || argv[1][0] == '-')
  111. && isdigit(argv[1][1])
  112. ) {
  113. count = eat_num(argv[1]);
  114. argv++;
  115. argc--;
  116. }
  117. #endif
  118. /* -s NUM, -F imlies -f */
  119. IF_FEATURE_FANCY_TAIL(opt_complementary = "s+:Ff";)
  120. opt = getopt32(argv, "fc:n:" IF_FEATURE_FANCY_TAIL("qs:vF"),
  121. &str_c, &str_n IF_FEATURE_FANCY_TAIL(,&sleep_period));
  122. #define FOLLOW (opt & 0x1)
  123. #define COUNT_BYTES (opt & 0x2)
  124. //if (opt & 0x1) // -f
  125. if (opt & 0x2) count = eat_num(str_c); // -c
  126. if (opt & 0x4) count = eat_num(str_n); // -n
  127. #if ENABLE_FEATURE_FANCY_TAIL
  128. /* q: make it impossible for nfiles to be > header_threshhold */
  129. if (opt & 0x8) header_threshhold = UINT_MAX; // -q
  130. //if (opt & 0x10) // -s
  131. if (opt & 0x20) header_threshhold = 0; // -v
  132. # define FOLLOW_RETRY (opt & 0x40)
  133. #else
  134. # define FOLLOW_RETRY 0
  135. #endif
  136. argc -= optind;
  137. argv += optind;
  138. /* open all the files */
  139. fds = xmalloc(sizeof(fds[0]) * (argc + 1));
  140. if (!argv[0]) {
  141. struct stat statbuf;
  142. if (fstat(STDIN_FILENO, &statbuf) == 0
  143. && S_ISFIFO(statbuf.st_mode)
  144. ) {
  145. opt &= ~1; /* clear FOLLOW */
  146. }
  147. argv[0] = (char *) bb_msg_standard_input;
  148. }
  149. nfiles = i = 0;
  150. do {
  151. int fd = open_or_warn_stdin(argv[i]);
  152. if (fd < 0 && !FOLLOW_RETRY) {
  153. G.exitcode = EXIT_FAILURE;
  154. continue;
  155. }
  156. fds[nfiles] = fd;
  157. argv[nfiles++] = argv[i];
  158. } while (++i < argc);
  159. if (!nfiles)
  160. bb_error_msg_and_die("no files");
  161. /* prepare the buffer */
  162. tailbufsize = BUFSIZ;
  163. if (!G.from_top && COUNT_BYTES) {
  164. if (tailbufsize < count + BUFSIZ) {
  165. tailbufsize = count + BUFSIZ;
  166. }
  167. }
  168. /* tail -c1024m REGULAR_FILE doesn't really need 1G mem block.
  169. * (In fact, it doesn't need ANY memory). So delay allocation.
  170. */
  171. tailbuf = NULL;
  172. /* tail the files */
  173. fmt = header_fmt_str + 1; /* skip leading newline in the header on the first output */
  174. i = 0;
  175. do {
  176. char *buf;
  177. int taillen;
  178. int newlines_seen;
  179. unsigned seen;
  180. int nread;
  181. int fd = fds[i];
  182. if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
  183. continue; /* may happen with -F */
  184. if (nfiles > header_threshhold) {
  185. tail_xprint_header(fmt, argv[i]);
  186. fmt = header_fmt_str;
  187. }
  188. if (!G.from_top) {
  189. off_t current = lseek(fd, 0, SEEK_END);
  190. if (current > 0) {
  191. unsigned off;
  192. if (COUNT_BYTES) {
  193. /* Optimizing count-bytes case if the file is seekable.
  194. * Beware of backing up too far.
  195. * Also we exclude files with size 0 (because of /proc/xxx) */
  196. if (count == 0)
  197. continue; /* showing zero bytes is easy :) */
  198. current -= count;
  199. if (current < 0)
  200. current = 0;
  201. xlseek(fd, current, SEEK_SET);
  202. bb_copyfd_size(fd, STDOUT_FILENO, count);
  203. continue;
  204. }
  205. #if 1 /* This is technically incorrect for *LONG* strings, but very useful */
  206. /* Optimizing count-lines case if the file is seekable.
  207. * We assume the lines are <64k.
  208. * (Users complain that tail takes too long
  209. * on multi-gigabyte files) */
  210. off = (count | 0xf); /* for small counts, be more paranoid */
  211. if (off > (INT_MAX / (64*1024)))
  212. off = (INT_MAX / (64*1024));
  213. current -= off * (64*1024);
  214. if (current < 0)
  215. current = 0;
  216. xlseek(fd, current, SEEK_SET);
  217. #endif
  218. }
  219. }
  220. if (!tailbuf)
  221. tailbuf = xmalloc(tailbufsize);
  222. buf = tailbuf;
  223. taillen = 0;
  224. /* "We saw 1st line/byte".
  225. * Used only by +N code ("start from Nth", 1-based): */
  226. seen = 1;
  227. newlines_seen = 0;
  228. while ((nread = tail_read(fd, buf, tailbufsize - taillen)) > 0) {
  229. if (G.from_top) {
  230. int nwrite = nread;
  231. if (seen < count) {
  232. /* We need to skip a few more bytes/lines */
  233. if (COUNT_BYTES) {
  234. nwrite -= (count - seen);
  235. seen += nread;
  236. } else {
  237. char *s = buf;
  238. do {
  239. --nwrite;
  240. if (*s++ == '\n' && ++seen == count) {
  241. break;
  242. }
  243. } while (nwrite);
  244. }
  245. }
  246. if (nwrite > 0)
  247. xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite);
  248. } else if (count) {
  249. if (COUNT_BYTES) {
  250. taillen += nread;
  251. if (taillen > (int)count) {
  252. memmove(tailbuf, tailbuf + taillen - count, count);
  253. taillen = count;
  254. }
  255. } else {
  256. int k = nread;
  257. int newlines_in_buf = 0;
  258. do { /* count '\n' in last read */
  259. k--;
  260. if (buf[k] == '\n') {
  261. newlines_in_buf++;
  262. }
  263. } while (k);
  264. if (newlines_seen + newlines_in_buf < (int)count) {
  265. newlines_seen += newlines_in_buf;
  266. taillen += nread;
  267. } else {
  268. int extra = (buf[nread-1] != '\n');
  269. char *s;
  270. k = newlines_seen + newlines_in_buf + extra - count;
  271. s = tailbuf;
  272. while (k) {
  273. if (*s == '\n') {
  274. k--;
  275. }
  276. s++;
  277. }
  278. taillen += nread - (s - tailbuf);
  279. memmove(tailbuf, s, taillen);
  280. newlines_seen = count - extra;
  281. }
  282. if (tailbufsize < (size_t)taillen + BUFSIZ) {
  283. tailbufsize = taillen + BUFSIZ;
  284. tailbuf = xrealloc(tailbuf, tailbufsize);
  285. }
  286. }
  287. buf = tailbuf + taillen;
  288. }
  289. } /* while (tail_read() > 0) */
  290. if (!G.from_top) {
  291. xwrite(STDOUT_FILENO, tailbuf, taillen);
  292. }
  293. } while (++i < nfiles);
  294. tailbuf = xrealloc(tailbuf, BUFSIZ);
  295. fmt = NULL;
  296. if (FOLLOW) while (1) {
  297. sleep(sleep_period);
  298. i = 0;
  299. do {
  300. int nread;
  301. const char *filename = argv[i];
  302. int fd = fds[i];
  303. if (FOLLOW_RETRY) {
  304. struct stat sbuf, fsbuf;
  305. if (fd < 0
  306. || fstat(fd, &fsbuf) < 0
  307. || stat(filename, &sbuf) < 0
  308. || fsbuf.st_dev != sbuf.st_dev
  309. || fsbuf.st_ino != sbuf.st_ino
  310. ) {
  311. int new_fd;
  312. if (fd >= 0)
  313. close(fd);
  314. new_fd = open(filename, O_RDONLY);
  315. if (new_fd >= 0) {
  316. bb_error_msg("%s has %s; following end of new file",
  317. filename, (fd < 0) ? "appeared" : "been replaced"
  318. );
  319. } else if (fd >= 0) {
  320. bb_perror_msg("%s has become inaccessible", filename);
  321. }
  322. fds[i] = fd = new_fd;
  323. }
  324. }
  325. if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
  326. continue;
  327. if (nfiles > header_threshhold) {
  328. fmt = header_fmt_str;
  329. }
  330. while ((nread = tail_read(fd, tailbuf, BUFSIZ)) > 0) {
  331. if (fmt) {
  332. tail_xprint_header(fmt, filename);
  333. fmt = NULL;
  334. }
  335. xwrite(STDOUT_FILENO, tailbuf, nread);
  336. }
  337. } while (++i < nfiles);
  338. } /* while (1) */
  339. if (ENABLE_FEATURE_CLEAN_UP) {
  340. free(fds);
  341. free(tailbuf);
  342. }
  343. return G.exitcode;
  344. }