1 /* 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Edward Sze-Tyan Wang. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 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 the Solaris event completion 37 * framework: port_create(), port_associate(), 38 * and port_get(). 39 */ 40 41 #include <sys/param.h> 42 #include <sys/mount.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/statfs.h> 46 #include <sys/statvfs.h> 47 #include <sys/time.h> 48 #include <sys/mman.h> 49 #include <sys/poll.h> 50 #include <port.h> 51 #include <err.h> 52 #include <errno.h> 53 #include <fcntl.h> 54 #include <limits.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 60 #include "extern.h" 61 62 static void rlines(FILE *, const char *fn, off_t, struct stat *); 63 static int show(file_info_t *); 64 static void set_events(file_info_t *files); 65 66 /* defines for inner loop actions */ 67 #define USE_SLEEP 0 68 #define USE_PORT 1 69 #define ADD_EVENTS 2 70 71 int port; 72 int action = USE_SLEEP; 73 port_event_t ev; 74 75 static const file_info_t *last; 76 77 /* 78 * forward -- display the file, from an offset, forward. 79 * 80 * There are eight separate cases for this -- regular and non-regular 81 * files, by bytes or lines and from the beginning or end of the file. 82 * 83 * FBYTES byte offset from the beginning of the file 84 * REG seek 85 * NOREG read, counting bytes 86 * 87 * FLINES line offset from the beginning of the file 88 * REG read, counting lines 89 * NOREG read, counting lines 90 * 91 * RBYTES byte offset from the end of the file 92 * REG seek 93 * NOREG cyclically read characters into a wrap-around buffer 94 * 95 * RLINES 96 * REG mmap the file and step back until reach the correct offset. 97 * NOREG cyclically read lines into a wrap-around array of buffers 98 */ 99 void 100 forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) 101 { 102 int ch; 103 104 switch (style) { 105 case FBYTES: 106 if (off == 0) 107 break; 108 if (S_ISREG(sbp->st_mode)) { 109 if (sbp->st_size < off) 110 off = sbp->st_size; 111 if (fseeko(fp, off, SEEK_SET) == -1) { 112 ierr(fn); 113 return; 114 } 115 } else while (off--) 116 if ((ch = getc(fp)) == EOF) { 117 if (ferror(fp)) { 118 ierr(fn); 119 return; 120 } 121 break; 122 } 123 break; 124 case FLINES: 125 if (off == 0) 126 break; 127 for (;;) { 128 if ((ch = getc(fp)) == EOF) { 129 if (ferror(fp)) { 130 ierr(fn); 131 return; 132 } 133 break; 134 } 135 if (ch == '\n' && !--off) 136 break; 137 } 138 break; 139 case RBYTES: 140 if (S_ISREG(sbp->st_mode)) { 141 if (sbp->st_size >= off && 142 fseeko(fp, -off, SEEK_END) == -1) { 143 ierr(fn); 144 return; 145 } 146 } else if (off == 0) { 147 while (getc(fp) != EOF) 148 ; 149 if (ferror(fp)) { 150 ierr(fn); 151 return; 152 } 153 } else 154 if (bytes(fp, fn, off)) 155 return; 156 break; 157 case RLINES: 158 if (S_ISREG(sbp->st_mode)) 159 if (!off) { 160 if (fseeko(fp, (off_t)0, SEEK_END) == -1) { 161 ierr(fn); 162 return; 163 } 164 } else 165 rlines(fp, fn, off, sbp); 166 else if (off == 0) { 167 while (getc(fp) != EOF) 168 ; 169 if (ferror(fp)) { 170 ierr(fn); 171 return; 172 } 173 } else 174 if (lines(fp, fn, off)) 175 return; 176 break; 177 default: 178 break; 179 } 180 181 while ((ch = getc(fp)) != EOF) 182 if (putchar(ch) == EOF) 183 oerr(); 184 if (ferror(fp)) { 185 ierr(fn); 186 return; 187 } 188 (void) fflush(stdout); 189 } 190 191 /* 192 * rlines -- display the last offset lines of the file. 193 */ 194 static void 195 rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp) 196 { 197 struct mapinfo map; 198 off_t curoff, size; 199 int i; 200 201 if ((size = sbp->st_size) == 0) 202 return; 203 map.start = NULL; 204 map.fd = fileno(fp); 205 map.mapoff = map.maxoff = size; 206 207 /* 208 * Last char is special, ignore whether newline or not. Note that 209 * size == 0 is dealt with above, and size == 1 sets curoff to -1. 210 */ 211 curoff = size - 2; 212 while (curoff >= 0) { 213 if (curoff < map.mapoff && maparound(&map, curoff) != 0) { 214 ierr(fn); 215 return; 216 } 217 for (i = curoff - map.mapoff; i >= 0; i--) 218 if (map.start[i] == '\n' && --off == 0) 219 break; 220 /* `i' is either the map offset of a '\n', or -1. */ 221 curoff = map.mapoff + i; 222 if (i >= 0) 223 break; 224 } 225 curoff++; 226 if (mapprint(&map, curoff, size - curoff) != 0) { 227 ierr(fn); 228 exit(1); 229 } 230 231 /* Set the file pointer to reflect the length displayed. */ 232 if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) { 233 ierr(fn); 234 return; 235 } 236 if (map.start != NULL && munmap(map.start, map.maplen)) { 237 ierr(fn); 238 return; 239 } 240 } 241 242 static int 243 show(file_info_t *file) 244 { 245 int ch; 246 247 while ((ch = getc(file->fp)) != EOF) { 248 if (last != file && no_files > 1) { 249 if (!qflag) 250 (void) printf("\n==> %s <==\n", 251 file->file_name); 252 last = file; 253 } 254 if (putchar(ch) == EOF) 255 oerr(); 256 } 257 (void) fflush(stdout); 258 if (ferror(file->fp)) { 259 (void) fclose(file->fp); 260 file->fp = NULL; 261 ierr(file->file_name); 262 return (0); 263 } 264 clearerr(file->fp); 265 return (1); 266 } 267 268 static void 269 set_events(file_info_t *files) 270 { 271 int i; 272 file_info_t *file; 273 274 action = USE_PORT; 275 for (i = 0, file = files; i < no_files; i++, file++) { 276 if (! file->fp) 277 continue; 278 279 (void) fstat(fileno(file->fp), &file->st); 280 /* For -f or -F will both use Solaris port interface */ 281 if (fflag && (fileno(file->fp) != STDIN_FILENO)) { 282 (void) port_associate(port, PORT_SOURCE_FD, 283 fileno(file->fp), POLLIN, (void*)file); 284 } 285 } 286 } 287 288 /* 289 * follow -- display the file, from an offset, forward. 290 * 291 */ 292 void 293 follow(file_info_t *files, enum STYLE style, off_t off) 294 { 295 int active, ev_change, i, n = -1; 296 struct stat sb2; 297 file_info_t *file; 298 struct timespec ts; 299 300 /* Position each of the files */ 301 302 file = files; 303 active = 0; 304 n = 0; 305 for (i = 0; i < no_files; i++, file++) { 306 if (file->fp) { 307 active = 1; 308 n++; 309 if (no_files > 1 && !qflag) 310 (void) printf("\n==> %s <==\n", 311 file->file_name); 312 forward(file->fp, file->file_name, style, off, 313 &file->st); 314 if (Fflag && fileno(file->fp) != STDIN_FILENO) 315 n++; 316 } 317 } 318 if (!Fflag && !active) 319 return; 320 321 last = --file; 322 port = port_create(); 323 set_events(files); 324 325 for (;;) { 326 ev_change = 0; 327 if (Fflag) { 328 for (i = 0, file = files; i < no_files; i++, file++) { 329 if (!file->fp) { 330 file->fp = fopen(file->file_name, "r"); 331 if (file->fp != NULL && 332 fstat(fileno(file->fp), &file->st) 333 == -1) { 334 (void) fclose(file->fp); 335 file->fp = NULL; 336 } 337 if (file->fp != NULL) 338 ev_change++; 339 continue; 340 } 341 if (fileno(file->fp) == STDIN_FILENO) 342 continue; 343 if (stat(file->file_name, &sb2) == -1) { 344 if (errno != ENOENT) 345 ierr(file->file_name); 346 (void) show(file); 347 (void) fclose(file->fp); 348 file->fp = NULL; 349 ev_change++; 350 continue; 351 } 352 353 if (sb2.st_ino != file->st.st_ino || 354 sb2.st_dev != file->st.st_dev || 355 sb2.st_nlink == 0) { 356 (void) show(file); 357 file->fp = freopen(file->file_name, "r", 358 file->fp); 359 if (file->fp != NULL) 360 (void) memcpy(&file->st, &sb2, 361 sizeof (struct stat)); 362 else if (errno != ENOENT) 363 ierr(file->file_name); 364 ev_change++; 365 } 366 } 367 } 368 369 for (i = 0, file = files; i < no_files; i++, file++) 370 if (file->fp && !show(file)) 371 ev_change++; 372 373 if (ev_change) 374 set_events(files); 375 376 switch (action) { 377 case USE_PORT: 378 ts.tv_sec = 1; 379 ts.tv_nsec = 0; 380 /* 381 * In the -F case we set a timeout to ensure that 382 * we re-stat the file at least once every second. 383 */ 384 n = port_get(port, &ev, Fflag? &ts : NULL); 385 if (n == 0) { 386 file = (file_info_t *)ev.portev_user; 387 (void) port_associate(port, PORT_SOURCE_FD, 388 fileno(file->fp), POLLIN, (void*)file); 389 } 390 break; 391 392 case USE_SLEEP: 393 (void) usleep(250000); 394 break; 395 } 396 } 397 } 398