]> git.ipfire.org Git - thirdparty/util-linux.git/blob - text-utils/tailf.c
Use --help suggestion on invalid option
[thirdparty/util-linux.git] / text-utils / tailf.c
1 /* tailf.c -- tail a log file and then follow it
2 * Created: Tue Jan 9 15:49:21 1996 by faith@acm.org
3 * Copyright 1996, 2003 Rickard E. Faith (faith@acm.org)
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 /*
25 * This command is deprecated. The utility is in maintenance mode,
26 * meaning we keep them in source tree for backward compatibility
27 * only. Do not waste time making this command better, unless the
28 * fix is about security or other very critical issue.
29 *
30 * See Documentation/deprecated.txt for more information.
31 */
32
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <getopt.h>
44 #include <sys/mman.h>
45 #include <limits.h>
46
47 #ifdef HAVE_INOTIFY_INIT
48 #include <sys/inotify.h>
49 #endif
50
51 #include "nls.h"
52 #include "xalloc.h"
53 #include "strutils.h"
54 #include "c.h"
55 #include "closestream.h"
56
57 #define DEFAULT_LINES 10
58
59 /* st->st_size has to be greater than zero and smaller or equal to SIZE_MAX! */
60 static void tailf(const char *filename, size_t lines, struct stat *st)
61 {
62 int fd;
63 size_t i;
64 char *data;
65
66 fd = open(filename, O_RDONLY);
67 if (fd < 0)
68 err(EXIT_FAILURE, _("cannot open %s"), filename);
69 data = mmap(0, st->st_size, PROT_READ, MAP_SHARED, fd, 0);
70 i = (size_t) st->st_size - 1;
71
72 /* humans do not think last new line in a file should be counted,
73 * in that case do off by one from counter point of view */
74 if (data[i] == '\n')
75 lines++;
76 while (i) {
77 if (data[i] == '\n' && --lines == 0) {
78 i++;
79 break;
80 }
81 i--;
82 }
83
84 fwrite(data + i, st->st_size - i, 1, stdout);
85
86 munmap(data, st->st_size);
87 close(fd);
88 fflush(stdout);
89 }
90
91 static void roll_file(const char *filename, struct stat *old)
92 {
93 char buf[BUFSIZ];
94 int fd;
95 struct stat st;
96 off_t pos;
97
98 fd = open(filename, O_RDONLY);
99 if (fd < 0)
100 err(EXIT_FAILURE, _("cannot open %s"), filename);
101
102 if (fstat(fd, &st) == -1)
103 err(EXIT_FAILURE, _("stat of %s failed"), filename);
104
105 if (st.st_size == old->st_size) {
106 close(fd);
107 return;
108 }
109
110 if (lseek(fd, old->st_size, SEEK_SET) != (off_t)-1) {
111 ssize_t rc, wc;
112
113 while ((rc = read(fd, buf, sizeof(buf))) > 0) {
114 wc = write(STDOUT_FILENO, buf, rc);
115 if (rc != wc)
116 warnx(_("incomplete write to \"%s\" (written %zd, expected %zd)\n"),
117 filename, wc, rc);
118 }
119 fflush(stdout);
120 }
121
122 pos = lseek(fd, 0, SEEK_CUR);
123
124 /* If we've successfully read something, use the file position, this
125 * avoids data duplication. If we read nothing or hit an error, reset
126 * to the reported size, this handles truncated files.
127 */
128 old->st_size = (pos != -1 && pos != old->st_size) ? pos : st.st_size;
129
130 close(fd);
131 }
132
133 static void watch_file(const char *filename, struct stat *old)
134 {
135 do {
136 roll_file(filename, old);
137 xusleep(250000);
138 } while(1);
139 }
140
141
142 #ifdef HAVE_INOTIFY_INIT
143
144 #define EVENTS (IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)
145 #define NEVENTS 4
146
147 static int watch_file_inotify(const char *filename, struct stat *old)
148 {
149 char buf[ NEVENTS * sizeof(struct inotify_event) ];
150 int fd, ffd, e;
151 ssize_t len;
152
153 fd = inotify_init();
154 if (fd == -1)
155 return 0;
156
157 ffd = inotify_add_watch(fd, filename, EVENTS);
158 if (ffd == -1) {
159 if (errno == ENOSPC)
160 errx(EXIT_FAILURE, _("%s: cannot add inotify watch "
161 "(limit of inotify watches was reached)."),
162 filename);
163
164 err(EXIT_FAILURE, _("%s: cannot add inotify watch."), filename);
165 }
166
167 while (ffd >= 0) {
168 len = read(fd, buf, sizeof(buf));
169 if (len < 0 && (errno == EINTR || errno == EAGAIN))
170 continue;
171 if (len < 0)
172 err(EXIT_FAILURE,
173 _("%s: cannot read inotify events"), filename);
174
175 for (e = 0; e < len; ) {
176 struct inotify_event *ev = (struct inotify_event *) &buf[e];
177
178 if (ev->mask & IN_MODIFY)
179 roll_file(filename, old);
180 else {
181 close(ffd);
182 ffd = -1;
183 break;
184 }
185 e += sizeof(struct inotify_event) + ev->len;
186 }
187 }
188 close(fd);
189 return 1;
190 }
191
192 #endif /* HAVE_INOTIFY_INIT */
193
194 static void __attribute__ ((__noreturn__)) usage(FILE *out)
195 {
196 fputs(USAGE_HEADER, out);
197 fprintf(out, _(" %s [option] <file>\n"), program_invocation_short_name);
198
199 fputs(USAGE_SEPARATOR, out);
200 fputs(_("Follow the growth of a log file.\n"), out);
201
202 fputs(USAGE_OPTIONS, out);
203 fputs(_(" -n, --lines <number> output the last <number> lines\n"), out);
204 fputs(_(" -<number> same as '-n <number>'\n"), out);
205
206 fputs(USAGE_SEPARATOR, out);
207 fputs(USAGE_HELP, out);
208 fputs(USAGE_VERSION, out);
209 fprintf(out, USAGE_MAN_TAIL("tailf(1)"));
210 fputs(_("Warning: use of 'tailf' is deprecated, use 'tail -f' instead.\n"), out);
211
212 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
213 }
214
215 /* parses -N option */
216 static long old_style_option(int *argc, char **argv, size_t *lines)
217 {
218 int i = 1, nargs = *argc, ret = 0;
219
220 while(i < nargs) {
221 if (argv[i][0] == '-' && isdigit(argv[i][1])) {
222 *lines = strtoul_or_err(argv[i] + 1,
223 _("failed to parse number of lines"));
224 nargs--;
225 ret = 1;
226 if (nargs - i)
227 memmove(argv + i, argv + i + 1,
228 sizeof(char *) * (nargs - i));
229 } else
230 i++;
231 }
232 *argc = nargs;
233 return ret;
234 }
235
236 int main(int argc, char **argv)
237 {
238 const char *filename;
239 size_t lines;
240 int ch;
241 struct stat st;
242
243 static const struct option longopts[] = {
244 { "lines", required_argument, 0, 'n' },
245 { "version", no_argument, 0, 'V' },
246 { "help", no_argument, 0, 'h' },
247 { NULL, 0, 0, 0 }
248 };
249
250 setlocale(LC_ALL, "");
251 bindtextdomain(PACKAGE, LOCALEDIR);
252 textdomain(PACKAGE);
253 atexit(close_stdout);
254
255 if (!old_style_option(&argc, argv, &lines))
256 lines = DEFAULT_LINES;
257
258 while ((ch = getopt_long(argc, argv, "n:N:Vh", longopts, NULL)) != -1)
259 switch ((char)ch) {
260 case 'n':
261 case 'N':
262 lines = strtoul_or_err(optarg,
263 _("failed to parse number of lines"));
264 break;
265 case 'V':
266 printf(UTIL_LINUX_VERSION);
267 exit(EXIT_SUCCESS);
268 case 'h':
269 usage(stdout);
270 default:
271 errtryhelp(EXIT_FAILURE);
272 }
273
274 if (argc == optind)
275 errx(EXIT_FAILURE, _("no input file specified"));
276
277 filename = argv[optind];
278
279 if (stat(filename, &st) != 0)
280 err(EXIT_FAILURE, _("stat of %s failed"), filename);
281 if (!S_ISREG(st.st_mode))
282 errx(EXIT_FAILURE, _("%s: is not a file"), filename);
283
284 /* mmap is based on size_t */
285 if (st.st_size > 0 && (uintmax_t) st.st_size <= (uintmax_t) SIZE_MAX)
286 tailf(filename, lines, &st);
287
288 #ifdef HAVE_INOTIFY_INIT
289 if (!watch_file_inotify(filename, &st))
290 #endif
291 watch_file(filename, &st);
292
293 return EXIT_SUCCESS;
294 }
295