/* | observe.c from Lsyncd -- the Live (Mirror) Syncing Demon | | Handles observing file descriptors and the big select. | | License: GPLv2 (see COPYING) or any later version | Authors: Axel Kittenberger */ #include "feature.h" #include #include #include #include #include #define LUA_USE_APICHECK 1 #include #include #include #include "log.h" #include "signal.h" #include "mem.h" #include "main.h" /** * An observance to be called when a file descritor becomes * read-ready or write-ready. */ struct observance { // The file descriptor to observe. int fd; // Function to call when read becomes ready. void (*ready)( lua_State *, int fd, void * extra ); // Function to call when write becomes ready. void (*writey)( lua_State *, int fd, void * extra ); // Function to call to clean up void (*tidy)( int fd, void * extra ); // Extra tokens to pass to the functions. void *extra; }; /* | List of file descriptor watches. */ static struct observance * observances = NULL; static int observances_len = 0; static int observances_size = 0; /* | List of file descriptors to not observe. | | While working for the oberver lists, it may | not be altered, thus nonobserve stores the | delayed removals. */ static int * nonobservances = NULL; static int nonobservances_len = 0; static int nonobservances_size = 0; /* | True while the observances list is being handled. */ static bool observance_action = false; /* | Core watches a filedescriptor to become ready, | one of read_ready or write_ready may be zero */ extern void observe_fd( int fd, void ( * ready ) ( lua_State *, int fd, void * extra ), void ( * writey ) ( lua_State *, int fd, void * extra ), void ( * tidy ) ( int fd, void * extra ), void * extra ) { int pos; // looks if the fd is already there as pos or // stores the position to insert the new fd in pos for( pos = 0; pos < observances_len; pos++) { if( fd <= observances[ pos ].fd ) break; } if( pos < observances_len && observances[ pos ].fd == fd ) { // just updates an existing observance logstring( "Masterloop", "updating fd observance" ); observances[ pos ].ready = ready; observances[ pos ].writey = writey; observances[ pos ].tidy = tidy; observances[ pos ].extra = extra; return; } if( observance_action ) { // FIXME logstring( "Error", "New observances in ready/writey handlers not yet supported" ); exit( -1 ); } if( !tidy ) { logstring( "Error", "internal, tidy( ) in observe_fd() must not be NULL." ); exit( -1 ); } if( observances_len + 1 > observances_size ) { observances_size = observances_len + 1; observances = s_realloc( observances, observances_size * sizeof( struct observance ) ); } memmove( observances + pos + 1, observances + pos, ( observances_len - pos ) * sizeof( struct observance ) ); observances_len++; observances[ pos ].fd = fd; observances[ pos ].ready = ready; observances[ pos ].writey = writey; observances[ pos ].tidy = tidy; observances[ pos ].extra = extra; } /* | Makes the core no longer observe a filedescriptor. */ extern void nonobserve_fd( int fd ) { int pos; if( observance_action ) { // this function is called through a ready/writey handler // while the core works through the observance list, thus // it does not alter the list, but stores this actions // on a stack nonobservances_len++; if( nonobservances_len > nonobservances_size ) { nonobservances_size = nonobservances_len; nonobservances = s_realloc( nonobservances, nonobservances_size * sizeof( int ) ); } nonobservances[ nonobservances_len - 1 ] = fd; return; } // looks for the fd for( pos = 0; pos < observances_len; pos++ ) { if( observances[ pos ].fd == fd ) break; } if( pos >= observances_len ) { logstring( "Error", "internal fail, not observance file descriptor in nonobserve" ); exit( -1 ); } // tidies up the observance observances[ pos ].tidy( observances[ pos ].fd, observances[ pos ].extra ); // and moves the list down memmove( observances + pos, observances + pos + 1, (observances_len - pos) * sizeof( struct observance ) ); observances_len--; } // time for Lsyncd to try to put itself to rest into the big select( ) // this configures: // timeout, // filedescriptors and // signals // that will wake Lsyncd void observe_select ( lua_State * L, struct timespec const * timeout ) { fd_set rfds; fd_set wfds; sigset_t sigset; int pi, pr; // Opens to all signals, FIXME might be a global. sigemptyset( &sigset ); FD_ZERO( &rfds ); FD_ZERO( &wfds ); for( pi = 0; pi < observances_len; pi++ ) { struct observance *obs = observances + pi; if ( obs->ready ) FD_SET( obs->fd, &rfds ); if ( obs->writey ) FD_SET( obs->fd, &wfds ); } if( !observances_len ) { logstring( "Error", "Internal fail, no observances, no monitor!" ); exit( -1 ); } // the great select, this is the very heart beat of Lsyncd // that puts Lsyncd to sleep until anything worth noticing // happens pr = pselect( observances[ observances_len - 1 ].fd + 1, &rfds, &wfds, NULL, timeout, &sigset ); signal_notify( L ); // something happened! if( pr >= 0 ) { // walks through the observances calling ready/writey observance_action = true; for( pi = 0; pi < observances_len; pi++ ) { struct observance *obs = observances + pi; int fd = obs->fd; if( softreset ) break; // a file descriptor became read-ready if( obs->ready && FD_ISSET( fd, &rfds ) ) obs->ready( L, fd, obs->extra ); if( softreset ) break; // FIXME breaks on multiple nonobservances in one beat if( nonobservances_len > 0 && nonobservances[ nonobservances_len - 1 ] == fd ) continue; // a file descriptor became write-ready if( obs->writey && FD_ISSET( fd, &wfds ) ) obs->writey( L, fd, obs->extra ); } observance_action = false; // works through delayed nonobserve_fd() calls for( pi = 0; pi < nonobservances_len; pi++ ) { nonobserve_fd( nonobservances[ pi ] ); } nonobservances_len = 0; } } /* | Tidies up all observances. */ void observe_tidy_all( ) { int i; for( i = 0; i < observances_len; i++ ) { struct observance *obs = observances + i; obs->tidy( obs->fd, obs->extra ); } observances_len = 0; nonobservances_len = 0; }