The problem with some of the previous code is that it doesn't cope when the log file is truncated (perhpas by being relegated). The code below is something I wrote to monitor a log file and report on any lines containing one of a number of search words.
You should be able to extract just the logfile monitoring part of the code.
Hope it helps.
/***********************************************************
PROGRAM : intelli_tail
LANGUAGE : C
AUTHOR : Gavin Newman
DATE : 25th October, 2002
PLATFORM : Solaris 2.8
COMPILER : gcc
RAISON D'ETRE
This program will perform a tail operation on the file specified on
command line and will only output lines containing the target words
The file position is stored in a file intelli_tail.filename.lpos
where filename is the file being monitored.
The seach words are stored in lines in the file
intelli_tail.filename.search, each set of words being checked
Example lines are as follows
ready
ready now
The first searches for lines with the word ready on it.
The second searches fir lines with bot words (in any order) in it.
Lines beginning with # are comments
These words are loaded into a memory based linked list for speed,
when the file is modified a SIGHUP sent to this application will
cause the in-memory list to be flushed and rebuilt.
I might improve the pattern matching later on.....
-------------------------------------------------------------------
MAINTENANCE HISTORY
*******************************************************************/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define BUFFLEN 2048
#define TRUE 1
#define FALSE 0
#define SLEEP 5
void show_usage(char * app);
long get_last_fpos(char *);
long get_file_length(char *);
void set_last_fpos(char *);
void sig_shutdown(int);
void sig_reload_patterns(int);
void load_patterns(void);
void unload_patterns(void);
void walk_patterns(void);
int pattern_match(char *);
int write_pid(void);
FILE * fptr_in;
char sz_target[2048];
/* This structure provides a linked list of pointers to
words in memory. Each line in the pattern file has a separate
linked list of these nodes, one per word, which runs from a
pointer in the node structure linked list. */
struct _word_node
{
char * ptr_word; /* Points to memory containing word */
struct _word_node * ptr_next; /* Points to next of these nodes */
};
/* This structure is used in a linked list, one per line in the
search pattern file, it is used to provide a root for each linked
list of word anchor nodes (see above) */
struct _node
{
struct _word_node * ptr_word_node; /* Pts to first in wordnode list */
struct _node * ptr_next; /* Points to next of these */
};
struct _node * ptr_first=NULL; /* Ptr to first node in list */
int main(int argc, char * argv[])
{
long last_pos;
long currpos;
long filelen;
char sz_buffer[BUFFLEN+1];
struct stat statbuff;
/* Check that we were supplied with input */
if(argc!=2)
show_usage(argv[0]);
if(write_pid()==FALSE)
return FALSE;
strcpy(sz_target,argv[1]);
load_patterns();
/* Set signal handling routines */
signal(SIGINT,sig_shutdown);
signal(SIGTERM,sig_shutdown);
signal(SIGHUP,sig_reload_patterns);
/* Get the last place we were up to */
last_pos=get_last_fpos(sz_target);
/* Loop forever (or until we get an external signal) */
while(1)
{
/* Get length of file now */
filelen=get_file_length(sz_target);
/* The file is now shorter than our last read position
restart at the beginning of the file */
if(filelen<last_pos)
{
printf("**** FILE HAS BEEN TRUNCATED. RESTART AT BEGINNING ****\n");
last_pos=0;
}
/* Check for file growth or truncation */
if(filelen==last_pos)
{
/* No addition to the file so sleep for a while */
sleep(SLEEP);
continue;
}
fptr_in=fopen(sz_target,"r");
/* Set file position to last position */
fseek(fptr_in,last_pos,SEEK_SET);
/* Read to EOF */
while(fgets(sz_buffer,BUFFLEN,fptr_in)!=NULL)
{
/* Check for match in search patterns */
if(pattern_match(sz_buffer)==TRUE)
printf("%s",sz_buffer);
}
/* Update file position marker then sleep */
last_pos=ftell(fptr_in);
fclose(fptr_in);
sleep(SLEEP);
}
return 0;
}
/* Just print out information re this program then exit */
void show_usage(char * sz_app)
{
printf("==================================================\n");
printf("USAGE : %s target_file_path\n\n",sz_app);
printf("The program will then extract the latest records\n");
printf("from the target file and output them. In the event\n");
printf("the target file is truncated then all records from\n");
printf("the start of the file will be output\n");
printf("==================================================\n");
exit(1);
}
/* Return the contents of the file intelli_tail.filename.lpos */
/* Return 0 if this file is not found */
long get_last_fpos(char * sz_target)
{
char * ptr_target;
char * ptr;
int hfile;
static long offset;
char sz_lastfile[2048];
/* Build the file name used to hold the last position data */
/* The file is called intelli_tail.filename.lpos */
ptr_target=(char *)malloc(strlen(sz_target)+1);
strcpy(ptr_target,sz_target);
if((ptr=strrchr(ptr_target,'/'))!=NULL)
*(ptr++)=0x00;
else
ptr=ptr_target;
sprintf(sz_lastfile,"intelli_tail.%s.lpos",ptr);
free(ptr_target);
if((hfile=open(sz_lastfile,O_RDONLY))==0)
{
hfile=open(sz_lastfile,O_WRONLY|O_CREAT|O_TRUNC,0666);
offset=0;
}
else
read(hfile,&offset,sizeof(long));
close(hfile);
return offset;
}
long get_file_length(char * sz_target)
{
long currpos;
long endpos;
struct stat statbuff;
if(stat(sz_target,&statbuff)==-1)
return 0;
else
return statbuff.st_size;
}
/* Write the current file position to our lpos file so we
know where we are up to next time we run. */
void set_last_fpos(char * sz_target)
{
char * ptr_target;
char * ptr;
int hfile;
long offset;
char sz_lastfile[2048];
/* Build the file name used to hold the last position data */
/* The file is called intelli_tail.filename.lpos */
ptr_target=(char *)malloc(strlen(sz_target)+1);
strcpy(ptr_target,sz_target);
if((ptr=strrchr(ptr_target,'/'))!=NULL)
*(ptr++)=0x00;
else
ptr=ptr_target;
sprintf(sz_lastfile,"intelli_tail.%s.lpos",ptr);
free(ptr_target);
hfile=open(sz_lastfile,O_WRONLY|O_CREAT|O_TRUNC,0666);
offset=get_file_length(sz_target);
write(hfile,&offset,sizeof(long));
close(hfile);
return;
}
/* This function is called as a result of a signal, it allows
the program to close gracefully. */
void sig_shutdown(int signum)
{
printf("Exiting on signal %d \n",signum);
unload_patterns();
set_last_fpos(sz_target);
fclose(fptr_in);
unlink("intelli_tail.pid");
exit(0);
}
/* Reload the match word list in memory */
void sig_reload_patterns(int signum)
{
printf("Configuration reload requested - signal %d\n",signum);
unload_patterns();
load_patterns();
signal(SIGHUP,sig_reload_patterns);
}
/* Load contents of search pattern file into memory based linked
lists */
void load_patterns()
{
char * ptr_target;
char * ptr;
char sz_patterns[2048];
char sz_buffer[2048];
FILE * fptr;
char sz_delimiter[]=" ";
struct _node * ptr_current_node;
struct _word_node * ptr_current_word_node;
char * ptr_word;
/* Build the file name
The file is called intelli_tail.filename.search */
ptr_target=(char *)malloc(strlen(sz_target)+1);
strcpy(ptr_target,sz_target);
if((ptr=strrchr(ptr_target,'/'))!=NULL)
*(ptr++)=0x00;
else
ptr=ptr_target;
sprintf(sz_patterns,"intelli_tail.%s.search",ptr);
free(ptr_target);
if((fptr=fopen(sz_patterns,"r"))==NULL)
{
printf("Could not open %s - patterns not loaded\n",sz_patterns);
exit(1);
}
while(fgets(sz_buffer,2047,fptr)!=NULL)
{
/* Ignore comments */
if(sz_buffer[0]=='#')
continue;
/* Strip trailing newline */
if((ptr=strchr(sz_buffer,'\n'))!=NULL)
*ptr=0x00;
/* Check for runt line */
if(strlen(sz_buffer)==0)
break;
/* Allocate NODE structure */
ptr_current_node=malloc(sizeof(struct _node));
ptr_current_node->ptr_next=ptr_first;
ptr_first=ptr_current_node;
ptr_current_node->ptr_word_node=NULL;
ptr=strtok(sz_buffer,sz_delimiter);
while(ptr!=NULL)
{
ptr_current_word_node=malloc(sizeof(struct _word_node));
ptr_current_word_node->ptr_next=ptr_current_node->ptr_word_node;
ptr_current_node->ptr_word_node=ptr_current_word_node;
ptr_current_word_node->ptr_word=malloc(strlen(ptr)+1);
strcpy(ptr_current_word_node->ptr_word,ptr);
ptr=strtok(NULL,sz_delimiter);
}
}
fclose(fptr);
}
/* Unload linked lists from memory */
void unload_patterns()
{
struct _node * ptr_temp_node;
struct _word_node * ptr_temp_word_node;
struct _node * ptr_current_node;
struct _word_node * ptr_current_word_node;
if(ptr_first==NULL)
return;
/* Walk primary nodes */
ptr_current_node=ptr_first;
while(ptr_current_node!=NULL)
{
/* Walk word nodes */
ptr_current_word_node=ptr_current_node->ptr_word_node;
while(ptr_current_word_node!=NULL)
{
free(ptr_current_word_node->ptr_word);
ptr_temp_word_node=ptr_current_word_node->ptr_next;
free(ptr_current_word_node);
ptr_current_word_node=ptr_temp_word_node;
}
ptr_temp_node=ptr_current_node->ptr_next;
free(ptr_current_node);
ptr_current_node=ptr_temp_node;
}
ptr_first=NULL;
}
/* Not used at present */
void walk_patterns()
{
struct _node * ptr_current_node;
struct _word_node * ptr_current_word_node;
if(ptr_first==NULL)
return;
/* Walk primary nodes */
ptr_current_node=ptr_first;
while(ptr_current_node!=NULL)
{
printf("At node %X\n",ptr_current_node);
printf(" Word node = %X\n",ptr_current_node->ptr_word_node);
printf(" Next node = %X\n",ptr_current_node->ptr_next);
/* Walk word nodes */
ptr_current_word_node=ptr_current_node->ptr_word_node;
while(ptr_current_word_node!=NULL)
{
printf("At word node %X\n",ptr_current_word_node);
printf(" Word = %X",ptr_current_word_node->ptr_word);
printf(" %s\n",ptr_current_word_node->ptr_word);
printf(" Next = %X\n",ptr_current_word_node->ptr_next);
ptr_current_word_node=ptr_current_word_node->ptr_next;
}
ptr_current_node=ptr_current_node->ptr_next;
}
}
/* Look for matches between this string and the contents of
the pattern tree. Returns TRUE is matched, FALSE if not */
int pattern_match(char * sz_text)
{
struct _node * ptr_current_node;
struct _word_node * ptr_current_word_node;
int matched=TRUE;
if(ptr_first==NULL)
return TRUE;
/* Walk primary nodes */
ptr_current_node=ptr_first;
while(ptr_current_node!=NULL)
{
/* For each major node we reset this to TRUE
We then go through each word for this node, if
we have a no-match we set it to FALSE. At the
end of each major node if the field is set to
TRUE then all words were matched! */
matched=TRUE;
/* Walk word nodes */
ptr_current_word_node=ptr_current_node->ptr_word_node;
while(ptr_current_word_node!=NULL)
{
if(strstr(sz_text,ptr_current_word_node->ptr_word)==NULL)
matched=FALSE;
ptr_current_word_node=ptr_current_word_node->ptr_next;
}
if(matched==TRUE)
return TRUE;
ptr_current_node=ptr_current_node->ptr_next;
}
return FALSE;
}
int write_pid()
{
FILE * fptr;
char sz_pid[10];
if((fptr=fopen("intelli_tail.pid","r"))!=NULL)
{
fgets(sz_pid,9,fptr);
printf("intelli_file is already running or has an\n");
printf("orphan intelli_file.pid file left over\n");
printf("Refer to PID=%s\n",sz_pid);
fclose(fptr);
return FALSE;
}
if((fptr=fopen("intelli_tail.pid","w"))==NULL)
return FALSE;
fprintf(fptr,"%d",getpid());
fclose(fptr);
return TRUE;
}