This implementation uses F_NOTIFY. It does not distinguish between files and directories, and it does not do subtree notification. Ian Pilcher is planning to add these features later.
Mike
ChangeLog:
* implement file change notification for simple cases
Index: server/change.c =================================================================== RCS file: /cvstrees/crossover/office/wine/server/change.c,v retrieving revision 1.1.1.2 diff -u -r1.1.1.2 change.c --- server/change.c 25 Mar 2002 21:24:39 -0000 1.1.1.2 +++ server/change.c 15 Nov 2002 20:45:02 -0000 @@ -18,6 +18,9 @@ * 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> @@ -28,15 +31,27 @@ #include "thread.h" #include "request.h" +#ifndef RT_SIGNAL_NOTIFY +#define RT_SIGNAL_NOTIFY 34 +#endif + struct change { struct object obj; /* object header */ int subtree; /* watch all the subtree */ int filter; /* notification filter */ + char *name; /* name of file */ + 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,25 +60,112 @@ add_queue, /* add_queue */ remove_queue, /* remove_queue */ change_signaled, /* signaled */ - no_satisfied, /* satisfied */ + change_satisfied, /* satisfied */ NULL, /* get_poll_events */ NULL, /* poll_event */ no_get_fd, /* get_fd */ no_flush, /* flush */ no_get_file_info, /* get_file_info */ NULL, /* queue_async */ - no_destroy /* destroy */ + change_destroy /* destroy */ }; +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 ); +} -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, RT_SIGNAL_NOTIFY) ) + 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; - if ((change = alloc_object( &change_ops, -1 ))) + char *filename; + int fd; + + /* fprintf(stderr, "Creating change notification\n"); */ + filename = (char *) malloc (len + 1); + memcpy(filename, name, len); + filename[len]=0; + + fd = open(filename, O_RDONLY); + if( fd < 0 ) + { + free( filename ); + return NULL; + } + + adjust_changes( fd, filter ); + + if ((change = alloc_object( &change_ops, fd ))) { change->subtree = subtree; change->filter = filter; + change->signaled = 0; + change->name = filename; + + add_change_notification( change ); } + + /* fprintf(stderr,"done\n"); */ + return change; } @@ -71,27 +173,82 @@ { 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 ); - return 0; /* never signaled for now */ + /* fprintf(stderr,"signalled\n"); */ + return change->signaled; +} + +static int change_satisfied( struct object *obj, struct thread *thread ) +{ + assert( obj->ops == &change_ops ); + /* 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->obj.fd == fd ) + break; + + return x; +} + +/* enter here from signal handler */ +void do_change_notify( int fd ) +{ + struct change *change; + + /* 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->signaled ++; + wake_up(&change->obj, 0); } /* 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/object.h =================================================================== RCS file: /cvstrees/crossover/office/wine/server/object.h,v retrieving revision 1.1.1.6 diff -u -r1.1.1.6 object.h --- server/object.h 11 Oct 2002 00:57:59 -0000 1.1.1.6 +++ server/object.h 15 Nov 2002 20:45:02 -0000 @@ -171,6 +171,10 @@ extern struct file *create_temp_file( int access ); extern void file_set_error(void); +/* change functions */ + +extern void do_change_notify( int fd ); + /* serial functions */ int get_serial_async_timeout(struct object *obj, int type, int count); Index: server/protocol.def =================================================================== RCS file: /cvstrees/crossover/office/wine/server/protocol.def,v retrieving revision 1.9 diff -u -r1.9 protocol.def --- server/protocol.def 9 Nov 2002 02:00:30 -0000 1.9 +++ server/protocol.def 15 Nov 2002 20:45:02 -0000 @@ -1051,6 +1051,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/select.c =================================================================== RCS file: /cvstrees/crossover/office/wine/server/select.c,v retrieving revision 1.1.1.5 diff -u -r1.1.1.5 select.c --- server/select.c 29 Aug 2002 20:08:30 -0000 1.1.1.5 +++ server/select.c 15 Nov 2002 20:45:02 -0000 @@ -32,6 +32,7 @@ #include "object.h" #include "thread.h" #include "process.h" +#include "change.h" struct timeout_user { @@ -237,6 +238,12 @@ exit(1); } +void siginfo_handler( int signum, siginfo_t *si, void *x) +{ + /* fprintf(stderr,"signal!\n"); */ + do_change_notify( si->si_fd ); +} + /* server main loop */ void select_loop(void) { @@ -265,6 +272,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( RT_SIGNAL_NOTIFY, &action, NULL ); while (active_users) { 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 15 Nov 2002 20:45:02 -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,15 @@ HANDLE WINAPI FindFirstChangeNotificationA( LPCSTR lpPathName, BOOL bWatchSubtree, DWORD dwNotifyFilter ) { - HANDLE ret = INVALID_HANDLE_VALUE; + HANDLE ret; + LPWSTR path; - FIXME("this is not supported yet (non-trivial).\n"); + TRACE("%s %d %08lx\n", debugstr_a(lpPathName), bWatchSubtree, dwNotifyFilter); + + path = HEAP_strdupAtoW(GetProcessHeap(), 0, lpPathName); + 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 +68,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 +96,17 @@ */ BOOL WINAPI FindNextChangeNotification( HANDLE handle ) { - /* FIXME: do something */ - return TRUE; + BOOL r; + + TRACE("%08x\n",handle); + + SERVER_START_REQ( next_change_notification ) + { + req->handle = handle; + r = wine_server_call_err( req ); + } + SERVER_END_REQ; + return !r; } /****************************************************************************