/* * kqueue_monitor.c * This program demonstrates how to use the kqueue function. * * to compile: gcc -O -pipe kqueue_monitor.c -o kqueue_monitor */ #include #include #include #include #include #include #include #include #include #include /* 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 */ }