/*
 * kqueue_monitor.c
 * This program demonstrates how to use the kqueue function.
 *
 * to compile: gcc -O -pipe  kqueue_monitor.c  -o kqueue_monitor
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>


/* our defines */
#define MAX_TIME         (60 * 60 * 2)
#define PAGE_END         ("\n\n----------------------END-OF-PAGE-----------------------\n\n")
#define LETTERS          (" abcdefghijklmnopqrstuvwxyz\n ABCDEFGHIJKLMNOPQRSTUVWXYZ.! \n ")
#define END_LEN          (60)
#define PAGE_LEN         (500 * 4)
#define MODE             (S_IRWXU | S_IRWXG | S_IROTH | S_IWOTH)
#define MAXPROC          (64)
#define MAXBUFF          (1024)
#define TIMEOUT          (1024)
#define NFILES           (4)
#define NSIGNALS         (3) /* SIGHUP, SIGTERM, SIGQUIT */
#define SIGNALS          {SIGHUP, SIGINT, SIGQUIT }
#define FILES            {"LOG_1", "LOG_2", "LOG_3", "LOG_4" }
#define KEVENT_SIZE      (MAXPROC + NFILES + NSIGNALS)
#define KV_TIMEOUT       (0)
#define KV_ERR           (-1)
/* our declerations */
void child_function(int fd);

int main(int argc, char **argv)
{
	int i, fds[NFILES], kq, kindex = 0;
	int signals[NSIGNALS] = SIGNALS;
	pid_t child[MAXPROC];
	char *files[NFILES] = FILES;
	struct kevent kfds[KEVENT_SIZE];
	struct timespec tm;

	/* start the kqueue */
	kq = kqueue();
	/* check to see if we have a valid kqueue */
	if( kq < 0 )
	{
		perror("Cannot create a kqueue !\n");
		exit(1);
	}
	/*
	 * Set up our signals for kqueue
	 */
	for( i = 0; i < NSIGNALS; i++ )
	{
		/* even though we ignore them, we will still get kevents */
		signal(signals[i], SIG_IGN);
		kfds[i].ident  = signals[i];
		kfds[i].flags  = EV_ADD;
		kfds[i].filter = EVFILT_SIGNAL;
	}
	/* keep track of our current index */ 
	kindex = i;
	kindex++;

	/*
	 * open our files first and set our kqueue information
	 */
	for( i = 0; i < NFILES; i++ )
	{
		fds[i] = open(files[i], (O_RDWR | O_CREAT), MODE);
		if( fds[i] < 0 )
		{
			perror("Cannot open file!\n");
			exit(1);
		}
		/* set kqueue structure information, with many fields */
		kfds[kindex].ident  = fds[i];
		kfds[kindex].flags  = EV_ADD;
		kfds[kindex].filter = EVFILT_VNODE;
		kfds[kindex].fflags = (NOTE_EXTEND | NOTE_DELETE | NOTE_ATTRIB);
		kfds[kindex].udata  = (void *)files[i];
		/* advance our kindex */
		kindex++;
	}

	/*
	 * Now fork off our children and set our kqueue structure information again. 
	 */
	for( i = 0; i < MAXPROC; i++ )
	{
		child[i] = fork();
		if( child[i] == 0 )
		{
			/* Child */
			child_function( fds[ i % NFILES] );
			/* if we return then simply exit */
			exit(1);
		}
		/* Parent */
		/* set kqueue structure information */
		kfds[kindex].ident  = child[i];
		kfds[kindex].flags  = EV_ADD;
		kfds[kindex].filter = EVFILT_PROC;
		kfds[kindex].fflags = (NOTE_EXIT | NOTE_FORK );
		/* advance our kindex */
		kindex++;
	}

	/*
	 * So we can use the same array for events and register them.
	 * we first call kevent before the main loop.
	 */
	kindex = kevent(kq, kfds, KEVENT_SIZE, kfds, KEVENT_SIZE, &tm);
	/*
	 * Our main event loop. All we have to do is wait for kqueue to return
	 * with specific information to  
	 * determine what has happend.  
	 */
	while( 1 )
	{
		/* 
		 * Call kevent. When kevent returns there are three possiblities:
		 * I)   (n) events are ready
		 * II)  an error
		 * III) a timeout
		 */
		switch( kindex )
		{
			case KV_TIMEOUT:
				break;

			case KV_ERR:
				break;

			default:
				/* 
				 * Check to see if we have more events than 
				 * we have space for. This is important because
				 * we could have more events than the size of
				 * our array.      
				 */
				printf("We have [%d] events\n", kindex);
				kindex >= KEVENT_SIZE ? KEVENT_SIZE : kindex;
				for( i =0; i < kindex; i++ )
				{
					switch( kfds[i].filter )
					{
						case EVFILT_SIGNAL:
							printf("We recieved the signal [%d]\n", kfds[i].ident);
							exit(1);
							break;
						case EVFILT_PROC:
							printf("We have a child issue, process [%d] has: \n",kfds[i].ident);
							switch( kfds[i].fflags )
							{
								case NOTE_EXIT:
									printf("[ EXITED ]\n");
									break;
								case NOTE_FORK:
									printf("[ FORKED ]\n");

							}
							break;
						case EVFILT_VNODE:
							printf("File [%s] was ", (char *)kfds[i].udata);
							switch( kfds[i].fflags )
							{
								case NOTE_EXTEND:
									printf("[ EXTENDED ]\n");
									break;
								case NOTE_ATTRIB:
									printf("[ ATTRIBUTES ]\n");
									break;
								case NOTE_DELETE:
									printf("[ DELETED ]\n");

							}
							break;
					}
				}
		}

		kindex = kevent(kq, (struct kevent*)NULL, 0, kfds, KEVENT_SIZE, &tm);

	}

	/* FIN */
	return(0);

} /* main */


/*
 * child_function
 * Simular to monkey book, except we don't have to lock, and
 * at a radnom interval we will simply exit. 
 */
void child_function(int fd)
{
	int i, len, c; 
	char buff[PAGE_LEN], *letters = LETTERS;  

	/* seed our random generator */
	srand( (time(NULL) - getpid()) / getpid() );
	len = strlen(letters);
	while( 1 )
	{
		for( i = 0; i < PAGE_LEN; i++ )
		{
			buff[i] = letters[ (rand() % len) ]; 
		}

		/* 
		 * Just for fun, check a random character in the
		 * buffer. If its 'K' then die, if its F then fork.
		 */
		c = buff[rand() % PAGE_LEN];
		switch( c )
		{
			case 'K':
				exit(1);
				break;

			case 'F':
				if( fork() == 0 )
				{
					/* child */
					child_function(fd);
				}
				else
				{
					/* parent */
					exit(0);
				}
				break;

		} /* switch */


		write(fd, buff, PAGE_LEN);
		write(fd, PAGE_END, END_LEN);
		sleep( (rand() % getpid()) * 10 );

	} /* while */

}



syntax highlighted by Code2HTML, v. 0.9