Basic implementation of file change notification
Mike McCormack
mike at codeweavers.com
Sun Nov 17 15:54:01 CST 2002
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
-------------- next part --------------
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
+ at REQ(next_change_notification)
+ obj_handle_t handle; /* handle to the change notification */
+ at 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;
}
/****************************************************************************
More information about the wine-patches
mailing list