Print this page
3484 enhance and document tail follow support
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/tail/forward.c
+++ new/usr/src/cmd/tail/forward.c
1 1 /*
2 2 * Copyright (c) 1991, 1993
3 3 * The Regents of the University of California. All rights reserved.
4 4 *
5 5 * This code is derived from software contributed to Berkeley by
6 6 * Edward Sze-Tyan Wang.
7 7 *
8 8 * Redistribution and use in source and binary forms, with or without
9 9 * modification, are permitted provided that the following conditions
10 10 * are met:
11 11 * 1. Redistributions of source code must retain the above copyright
12 12 * notice, this list of conditions and the following disclaimer.
13 13 * 2. Redistributions in binary form must reproduce the above copyright
14 14 * notice, this list of conditions and the following disclaimer in the
15 15 * documentation and/or other materials provided with the distribution.
16 16 * 4. Neither the name of the University nor the names of its contributors
17 17 * may be used to endorse or promote products derived from this software
18 18 * without specific prior written permission.
19 19 *
20 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
↓ open down ↓ |
23 lines elided |
↑ open up ↑ |
24 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 30 * SUCH DAMAGE.
31 31 */
32 32
33 33 /*
34 - * Solaris porting notes: the original FreeBSD version made use of the
35 - * BSD kqueue event notification framework; this
36 - * was changed to use usleep()
34 + * Copyright (c) 2013, Joyent, Inc. All rights reserved.
37 35 */
38 36
39 37 #include <sys/param.h>
40 38 #include <sys/mount.h>
41 39 #include <sys/types.h>
42 40 #include <sys/stat.h>
43 41 #include <sys/statfs.h>
44 42 #include <sys/statvfs.h>
45 43 #include <sys/time.h>
46 44 #include <sys/mman.h>
47 45 #include <sys/poll.h>
48 46 #include <port.h>
49 47 #include <err.h>
50 48 #include <errno.h>
51 49 #include <fcntl.h>
52 50 #include <limits.h>
53 51 #include <stdio.h>
54 52 #include <stdlib.h>
55 53 #include <string.h>
54 +#include <strings.h>
56 55 #include <unistd.h>
57 56
58 57 #include "extern.h"
59 58
60 59 static void rlines(FILE *, const char *fn, off_t, struct stat *);
61 60 static int show(file_info_t *);
62 61 static void set_events(file_info_t *files);
63 62
64 63 /* defines for inner loop actions */
65 64 #define USE_SLEEP 0
65 +#define USE_PORT 1
66 66 #define ADD_EVENTS 2
67 67
68 -int action = USE_SLEEP;
68 +int port;
69 +int action = USE_PORT;
69 70
70 71 static const file_info_t *last;
71 72
72 73 /*
73 74 * forward -- display the file, from an offset, forward.
74 75 *
75 76 * There are eight separate cases for this -- regular and non-regular
76 77 * files, by bytes or lines and from the beginning or end of the file.
77 78 *
78 79 * FBYTES byte offset from the beginning of the file
79 80 * REG seek
80 81 * NOREG read, counting bytes
81 82 *
82 83 * FLINES line offset from the beginning of the file
83 84 * REG read, counting lines
84 85 * NOREG read, counting lines
85 86 *
86 87 * RBYTES byte offset from the end of the file
87 88 * REG seek
88 89 * NOREG cyclically read characters into a wrap-around buffer
89 90 *
90 91 * RLINES
91 92 * REG mmap the file and step back until reach the correct offset.
92 93 * NOREG cyclically read lines into a wrap-around array of buffers
93 94 */
94 95 void
95 96 forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
96 97 {
97 98 int ch;
98 99
99 100 switch (style) {
100 101 case FBYTES:
101 102 if (off == 0)
102 103 break;
103 104 if (S_ISREG(sbp->st_mode)) {
104 105 if (sbp->st_size < off)
105 106 off = sbp->st_size;
106 107 if (fseeko(fp, off, SEEK_SET) == -1) {
107 108 ierr(fn);
108 109 return;
109 110 }
110 111 } else while (off--)
111 112 if ((ch = getc(fp)) == EOF) {
112 113 if (ferror(fp)) {
113 114 ierr(fn);
114 115 return;
115 116 }
116 117 break;
117 118 }
118 119 break;
119 120 case FLINES:
120 121 if (off == 0)
121 122 break;
122 123 for (;;) {
123 124 if ((ch = getc(fp)) == EOF) {
124 125 if (ferror(fp)) {
125 126 ierr(fn);
126 127 return;
127 128 }
128 129 break;
129 130 }
130 131 if (ch == '\n' && !--off)
131 132 break;
132 133 }
133 134 break;
134 135 case RBYTES:
135 136 if (S_ISREG(sbp->st_mode)) {
136 137 if (sbp->st_size >= off &&
137 138 fseeko(fp, -off, SEEK_END) == -1) {
138 139 ierr(fn);
139 140 return;
140 141 }
141 142 } else if (off == 0) {
142 143 while (getc(fp) != EOF)
143 144 ;
144 145 if (ferror(fp)) {
145 146 ierr(fn);
146 147 return;
147 148 }
148 149 } else
149 150 if (bytes(fp, fn, off))
150 151 return;
151 152 break;
152 153 case RLINES:
153 154 if (S_ISREG(sbp->st_mode))
154 155 if (!off) {
155 156 if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
156 157 ierr(fn);
157 158 return;
158 159 }
159 160 } else
160 161 rlines(fp, fn, off, sbp);
161 162 else if (off == 0) {
162 163 while (getc(fp) != EOF)
163 164 ;
164 165 if (ferror(fp)) {
165 166 ierr(fn);
166 167 return;
167 168 }
168 169 } else
169 170 if (lines(fp, fn, off))
170 171 return;
171 172 break;
172 173 default:
173 174 break;
174 175 }
175 176
176 177 while ((ch = getc(fp)) != EOF)
177 178 if (putchar(ch) == EOF)
178 179 oerr();
179 180 if (ferror(fp)) {
180 181 ierr(fn);
181 182 return;
182 183 }
183 184 (void) fflush(stdout);
184 185 }
185 186
186 187 /*
187 188 * rlines -- display the last offset lines of the file.
188 189 */
189 190 static void
190 191 rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
191 192 {
192 193 struct mapinfo map;
193 194 off_t curoff, size;
194 195 int i;
195 196
196 197 if ((size = sbp->st_size) == 0)
197 198 return;
198 199 map.start = NULL;
199 200 map.fd = fileno(fp);
200 201 map.mapoff = map.maxoff = size;
201 202
202 203 /*
203 204 * Last char is special, ignore whether newline or not. Note that
204 205 * size == 0 is dealt with above, and size == 1 sets curoff to -1.
205 206 */
206 207 curoff = size - 2;
207 208 while (curoff >= 0) {
208 209 if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
209 210 ierr(fn);
210 211 return;
211 212 }
212 213 for (i = curoff - map.mapoff; i >= 0; i--)
213 214 if (map.start[i] == '\n' && --off == 0)
214 215 break;
215 216 /* `i' is either the map offset of a '\n', or -1. */
216 217 curoff = map.mapoff + i;
217 218 if (i >= 0)
218 219 break;
219 220 }
220 221 curoff++;
221 222 if (mapprint(&map, curoff, size - curoff) != 0) {
222 223 ierr(fn);
223 224 exit(1);
224 225 }
225 226
226 227 /* Set the file pointer to reflect the length displayed. */
227 228 if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
228 229 ierr(fn);
229 230 return;
230 231 }
231 232 if (map.start != NULL && munmap(map.start, map.maplen)) {
232 233 ierr(fn);
233 234 return;
234 235 }
235 236 }
236 237
237 238 static int
238 239 show(file_info_t *file)
239 240 {
240 241 int ch;
241 242
242 243 while ((ch = getc(file->fp)) != EOF) {
243 244 if (last != file && no_files > 1) {
244 245 if (!qflag)
245 246 (void) printf("\n==> %s <==\n",
246 247 file->file_name);
247 248 last = file;
248 249 }
249 250 if (putchar(ch) == EOF)
250 251 oerr();
251 252 }
252 253 (void) fflush(stdout);
253 254 if (ferror(file->fp)) {
↓ open down ↓ |
175 lines elided |
↑ open up ↑ |
254 255 (void) fclose(file->fp);
255 256 file->fp = NULL;
256 257 ierr(file->file_name);
257 258 return (0);
258 259 }
259 260 clearerr(file->fp);
260 261 return (1);
261 262 }
262 263
263 264 static void
265 +associate(file_info_t *file, boolean_t assoc)
266 +{
267 + char buf[64];
268 +
269 + if (action != USE_PORT || file->fp == NULL)
270 + return;
271 +
272 + if (!S_ISREG(file->st.st_mode)) {
273 + /*
274 + * For FIFOs, we use PORT_SOURCE_FD as our port event source.
275 + */
276 + if (assoc) {
277 + (void) port_associate(port, PORT_SOURCE_FD,
278 + fileno(file->fp), POLLIN, file);
279 + } else {
280 + (void) port_dissociate(port, PORT_SOURCE_FD,
281 + fileno(file->fp));
282 + }
283 +
284 + return;
285 + }
286 +
287 + bzero(&file->fobj, sizeof (file->fobj));
288 +
289 + /*
290 + * We pull a bit of a stunt here. PORT_SOURCE_FILE only allows us to
291 + * specify a file name -- not a file descriptor. If we were to specify
292 + * the name of the file to port_associate() and that file were moved
293 + * aside, we would not be able to reassociate an event because we would
294 + * not know a name that would resolve to the new file (indeed, there
295 + * might not be such a name -- the file may have been unlinked). But
296 + * there _is_ a name that we know maps to the file and doesn't change:
297 + * the name of the representation of the open file descriptor in /proc.
298 + * We therefore associate with this name (and the underlying file),
299 + * not the name of the file as specified at the command line.
300 + */
301 + (void) snprintf(buf,
302 + sizeof (buf), "/proc/self/fd/%d", fileno(file->fp));
303 +
304 + /*
305 + * Note that portfs uses the address of the specified file_obj_t to
306 + * tag an association; if one creates a different association with a
307 + * (different) file_ob_t that happens to be at the same address,
308 + * the first association will be implicitly removed. To assure that
309 + * each file has a disjoint file_obj_t, we allocate the memory for it
310 + * in the file_info, not on the stack.
311 + */
312 + file->fobj.fo_name = buf;
313 +
314 + if (assoc) {
315 + (void) port_associate(port, PORT_SOURCE_FILE,
316 + (uintptr_t)&file->fobj, FILE_MODIFIED | FILE_TRUNC, file);
317 + } else {
318 + (void) port_dissociate(port, PORT_SOURCE_FILE,
319 + (uintptr_t)&file->fobj);
320 + }
321 +}
322 +
323 +static void
264 324 set_events(file_info_t *files)
265 325 {
266 326 int i;
267 327 file_info_t *file;
268 328
269 329 for (i = 0, file = files; i < no_files; i++, file++) {
270 330 if (! file->fp)
271 331 continue;
272 332
273 333 (void) fstat(fileno(file->fp), &file->st);
334 +
335 + associate(file, B_TRUE);
274 336 }
275 337 }
276 338
277 339 /*
278 340 * follow -- display the file, from an offset, forward.
279 341 *
280 342 */
281 343 void
282 344 follow(file_info_t *files, enum STYLE style, off_t off)
283 345 {
284 346 int active, ev_change, i, n = -1;
285 347 struct stat sb2;
286 348 file_info_t *file;
349 + struct timespec ts;
350 + port_event_t ev;
287 351
288 352 /* Position each of the files */
289 353
290 354 file = files;
291 355 active = 0;
292 356 n = 0;
293 357 for (i = 0; i < no_files; i++, file++) {
294 358 if (file->fp) {
295 359 active = 1;
296 360 n++;
297 361 if (no_files > 1 && !qflag)
298 362 (void) printf("\n==> %s <==\n",
299 363 file->file_name);
↓ open down ↓ |
3 lines elided |
↑ open up ↑ |
300 364 forward(file->fp, file->file_name, style, off,
301 365 &file->st);
302 366 if (Fflag && fileno(file->fp) != STDIN_FILENO)
303 367 n++;
304 368 }
305 369 }
306 370 if (!Fflag && !active)
307 371 return;
308 372
309 373 last = --file;
374 +
375 + if (action == USE_PORT &&
376 + (stat("/proc/self/fd", &sb2) == -1 || !S_ISDIR(sb2.st_mode) ||
377 + (port = port_create()) == -1))
378 + action = USE_SLEEP;
379 +
310 380 set_events(files);
311 381
312 382 for (;;) {
313 383 ev_change = 0;
314 384 if (Fflag) {
315 385 for (i = 0, file = files; i < no_files; i++, file++) {
316 386 if (!file->fp) {
317 387 file->fp = fopen(file->file_name, "r");
318 388 if (file->fp != NULL &&
319 389 fstat(fileno(file->fp), &file->st)
320 390 == -1) {
321 391 (void) fclose(file->fp);
322 392 file->fp = NULL;
323 393 }
324 394 if (file->fp != NULL)
325 395 ev_change++;
326 396 continue;
327 397 }
328 398 if (fileno(file->fp) == STDIN_FILENO)
329 399 continue;
330 400 if (stat(file->file_name, &sb2) == -1) {
331 401 if (errno != ENOENT)
332 402 ierr(file->file_name);
333 403 (void) show(file);
↓ open down ↓ |
14 lines elided |
↑ open up ↑ |
334 404 (void) fclose(file->fp);
335 405 file->fp = NULL;
336 406 ev_change++;
337 407 continue;
338 408 }
339 409
340 410 if (sb2.st_ino != file->st.st_ino ||
341 411 sb2.st_dev != file->st.st_dev ||
342 412 sb2.st_nlink == 0) {
343 413 (void) show(file);
414 + associate(file, B_FALSE);
344 415 file->fp = freopen(file->file_name, "r",
345 416 file->fp);
346 - if (file->fp != NULL)
417 + if (file->fp != NULL) {
347 418 (void) memcpy(&file->st, &sb2,
348 419 sizeof (struct stat));
349 - else if (errno != ENOENT)
420 + } else if (errno != ENOENT)
350 421 ierr(file->file_name);
351 422 ev_change++;
352 423 }
353 424 }
354 425 }
355 426
356 427 for (i = 0, file = files; i < no_files; i++, file++)
357 428 if (file->fp && !show(file))
358 429 ev_change++;
359 430
360 431 if (ev_change)
361 432 set_events(files);
362 433
363 434 switch (action) {
435 + case USE_PORT:
436 + ts.tv_sec = 1;
437 + ts.tv_nsec = 0;
438 +
439 + /*
440 + * In the -F case we set a timeout to ensure that
441 + * we re-stat the file at least once every second.
442 + */
443 + n = port_get(port, &ev, Fflag ? &ts : NULL);
444 +
445 + if (n == 0) {
446 + file = (file_info_t *)ev.portev_user;
447 + associate(file, B_TRUE);
448 +
449 + if (ev.portev_events & FILE_TRUNC)
450 + (void) fseek(file->fp, 0, SEEK_SET);
451 + }
452 +
453 + break;
454 +
364 455 case USE_SLEEP:
365 456 (void) usleep(250000);
366 457 break;
367 458 }
368 459 }
369 460 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX