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)
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:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
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.
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.
30 * See Documentation/deprecated.txt for more information.
38 #include <sys/types.h>
47 #ifdef HAVE_INOTIFY_INIT
48 #include <sys/inotify.h>
55 #include "closestream.h"
57 #define DEFAULT_LINES 10
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
)
66 fd
= open(filename
, O_RDONLY
);
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;
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 */
77 if (data
[i
] == '\n' && --lines
== 0) {
84 fwrite(data
+ i
, st
->st_size
- i
, 1, stdout
);
86 munmap(data
, st
->st_size
);
91 static void roll_file(const char *filename
, struct stat
*old
)
98 fd
= open(filename
, O_RDONLY
);
100 err(EXIT_FAILURE
, _("cannot open %s"), filename
);
102 if (fstat(fd
, &st
) == -1)
103 err(EXIT_FAILURE
, _("stat of %s failed"), filename
);
105 if (st
.st_size
== old
->st_size
) {
110 if (lseek(fd
, old
->st_size
, SEEK_SET
) != (off_t
)-1) {
113 while ((rc
= read(fd
, buf
, sizeof(buf
))) > 0) {
114 wc
= write(STDOUT_FILENO
, buf
, rc
);
116 warnx(_("incomplete write to \"%s\" (written %zd, expected %zd)\n"),
122 pos
= lseek(fd
, 0, SEEK_CUR
);
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.
128 old
->st_size
= (pos
!= -1 && pos
!= old
->st_size
) ? pos
: st
.st_size
;
133 static void watch_file(const char *filename
, struct stat
*old
)
136 roll_file(filename
, old
);
142 #ifdef HAVE_INOTIFY_INIT
144 #define EVENTS (IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)
147 static int watch_file_inotify(const char *filename
, struct stat
*old
)
149 char buf
[ NEVENTS
* sizeof(struct inotify_event
) ];
157 ffd
= inotify_add_watch(fd
, filename
, EVENTS
);
160 errx(EXIT_FAILURE
, _("%s: cannot add inotify watch "
161 "(limit of inotify watches was reached)."),
164 err(EXIT_FAILURE
, _("%s: cannot add inotify watch."), filename
);
168 len
= read(fd
, buf
, sizeof(buf
));
169 if (len
< 0 && (errno
== EINTR
|| errno
== EAGAIN
))
173 _("%s: cannot read inotify events"), filename
);
175 for (e
= 0; e
< len
; ) {
176 struct inotify_event
*ev
= (struct inotify_event
*) &buf
[e
];
178 if (ev
->mask
& IN_MODIFY
)
179 roll_file(filename
, old
);
185 e
+= sizeof(struct inotify_event
) + ev
->len
;
192 #endif /* HAVE_INOTIFY_INIT */
194 static void __attribute__ ((__noreturn__
)) usage(FILE *out
)
196 fputs(USAGE_HEADER
, out
);
197 fprintf(out
, _(" %s [option] <file>\n"), program_invocation_short_name
);
199 fputs(USAGE_SEPARATOR
, out
);
200 fputs(_("Follow the growth of a log file.\n"), out
);
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
);
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
);
212 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);
215 /* parses -N option */
216 static long old_style_option(int *argc
, char **argv
, size_t *lines
)
218 int i
= 1, nargs
= *argc
, ret
= 0;
221 if (argv
[i
][0] == '-' && isdigit(argv
[i
][1])) {
222 *lines
= strtoul_or_err(argv
[i
] + 1,
223 _("failed to parse number of lines"));
227 memmove(argv
+ i
, argv
+ i
+ 1,
228 sizeof(char *) * (nargs
- i
));
236 int main(int argc
, char **argv
)
238 const char *filename
;
243 static const struct option longopts
[] = {
244 { "lines", required_argument
, 0, 'n' },
245 { "version", no_argument
, 0, 'V' },
246 { "help", no_argument
, 0, 'h' },
250 setlocale(LC_ALL
, "");
251 bindtextdomain(PACKAGE
, LOCALEDIR
);
253 atexit(close_stdout
);
255 if (!old_style_option(&argc
, argv
, &lines
))
256 lines
= DEFAULT_LINES
;
258 while ((ch
= getopt_long(argc
, argv
, "n:N:Vh", longopts
, NULL
)) != -1)
262 lines
= strtoul_or_err(optarg
,
263 _("failed to parse number of lines"));
266 printf(UTIL_LINUX_VERSION
);
271 errtryhelp(EXIT_FAILURE
);
275 errx(EXIT_FAILURE
, _("no input file specified"));
277 filename
= argv
[optind
];
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
);
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
);
288 #ifdef HAVE_INOTIFY_INIT
289 if (!watch_file_inotify(filename
, &st
))
291 watch_file(filename
, &st
);