summaryrefslogtreecommitdiffstats
path: root/tail.c
diff options
context:
space:
mode:
Diffstat (limited to 'tail.c')
-rw-r--r--tail.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/tail.c b/tail.c
new file mode 100644
index 0000000..3bbb621
--- /dev/null
+++ b/tail.c
@@ -0,0 +1,440 @@
+/* $Header: /home/ksheff/src/e2tools/RCS/tail.c,v 0.2 2003/07/12 16:33:11 ksheff Exp $ */
+/*
+ * tail.c
+ *
+ * Copyright (C) 2002 Keith W Sheffield. This file may be redistributed
+ * under the terms of the GNU Public License.
+ *
+ */
+#ifndef TAIL_C
+#define TAIL_C
+#endif
+
+/* Description */
+/* This module implements a basic version of the tail command.
+ *
+ * The user can specify the number of lines to view from the bottom of the
+ * file. This is done by specifying -n #of_lines on the command line. The
+ * default is 5.
+ *
+ * The user can also run in 'follow' mode which prints new lines as they are
+ * appended to the end of the file. This can be specified by the -f option,
+ * which is dependent on the initial inode of the file being tailed or the -F
+ * option which will always use the inode associated with the file name. The
+ * latter can be useful for log files that get switched out.
+ *
+ * The -s #seconds option allows the user to specify the sleep interval while
+ * in follow mode. The default is 1.
+ *
+ */
+/*
+ * $Log: tail.c,v $
+ * Revision 0.2 2003/07/12 16:33:11 ksheff
+ * fixed a bug when no arguments are given.
+ *
+ * Revision 0.1 2002/08/08 08:01:51 ksheff
+ * Initial revision.
+ *
+ */
+
+/* Feature Test Switches */
+/* Headers */
+
+#include "e2tools.h"
+
+
+/* Macros */
+#define USAGE "Usage: e2tail [-n num_lines][-fF][-s sleep_interval] file\n"
+#define BLK_SIZE 4096
+
+#define FOLLOW_INODE 1
+#define FOLLOW_NAME 2
+
+/* Structures and Unions */
+
+/* External Variables */
+
+/* Global Variables */
+
+/* Local Variables */
+
+/* External Prototypes */
+
+/* Local Prototypes */
+long
+do_tail(int argc, char *argv[]);
+static long
+tail(ext2_filsys *fs, ext2_ino_t root, char *input, int num_lines,
+ int follow, int sleep_int, char *cur_filesys);
+
+
+/* Name: do_tail()
+ *
+ * Description:
+ *
+ * This function reads the command line arguments and displays the last lines
+ * at the end of a file in an ext2 file system.
+ *
+ * Algorithm:
+ *
+ * Read any command line switches
+ * Get the file specification for the file we are going to display.
+ * Open the file system read only
+ * Tail the file
+ * Close the file system
+ * return the status of the tail
+ *
+ * Global Variables:
+ *
+ * None
+ *
+ * Arguments:
+ *
+ * int argc; The number of arguments
+ * char *argv[]; The command line arguments
+ *
+ * Return Values:
+ *
+ * 0 - the last number of lines was displayed correctly.
+ * an error occurred.
+ *
+ * Author: Keith W. Sheffield
+ * Date: 08/07/2002
+ *
+ * Modification History:
+ *
+ * MM/DD/YY Name Description
+ * 07/12/03 K.Sheffield fixed a bug when no arguments are given.
+ */
+long
+do_tail(int argc, char *argv[])
+{
+ int verbose=0;
+ int follow=0;
+ int num_lines = 5;
+ int sleep_int = 1;
+ int errcnt=0;
+ char *cur_filesys = NULL;
+ ext2_filsys fs = NULL;
+ ext2_ino_t root;
+ long retval;
+ int curidx;
+ char *tail_dir;
+ int c;
+
+
+#ifdef HAVE_OPTRESET
+ optreset = 1; /* Makes BSD getopt happy */
+#endif
+ while ((c = getopt(argc, argv, "vFfn:s:")) != EOF)
+ {
+ switch (c)
+ {
+ case 'v':
+ verbose = 1;
+ break;
+ case 'f':
+ follow = FOLLOW_INODE;
+ break;
+ case 'F':
+ follow = FOLLOW_NAME;
+ break;
+ case 'n':
+ num_lines = atoi(optarg);
+ break;
+ case 's':
+ sleep_int = atoi(optarg);
+ if (sleep_int < 1)
+ errcnt++;
+ break;
+ default:
+ errcnt++;
+ break;
+ }
+ }
+
+ curidx = optind;
+
+ if (errcnt || argc <= curidx)
+ {
+ fputs(USAGE, stderr);
+ return(1);
+ }
+
+ cur_filesys = argv[curidx++];
+ if (NULL == (tail_dir = strchr(cur_filesys, ':')))
+ {
+ fprintf(stderr, "Invalid file specification: %s\n", cur_filesys);
+ return(1);
+ }
+ *tail_dir++ = '\0';
+
+ if ((retval = open_filesystem(cur_filesys, &fs, &root, 0)))
+ {
+ fprintf(stderr, "%s: %s\n", error_message(retval), cur_filesys);
+ return retval;
+ }
+
+ retval = tail(&fs, root, tail_dir, num_lines, follow, sleep_int,
+ cur_filesys) ? -1 : 0;
+ ext2fs_close(fs);
+ return(retval);
+}
+
+/* Name: tail()
+ *
+ * Description:
+ *
+ * This function displays the last lines at the end of a file in an ext2
+ * file system.
+ *
+ * Algorithm:
+ *
+ * Get the directory and basename of the file
+ * Determine the inode number for the file
+ * Open the file for reading
+ * Skip to the last block in the file
+ * While we have not found the last num_lines of newline characters
+ * Skip backwards in the file one block and read it
+ * Display the contents of the block from that point on.
+ * Display the rest of the file if not contained in the block
+ * Save the current location of the file.
+ * If we are following the file as it grows
+ * While forever
+ * Sleep
+ * Re-read the inode for the file
+ * If the size has changed
+ * Display the file from the saved point on
+ * Save the current location of the file.
+ *
+ *
+ * Global Variables:
+ *
+ * None
+ *
+ * Arguments:
+ *
+ * ext2_filsys *fs; Our filesystem
+ * ext2_ino_t root; The root directory inode number
+ * char *input; The name of the input file to tail
+ * int num_lines; The number of lines to display
+ * int follow; Flag indicating if the we should follow any
+ * new contents to the file.
+ * int sleep_int; The number of seconds to sleep between checking
+ * for new lines
+ * char *cur_filesys
+ *
+ * Return Values:
+ *
+ * 0 - the last number of lines was displayed correctly.
+ * an error occurred.
+ *
+ * Author: Keith W. Sheffield
+ * Date: 08/07/2002
+ *
+ * Modification History:
+ *
+ * MM/DD/YY Name Description
+ */
+static long
+tail(ext2_filsys *fs_ptr, ext2_ino_t root, char *input, int num_lines,
+ int follow, int sleep_int, char *cur_filesys)
+{
+ ext2_filsys fs = *fs_ptr;
+ ext2_ino_t cwd;
+ ext2_ino_t tail_ino;
+ ext2_ino_t t_tail_ino;
+ char *tail_dir;
+ char *tail_name;
+ long retval;
+ char buf[BLK_SIZE];
+ unsigned int bytes_to_read;
+ unsigned int bytes_read;
+ char *ptr;
+ off_t pos;
+ struct ext2_inode inode;
+ ext2_file_t tail_fd;
+ ext2_off_t offset;
+ ext2_off_t cur_pos;
+
+ if (get_file_parts(fs, root, input, &cwd, &tail_dir, &tail_name))
+ {
+ ext2fs_close(fs);
+ return(-1);
+ }
+
+ /* get the inode number for the source file */
+ if ((retval = ext2fs_namei(fs, cwd, cwd, tail_name, &tail_ino)))
+ {
+ fprintf(stderr, "%s: file %s\n",error_message(retval),
+ tail_name);
+ return(retval);
+ }
+
+ /* open the file */
+ if ((retval = ext2fs_file_open(fs, tail_ino, 0, &tail_fd)))
+ {
+ fputs(error_message(retval), stderr);
+ return retval;
+ }
+
+ /* get the length of the file and determine where to start reading */
+ inode.i_size = offset = ext2fs_file_get_size(tail_fd);
+ bytes_to_read = offset % BLK_SIZE;
+ if (bytes_to_read == 0)
+ bytes_to_read = BLK_SIZE;
+
+ offset -= bytes_to_read;
+ if (offset < 0)
+ offset = 0;
+
+ do
+ {
+ /* seek to the start of the last block in the file */
+ if ((retval = ext2fs_file_lseek(tail_fd, offset, EXT2_SEEK_SET, NULL)))
+ {
+ fputs(error_message(retval), stderr);
+ return retval;
+ }
+ /* read the last block in the file */
+ if ((retval = ext2fs_file_read(tail_fd, buf, bytes_to_read,
+ &bytes_read)))
+ {
+ fputs(error_message(retval), stderr);
+ return retval;
+ }
+ if (bytes_to_read != bytes_read)
+ {
+ fputs("error reading file\n", stderr);
+ return(-1);
+ }
+
+ ptr = buf + bytes_read - 1;
+ while (bytes_to_read--)
+ {
+ if (*ptr == '\n' && num_lines-- == 0)
+ {
+ /* if the newline wasn't the last character in the buffer, then
+ * print what's remaining.
+ */
+ if (bytes_to_read != bytes_read - 1)
+ {
+ ptr++;
+ write(1, ptr, bytes_read - bytes_to_read - 1);
+ }
+ offset = 0; /* make sure we break out of the main loop */
+ break;
+ }
+ ptr--;
+ }
+
+ offset -= (offset < BLK_SIZE) ? offset : BLK_SIZE;
+ bytes_to_read = BLK_SIZE;
+ }
+ while (offset > 0);
+
+ /* if we are here and have any lines left, we hit the beginning, so
+ * dump the rest of what's in memory out.
+ */
+
+ if (num_lines > 0)
+ write(1, buf, bytes_read);
+
+ /* retreive the current position in the file */
+ if ((retval = ext2fs_file_lseek(tail_fd, 0, EXT2_SEEK_CUR, &cur_pos)))
+ {
+ fputs(error_message(retval), stderr);
+ return retval;
+ }
+
+ /* ok, if we are before the end of the file, then dump the rest of it */
+ if (cur_pos < inode.i_size)
+ {
+ if ((retval = read_to_eof(tail_fd, 1, cur_pos, &cur_pos)))
+ {
+ return retval;
+ }
+ }
+
+ if ((retval = ext2fs_file_close(tail_fd)))
+ {
+ fputs(error_message(retval), stderr);
+ return retval;
+ }
+
+ if (follow)
+ {
+ while(1)
+ {
+ sleep(sleep_int);
+ /* I don't know how to force a re-read of the file system info yet,
+ * so, just close the file system and reopen it.
+ */
+ ext2fs_close(fs);
+ if ((retval = open_filesystem(cur_filesys, &fs, &root, 0)))
+ {
+ *fs_ptr = NULL;
+ fprintf(stderr, "%s: %s\n", error_message(retval), cur_filesys);
+ return retval;
+ }
+ *fs_ptr = fs;
+
+ /* if we are following the name, find the directory and file name
+ * again.
+ */
+ if (follow == FOLLOW_NAME)
+ {
+ cwd = root;
+
+ if (tail_dir != NULL && *tail_dir != '\0' &&
+ strcmp(tail_dir, ",") != 0 &&
+ (retval = change_cwd(fs, root, &cwd, tail_dir)))
+ {
+ fprintf(stderr, "Error changing to directory %s\n",
+ tail_dir);
+ return(retval);
+ }
+
+ /* get the inode number for the source file */
+ if ((retval = ext2fs_namei(fs, cwd, cwd, tail_name,
+ &t_tail_ino)))
+ {
+ fprintf(stderr, "%s: file %s\n",error_message(retval),
+ tail_name);
+ return(retval);
+ }
+
+ /* if we are dealing with a new file, then start from the
+ * beginning.
+ */
+
+ if (t_tail_ino != tail_ino)
+ {
+ tail_ino = t_tail_ino;
+ cur_pos = 0;
+ }
+ }
+
+ if ((retval = ext2fs_read_inode(fs, tail_ino, &inode)))
+ {
+ fputs(error_message(retval), stderr);
+ return retval;
+ }
+ if (inode.i_size > cur_pos)
+ {
+ if ((retval = retrieve_data(fs, tail_ino, 1, NULL, 0, cur_pos,
+ &cur_pos)))
+ {
+ fputs(error_message(retval), stderr);
+ return retval;
+ }
+ }
+ else if (inode.i_size < cur_pos)
+ {
+ /* the file was truncated, so bail */
+ return(0);
+ }
+ }
+ }
+ return(0);
+}