Hi,
This patch implements file change notification. It contains a fix for portability and some changes to avoid race conditions with signal handlers.
Mike
ChangeLog:
* implement file change notification using F_NOTIFY
Index: server/fd.c =================================================================== RCS file: /cvstrees/crossover/office/wine/server/fd.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 fd.c --- server/fd.c 21 Feb 2003 01:33:09 -0000 1.1.1.1 +++ server/fd.c 7 Mar 2003 01:41:16 -0000 @@ -238,6 +238,13 @@ exit(1); } +void siginfo_handler( int signum, siginfo_t *si, void *x) +{ + /* fprintf(stderr,"signal!\n"); */ + + do_change_notify( si->si_fd ); +} + /* server main poll() loop */ void main_loop(void) { @@ -252,6 +259,7 @@ sigaddset( &sigset, SIGINT ); sigaddset( &sigset, SIGQUIT ); sigaddset( &sigset, SIGTERM ); + sigaddset( &sigset, SIGIO ); sigprocmask( SIG_BLOCK, &sigset, NULL ); /* set the handlers */ @@ -266,6 +274,11 @@ action.sa_handler = sigterm_handler; sigaction( SIGQUIT, &action, NULL ); sigaction( SIGTERM, &action, NULL ); + + action.sa_handler = NULL; + action.sa_sigaction = siginfo_handler; + action.sa_flags = SA_SIGINFO; + sigaction( SIGIO, &action, NULL ); while (active_users) { Index: server/change.c =================================================================== RCS file: /cvstrees/crossover/office/wine/server/change.c,v retrieving revision 1.1.1.4 diff -u -r1.1.1.4 change.c --- server/change.c 21 Feb 2003 01:33:09 -0000 1.1.1.4 +++ server/change.c 7 Mar 2003 01:41:16 -0000 @@ -18,25 +18,40 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" +#include "wine/port.h" + #include <assert.h> #include <stdio.h> #include <stdlib.h> +#include <signal.h> #include "windef.h" #include "handle.h" #include "thread.h" #include "request.h" +#include "file.h" struct change { struct object obj; /* object header */ + int fd; /* the unix file descriptor */ int subtree; /* watch all the subtree */ int filter; /* notification filter */ + char *name; /* name of file */ + int notified; /* got a SIGIO */ + int signaled; /* the file changed */ + struct change *next; + struct change *prev; }; +static struct change *first_change, *last_change; + static void change_dump( struct object *obj, int verbose ); static int change_signaled( struct object *obj, struct thread *thread ); +static int change_satisfied( struct object *obj, struct thread *thread ); +static void change_destroy( struct object *obj ); static const struct object_ops change_ops = { @@ -45,20 +60,188 @@ add_queue, /* add_queue */ remove_queue, /* remove_queue */ change_signaled, /* signaled */ - no_satisfied, /* satisfied */ + change_satisfied, /* satisfied */ no_get_fd, /* get_fd */ - no_destroy /* destroy */ + change_destroy /* destroy */ +}; + +/* the pipe avoids possible race conditions with signal handlers */ +static int change_pipe_get_poll_events( struct fd *fd ); +static void change_pipe_poll_event( struct fd *fd, int event ); + +static const struct fd_ops change_pipe_ops = +{ + change_pipe_get_poll_events, /* get_poll_events */ + change_pipe_poll_event, /* poll_event */ + no_flush, /* flush */ + no_get_file_info, /* get_file_info */ + no_queue_async /* queue_async */ }; +static int change_pipe[2]; +static struct fd *change_notifier; +static int pipe_has_data; + +static int create_change_notifier( void ) +{ + int r; + + if( change_notifier ) + return 0; + + r = pipe( &change_pipe[0] ); + if( r < 0 ) + return r; + change_notifier = alloc_fd( &change_pipe_ops, change_pipe[0], NULL ); + if( !change_notifier ) + return -1; + + set_fd_events( change_notifier, POLLIN ); + + return 0; +} + +static void destroy_change_notifier( void ) +{ + close( change_pipe[1] ); + release_object( (struct object *) change_notifier ); + change_notifier = NULL; +} + +static int change_pipe_get_poll_events( struct fd *fd ) +{ + return POLLIN; +} + +static void change_pipe_poll_event( struct fd *fd, int event ) +{ + struct change *x; + char ch; + int unix_fd = get_unix_fd( fd ); + + read( unix_fd, &ch, sizeof ch); + pipe_has_data = 0; + /* fprintf(stderr,"change: read byte\n"); */ + + /* search for a matching fd */ + for( x=first_change; x; x=x->next ) + { + if( x->notified ) + { + /* fprintf(stderr,"waking up %p\n",x); */ + x->signaled++; + x->notified--; + wake_up( &x->obj, 0 ); + } + } +} + +static void add_change_notification( struct change *change ) +{ + change->prev = NULL; + change->next = first_change; + if ( first_change ) + first_change->prev = change; + else + last_change = change; + first_change = change; +} + +static void remove_change_notification( struct change *change ) +{ + if( change->next ) + change->next->prev = change->prev; + else + last_change = change->prev; + if( change->prev ) + change->prev->next = change->next; + else + first_change = change->next; + change->prev = change->next = NULL; +} + +struct change *get_change_obj( struct process *process, obj_handle_t handle, unsigned int access ) +{ + return (struct change *)get_handle_obj( process, handle, access, &change_ops ); +} + +static void change_destroy( struct object *obj ) +{ + struct change *change = (struct change *)obj; + + remove_change_notification( change ); + free( change->name ); + + if( first_change == NULL ) + destroy_change_notifier(); +} -static struct change *create_change_notification( int subtree, int filter ) +static void adjust_changes( int fd, unsigned int filter ) { +#ifdef F_NOTIFY + unsigned int val; + if ( 0 > fcntl( fd, F_SETSIG, SIGIO) ) + return; + + val = DN_MULTISHOT; + if( filter & FILE_NOTIFY_CHANGE_FILE_NAME ) + val |= DN_RENAME | DN_DELETE | DN_CREATE; + if( filter & FILE_NOTIFY_CHANGE_DIR_NAME ) + val |= DN_RENAME | DN_DELETE | DN_CREATE; + if( filter & FILE_NOTIFY_CHANGE_ATTRIBUTES ) + val |= DN_ATTRIB; + if( filter & FILE_NOTIFY_CHANGE_SIZE ) + val |= DN_MODIFY; + if( filter & FILE_NOTIFY_CHANGE_LAST_WRITE ) + val |= DN_MODIFY; + if( filter & FILE_NOTIFY_CHANGE_SECURITY ) + val |= DN_ATTRIB; + /* fprintf(stderr,"setting F_NOTIFY to %08x for %d (%s)\n", + val,fd,change->name); */ + fcntl( fd, F_NOTIFY, val); +#endif +} + +static struct change *create_change_notification( + const char *name, int len, int subtree, int filter ) + { struct change *change; + char *filename; + int fd; + + /* fprintf(stderr, "Creating change notification\n"); */ + filename = (char *) malloc (len + 1); + memcpy(filename, name, len); + filename[len]=0; + + if (0>create_change_notifier()) + { + set_error( STATUS_ACCESS_DENIED ); + return NULL; + } + + fd = open(filename, O_RDONLY); + if( fd < 0 ) + { + free( filename ); + return NULL; + } + + adjust_changes( fd, filter ); + if ((change = alloc_object( &change_ops ))) { + change->fd = fd; change->subtree = subtree; change->filter = filter; + change->signaled = 0; + change->name = filename; + + add_change_notification( change ); } + + /* fprintf(stderr,"done\n"); */ + return change; } @@ -66,27 +249,89 @@ { struct change *change = (struct change *)obj; assert( obj->ops == &change_ops ); - fprintf( stderr, "Change notification sub=%d filter=%08x\n", + fprintf( stderr, "Change notification sub=%d filter=%08x file:\n", change->subtree, change->filter ); } static int change_signaled( struct object *obj, struct thread *thread ) { -/* struct change *change = (struct change *)obj;*/ + struct change *change = (struct change *)obj; + assert( obj->ops == &change_ops ); + /* fprintf(stderr,"signaled\n"); */ + return change->signaled; +} + +static int change_satisfied( struct object *obj, struct thread *thread ) +{ assert( obj->ops == &change_ops ); - return 0; /* never signaled for now */ + /* fprintf(stderr,"satisfied\n"); */ + return 0; /* not abandoned */ +} + +/* FIXME: this is O(n) ... probably can be improved */ +static struct change * change_from_fd ( int fd ) +{ + struct change *x; + + /* search for a matching fd */ + for( x=first_change; x; x=x->next ) + if( x->fd == fd ) + break; + + return x; +} + +/* enter here from signal handler */ +void do_change_notify( int fd ) +{ + struct change *change; + char ch = 0; /* value is not important */ + + /* fprintf(stderr, "got signal!\n"); */ + + change = change_from_fd( fd ); + if(!change) + return; + + /* fprintf(stderr, "found notification %s (%d)\n", change->name, fd ); */ + + /* wake up threads waiting on this change notification */ + change->notified ++; + + if( pipe_has_data ) + return; + + pipe_has_data = 1; + write( change_pipe[1], &ch, sizeof ch ); + /* fprintf(stderr,"change: wrote byte\n"); */ } /* create a change notification */ DECL_HANDLER(create_change_notification) { struct change *change; + const char *name = get_req_data(); + int len = get_req_data_size(); reply->handle = 0; - if ((change = create_change_notification( req->subtree, req->filter ))) + if ((change = create_change_notification( name, len, req->subtree, req->filter ))) { reply->handle = alloc_handle( current->process, change, STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE, 0 ); release_object( change ); } } + +DECL_HANDLER(next_change_notification) +{ + struct change *change; + + change = get_change_obj( current->process, req->handle, 0 ); + if (!change) + return; + + if( change->signaled > 0 ) + change->signaled --; + release_object( change ); +} + Index: server/protocol.def =================================================================== RCS file: /cvstrees/crossover/office/wine/server/protocol.def,v retrieving revision 1.15 diff -u -r1.15 protocol.def --- server/protocol.def 6 Mar 2003 02:57:11 -0000 1.15 +++ server/protocol.def 7 Mar 2003 01:41:16 -0000 @@ -1061,6 +1061,9 @@ obj_handle_t handle; /* handle to the change notification */ @END +@REQ(next_change_notification) + obj_handle_t handle; /* handle to the change notification */ +@END /* Create a file mapping */ @REQ(create_mapping) Index: server/object.h =================================================================== RCS file: /cvstrees/crossover/office/wine/server/object.h,v retrieving revision 1.1.1.8 diff -u -r1.1.1.8 object.h --- server/object.h 21 Feb 2003 01:33:10 -0000 1.1.1.8 +++ server/object.h 7 Mar 2003 01:41:16 -0000 @@ -119,6 +119,10 @@ extern void abandon_mutexes( struct thread *thread ); +/* change functions */ + +extern void do_change_notify( int fd ); + /* serial functions */ int get_serial_async_timeout(struct object *obj, int type, int count); Index: files/change.c =================================================================== RCS file: /cvstrees/crossover/office/wine/files/change.c,v retrieving revision 1.1.1.4 diff -u -r1.1.1.4 change.c --- files/change.c 29 Aug 2002 20:08:19 -0000 1.1.1.4 +++ files/change.c 7 Mar 2003 01:41:16 -0000 @@ -34,6 +34,9 @@ #include <time.h> #include "winbase.h" #include "winerror.h" +#include "msdos.h" +#include "file.h" +#include "heap.h" #include "wine/server.h" #include "wine/debug.h" @@ -45,17 +48,18 @@ HANDLE WINAPI FindFirstChangeNotificationA( LPCSTR lpPathName, BOOL bWatchSubtree, DWORD dwNotifyFilter ) { - HANDLE ret = INVALID_HANDLE_VALUE; - - FIXME("this is not supported yet (non-trivial).\n"); + HANDLE ret; + LPWSTR path; + DWORD len; + + TRACE("%s %d %08lx\n", debugstr_a(lpPathName), bWatchSubtree, dwNotifyFilter); + + len = MultiByteToWideChar(CP_ACP, 0, lpPathName, -1, NULL, 0); + path = HeapAlloc(GetProcessHeap(), 0, len); + MultiByteToWideChar(CP_ACP, 0, lpPathName, -1, path, len); + ret = FindFirstChangeNotificationW( path, bWatchSubtree, dwNotifyFilter ); + HeapFree(GetProcessHeap(), 0, path); - SERVER_START_REQ( create_change_notification ) - { - req->subtree = bWatchSubtree; - req->filter = dwNotifyFilter; - if (!wine_server_call_err( req )) ret = reply->handle; - } - SERVER_END_REQ; return ret; } @@ -67,13 +71,23 @@ DWORD dwNotifyFilter) { HANDLE ret = INVALID_HANDLE_VALUE; + DOS_FULL_NAME full_name; - FIXME("this is not supported yet (non-trivial).\n"); + TRACE("%s %d %08lx\n",debugstr_w(lpPathName), bWatchSubtree, dwNotifyFilter); + + if (!DOSFS_GetFullName( lpPathName, TRUE, &full_name )) + { + WARN("Unable to get full filename from %s (GLE %ld)\n", + debugstr_w(lpPathName), GetLastError()); + return ret; + } SERVER_START_REQ( create_change_notification ) { req->subtree = bWatchSubtree; req->filter = dwNotifyFilter; + wine_server_add_data( req, full_name.long_name, + strlen(full_name.long_name) ); if (!wine_server_call_err( req )) ret = reply->handle; } SERVER_END_REQ; @@ -85,8 +99,17 @@ */ BOOL WINAPI FindNextChangeNotification( HANDLE handle ) { - /* FIXME: do something */ - return TRUE; + BOOL r; + + TRACE("%p\n",handle); + + SERVER_START_REQ( next_change_notification ) + { + req->handle = handle; + r = wine_server_call_err( req ); + } + SERVER_END_REQ; + return !r; } /****************************************************************************