@ -471,6 +471,7 @@ static AST_LIST_HEAD_STATIC(dirlist, direntry);
# if defined(HAVE_INOTIFY)
# if defined(HAVE_INOTIFY)
/* Only one thread is accessing this list, so no lock is necessary */
/* Only one thread is accessing this list, so no lock is necessary */
static AST_LIST_HEAD_NOLOCK_STATIC ( createlist , direntry ) ;
static AST_LIST_HEAD_NOLOCK_STATIC ( createlist , direntry ) ;
static AST_LIST_HEAD_NOLOCK_STATIC ( openlist , direntry ) ;
# endif
# endif
static void queue_file ( const char * filename , time_t when )
static void queue_file ( const char * filename , time_t when )
@ -551,14 +552,47 @@ static void queue_file_create(const char *filename)
return ;
return ;
}
}
strcpy ( cur - > name , filename ) ;
strcpy ( cur - > name , filename ) ;
/* We'll handle this file unless an IN_OPEN event occurs within 2 seconds */
cur - > mtime = time ( NULL ) + 2 ;
AST_LIST_INSERT_TAIL ( & createlist , cur , list ) ;
AST_LIST_INSERT_TAIL ( & createlist , cur , list ) ;
}
}
static void queue_file_open ( const char * filename )
{
struct direntry * cur ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( & createlist , cur , list ) {
if ( ! strcmp ( cur - > name , filename ) ) {
AST_LIST_REMOVE_CURRENT ( list ) ;
AST_LIST_INSERT_TAIL ( & openlist , cur , list ) ;
break ;
}
}
AST_LIST_TRAVERSE_SAFE_END
}
static void queue_created_files ( void )
{
struct direntry * cur ;
time_t now = time ( NULL ) ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( & createlist , cur , list ) {
if ( cur - > mtime > now ) {
break ;
}
AST_LIST_REMOVE_CURRENT ( list ) ;
queue_file ( cur - > name , 0 ) ;
ast_free ( cur ) ;
}
AST_LIST_TRAVERSE_SAFE_END
}
static void queue_file_write ( const char * filename )
static void queue_file_write ( const char * filename )
{
{
struct direntry * cur ;
struct direntry * cur ;
/* Only queue entries where an IN_CREATE preceded the IN_CLOSE_WRITE */
/* Only queue entries where an IN_CREATE preceded the IN_CLOSE_WRITE */
AST_LIST_TRAVERSE_SAFE_BEGIN ( & createlist , cur , list ) {
AST_LIST_TRAVERSE_SAFE_BEGIN ( & open list, cur , list ) {
if ( ! strcmp ( cur - > name , filename ) ) {
if ( ! strcmp ( cur - > name , filename ) ) {
AST_LIST_REMOVE_CURRENT ( list ) ;
AST_LIST_REMOVE_CURRENT ( list ) ;
ast_free ( cur ) ;
ast_free ( cur ) ;
@ -605,7 +639,7 @@ static void *scan_thread(void *unused)
}
}
# ifdef HAVE_INOTIFY
# ifdef HAVE_INOTIFY
inotify_add_watch ( inotify_fd , qdir , IN_CREATE | IN_ CLOSE_WRITE | IN_MOVED_TO ) ;
inotify_add_watch ( inotify_fd , qdir , IN_CREATE | IN_ OPEN | IN_ CLOSE_WRITE | IN_MOVED_TO ) ;
# endif
# endif
/* First, run through the directory and clear existing entries */
/* First, run through the directory and clear existing entries */
@ -641,14 +675,35 @@ static void *scan_thread(void *unused)
/* Convert from seconds to milliseconds, unless there's nothing
/* Convert from seconds to milliseconds, unless there's nothing
* in the queue already , in which case , we wait forever . */
* in the queue already , in which case , we wait forever . */
int waittime = next = = INT_MAX ? - 1 : ( next - now ) * 1000 ;
int waittime = next = = INT_MAX ? - 1 : ( next - now ) * 1000 ;
if ( ! AST_LIST_EMPTY ( & createlist ) ) {
waittime = 1000 ;
}
/* When a file arrives, add it to the queue, in mtime order. */
/* When a file arrives, add it to the queue, in mtime order. */
if ( ( res = poll ( & pfd , 1 , waittime ) ) > 0 & & ( stage = 1 ) & &
if ( ( res = poll ( & pfd , 1 , waittime ) ) > 0 & & ( stage = 1 ) & &
( res = read ( inotify_fd , & buf , sizeof ( buf ) ) ) > = sizeof ( * iev ) ) {
( res = read ( inotify_fd , & buf , sizeof ( buf ) ) ) > = sizeof ( * iev ) ) {
ssize_t len = 0 ;
ssize_t len = 0 ;
/* File(s) added to directory, add them to my list */
/* File(s) added to directory, add them to my list */
for ( iev = ( void * ) buf ; res > = sizeof ( * iev ) ; iev = ( struct inotify_event * ) ( ( ( char * ) iev ) + len ) ) {
for ( iev = ( void * ) buf ; res > = sizeof ( * iev ) ; iev = ( struct inotify_event * ) ( ( ( char * ) iev ) + len ) ) {
/* For an IN_MOVED_TO event, simply process the file. However, if
* we get an IN_CREATE event it * might * be an open ( O_CREAT ) or it
* might be a hardlink ( like smsq does , since rename ( ) might
* overwrite an existing file ) . So we have to see if we get a
* subsequent IN_OPEN event on the same file . If we do , keep it
* on the openlist and wait for the corresponding IN_CLOSE_WRITE .
* If we * don ' t * see an IN_OPEN event , then it was a hard link so
* it can be processed immediately .
*
* Unfortunately , although open ( O_CREAT ) is an atomic file system
* operation , the inotify subsystem doesn ' t give it to us in a
* single event with both IN_CREATE | IN_OPEN set . It ' s two separate
* events , and the kernel doesn ' t even give them to us at the same
* time . We can read ( ) from inotify_fd after the IN_CREATE event ,
* and get * nothing * from it . The IN_OPEN arrives only later ! So
* we have a very short timeout of 2 seconds . */
if ( iev - > mask & IN_CREATE ) {
if ( iev - > mask & IN_CREATE ) {
queue_file_create ( iev - > name ) ;
queue_file_create ( iev - > name ) ;
} else if ( iev - > mask & IN_OPEN ) {
queue_file_open ( iev - > name ) ;
} else if ( iev - > mask & IN_CLOSE_WRITE ) {
} else if ( iev - > mask & IN_CLOSE_WRITE ) {
queue_file_write ( iev - > name ) ;
queue_file_write ( iev - > name ) ;
} else if ( iev - > mask & IN_MOVED_TO ) {
} else if ( iev - > mask & IN_MOVED_TO ) {
@ -679,6 +734,7 @@ static void *scan_thread(void *unused)
time ( & now ) ;
time ( & now ) ;
}
}
queue_created_files ( ) ;
/* Empty the list of all entries ready to be processed */
/* Empty the list of all entries ready to be processed */
AST_LIST_LOCK ( & dirlist ) ;
AST_LIST_LOCK ( & dirlist ) ;
while ( ! AST_LIST_EMPTY ( & dirlist ) & & AST_LIST_FIRST ( & dirlist ) - > mtime < = now ) {
while ( ! AST_LIST_EMPTY ( & dirlist ) & & AST_LIST_FIRST ( & dirlist ) - > mtime < = now ) {