FindFirstChangeNotification (Was: miranda)

Mike McCormack mike at codeweavers.com
Tue Mar 4 12:48:45 CST 2003


I've already adapted it to the new fd management stuff. The attached 
patch is against the Crossover Wine tree, but it seems to apply cleanly 
to WineHQ's tree. (You'll need to run tools/make_requests after applying).

Which portability fixes need to be made Alexandre?

Mike

Alexandre Julliard wrote:

> It needs some portability work, and I haven't found the time to do
> that. And now of course it will need to be adapted to the new fd
> management.
> 

-------------- next part --------------
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	27 Feb 2003 01:22:18 -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,28 @@
 #include "thread.h"
 #include "request.h"
 
+#ifndef RT_SIGNAL_NOTIFY
+#define RT_SIGNAL_NOTIFY 34
+#endif
+
 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            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 +61,108 @@
     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 */
 };
 
+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 struct change *create_change_notification( int subtree, int filter )
+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 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;
+    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 )))
     {
+        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 +170,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->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/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	27 Feb 2003 01:22:18 -0000
@@ -40,6 +40,12 @@
 #include "request.h"
 #include "console.h"
 
+extern void do_change_notify( int fd );
+
+#ifndef RT_SIGNAL_NOTIFY
+#define RT_SIGNAL_NOTIFY 34
+#endif
+
 struct fd
 {
     struct object        obj;        /* object header */
@@ -238,6 +244,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)
 {
@@ -266,6 +279,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: 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	27 Feb 2003 01:22:18 -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: server/protocol.def
===================================================================
RCS file: /cvstrees/crossover/office/wine/server/protocol.def,v
retrieving revision 1.14
diff -u -r1.14 protocol.def
--- server/protocol.def	21 Feb 2003 05:23:23 -0000	1.14
+++ server/protocol.def	27 Feb 2003 01:22:18 -0000
@@ -1061,6 +1061,9 @@
     obj_handle_t handle;        /* handle to the change notification */
 @END
 
+ at REQ(next_change_notification)
+    obj_handle_t handle;        /* handle to the change notification */
+ at END 
 
 /* Create a file mapping */
 @REQ(create_mapping)
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	27 Feb 2003 01:22:19 -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;
 }
 
 /****************************************************************************


More information about the wine-devel mailing list