/*
logtailer: a tool to watch log files.
Copyright 2001, 2002, 2008 Adam Sampson <ats@offog.org>
Please report bugs to <ats@offog.org>.
logtailer is free software; you can redistribute and/or modify it
under the terms of that license as published by the Free Software
Foundation; either version 2 of the License, or (at your option)
any later version.
logtailer is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with logtailer; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA, or see <a href="http://www.gnu.org/.<br />
*/</a></p>
<p>/*" title="http://www.gnu.org/.<br />
*/</p>
<p>/*">http://www.gnu.org/.<br />
*/</p>
<p>/* Interval to poll the logs, in seconds. */
#define TIMEOUT 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
void die(char *fmt, char *arg) {
fprintf(stderr, fmt, arg);
exit(20);
}
struct log {
char *name;
char *label;
ino_t inode;
off_t size;
int fd;
struct log *next;
};
typedef struct log log;
log *first = NULL, *prev = NULL, *this;
/* Print all the text that's available from a log. */
void printlog(log *this) {
#define BUFSIZE 1024
char buf[BUFSIZE];
int len;
static log *lastlog = NULL;
/* If the log is not the last one we printed from,
then print a label. */
if (this != lastlog) {
fprintf(stderr, "<%s>\n", this->label);
lastlog = this;
}
/* Write everything we can to stderr. */
while ((len = read(this->fd, buf, BUFSIZE)) > 0) {
fwrite(buf, sizeof(char), len, stderr);
}
}
/* Open up a log file. */
void openlog(log *this) {
this->fd = open(this->name, O_RDONLY);
if ((this->fd) < 0) die("Unable to open '%s'\n", this->name);
}
/* Add a logfile to the list. */
void addlog(char *label, char *name) {
struct stat st;
if (stat(name, &st) < 0) die("Unable to stat file '%s'\n", name);
if (!S_ISREG(st.st_mode)) die("'%s' is not a file\n", name);
this = (log *)malloc(sizeof(log));
this->label = label;
this->name = name;
this->inode = st.st_ino;
this->size = st.st_size;
openlog(this);
/* Start at the end of the log. */
lseek(this->fd, 0, SEEK_END);
if (!first) first = this;
if (prev) prev->next = this;
prev = this;
}
int main(int argc, char **argv) {
int i;
for (i=1; i<argc;) {
if (!strcmp(argv[i++], "{")) {
int labelpos = i++;
while (strcmp(argv[i], "}")) {
if (i + 1 >= argc) die("Invalid arguments%s\n", "");
addlog(argv[labelpos], argv[i++]);
}
i++;
} else {
if (i >= argc) die("Invalid arguments%s\n", "");
addlog(argv[i - 1], argv[i]);
i++;
}
}
if (i<argc) die("Too many arguments%s\n", "");
if (!first) die("No logs specified%s\n","");
while (1) {
int found = 0;
/* Scan though all the fds. */
for (this = first; this; this = this->next) {
struct stat st;
if (stat(this->name, &st) < 0) {
die("Unable to stat file '%s'\n", this->name);
}
/* Has new content been written? */
if (st.st_size > this->size) {
printlog(this);
this->size = st.st_size;
found = 1;
}
/* Has the file been rotated? */
if (st.st_ino != this->inode || st.st_size < this->size) {
/* Read any remaining contents, then reopen it. */
printlog(this);
close(this->fd);
openlog(this);
this->inode = st.st_ino;
this->size = st.st_size;
found = 1;
}
}
/* If we didn't find anything, wait for a while. */
if (!found) sleep(TIMEOUT);
}
/* Close all the fds. */
for (this = first; this; this = this->next) {
close(this->fd);
}
return 0;
}
</code>