2008-12-19 Rolf Bjarne Kvinge <RKvinge@novell.com>
[mono.git] / mono / io-layer / daemon.c
index f6ffa8ec15f19b2aedf3782e6152f7c789cc8a18..0f5ad38ab882c7136f58c7e02e21ca1e4594c979 100644 (file)
@@ -32,6 +32,7 @@
 #include <mono/io-layer/daemon-messages.h>
 #include <mono/io-layer/timefuncs-private.h>
 #include <mono/io-layer/daemon-private.h>
+#include <mono/io-layer/socket-wrappers.h>
 
 #undef DEBUG
 
 
 /* Keep track of the number of clients */
 static int nfds=0;
-/* Arrays to keep track of handles that have been referenced by the
- * daemon and clients.  client_handles is indexed by file descriptor
+
+/* Arrays to keep track of channel data for the 
+ * daemon and clients indexed by file descriptor
  * value.
  */
-static guint32 *daemon_handles=NULL, **client_handles=NULL;
-static int client_handles_length=0;
+
+typedef struct _channel_data {
+       int io_source; /* the ID given back by g_io_add_watch */
+       guint32 *open_handles; /* array of open handles for this client */
+} ChannelData;
+
+static ChannelData *daemon_channel_data=NULL;
+static ChannelData *channels=NULL;
+static int channels_length=0;
 
 /* The socket which we listen to new connections on */
 static int main_sock;
@@ -53,9 +62,24 @@ static int main_sock;
 /* Set to TRUE by the SIGCHLD signal handler */
 static volatile gboolean check_processes=FALSE;
 
+/* The file_share_hash is used to emulate the windows file sharing mode */
+typedef struct _share_key
+{
+       dev_t device;
+       ino_t inode;
+} ShareKey;
+
+typedef struct _share_data
+{
+       guint32 sharemode;
+       guint32 access;
+} ShareData;
+
+static GHashTable *file_share_hash = NULL;
+
 static gboolean fd_activity (GIOChannel *channel, GIOCondition condition,
                             gpointer data);
-
+static void check_sharing (dev_t device, ino_t inode);
 
 /* Deletes the shared memory segment.  If we're exiting on error,
  * clients will get EPIPEs.
@@ -101,7 +125,7 @@ static void maybe_exit (void)
        for(i=0;
            i<_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT;
            i++) {
-               if(daemon_handles[i]>0) {
+               if(daemon_channel_data->open_handles[i]>0) {
 #ifdef DEBUG
                        g_message (G_GNUC_PRETTY_FUNCTION
                                   ": Still got handle references");
@@ -163,8 +187,11 @@ static void maybe_exit (void)
  *
  * Called if daemon receives a SIGTERM or SIGINT
  */
-static void signal_handler (int unused)
+static void signal_handler (int signo)
 {
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": daemon received signal %d", signo);
+#endif
        cleanup ();
        exit (-1);
 }
@@ -182,10 +209,27 @@ static void sigchld_handler (int unused)
        check_processes=TRUE;
 }
 
+static guint sharedata_hash (gconstpointer key)
+{
+       ShareKey *sharekey = (ShareKey *)key;
+       
+       return(g_int_hash (&(sharekey->inode)));
+}
+
+static gboolean sharedata_equal (gconstpointer a, gconstpointer b)
+{
+       ShareKey *share_a = (ShareKey *)a;
+       ShareKey *share_b = (ShareKey *)b;
+       
+       return(share_a->device == share_b->device &&
+              share_a->inode == share_b->inode);
+}
+
 /*
  * startup:
  *
- * Bind signals and attach to shared memory
+ * Bind signals, attach to shared memory and set up any internal data
+ * structures needed.
  */
 static void startup (void)
 {
@@ -223,18 +267,22 @@ static void startup (void)
                  "mono-handle-daemon-%d-%d-%ld", getuid (), getpid (),
                  time (NULL));
 #endif
+
+       file_share_hash = g_hash_table_new_full (sharedata_hash,
+                                                sharedata_equal, g_free,
+                                                g_free);
 }
 
 
 /*
  * ref_handle:
- * @open_handles: An array of handles referenced by the calling client
+ * @channel_data: Channel data for calling client
  * @handle: handle to inc refcnt
  *
  * Increase ref count of handle for the calling client.  Handle 0 is
  * ignored.
  */
-static void ref_handle (guint32 *open_handles, guint32 handle)
+static void ref_handle (ChannelData *channel_data, guint32 handle)
 {
        guint32 segment, idx;
        
@@ -245,26 +293,26 @@ static void ref_handle (guint32 *open_handles, guint32 handle)
        _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx);
        
        _wapi_shared_data[segment]->handles[idx].ref++;
-       open_handles[handle]++;
+       channel_data->open_handles[handle]++;
        
 #ifdef DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION
                   ": handle 0x%x ref now %d (%d this process)", handle,
                   _wapi_shared_data[segment]->handles[idx].ref,
-                  open_handles[handle]);
+                  channel_data->open_handles[handle]);
 #endif
 }
 
 /*
  * unref_handle:
- * @open_handles: An array of handles referenced by the calling client
+ * @channel_data: Channel data for calling client
  * @handle: handle to inc refcnt
  *
  * Decrease ref count of handle for the calling client. If global ref
  * count reaches 0 it is free'ed. Return TRUE if the local ref count
  * is 0. Handle 0 is ignored.
  */
-static gboolean unref_handle (guint32 *open_handles, guint32 handle)
+static gboolean unref_handle (ChannelData *channel_data, guint32 handle)
 {
        gboolean destroy=FALSE;
        guint32 segment, idx;
@@ -273,7 +321,7 @@ static gboolean unref_handle (guint32 *open_handles, guint32 handle)
                return(FALSE);
        }
        
-       if (open_handles[handle] == 0) {
+       if (channel_data->open_handles[handle] == 0) {
                 g_warning(G_GNUC_PRETTY_FUNCTION
                           ": unref on %d called when ref was already 0", 
                           handle);
@@ -283,29 +331,56 @@ static gboolean unref_handle (guint32 *open_handles, guint32 handle)
        _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx);
        
        _wapi_shared_data[segment]->handles[idx].ref--;
-       open_handles[handle]--;
+       channel_data->open_handles[handle]--;
        
 #ifdef DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION
                   ": handle 0x%x ref now %d (%d this process)", handle,
                   _wapi_shared_data[segment]->handles[idx].ref,
-                  open_handles[handle]);
+                  channel_data->open_handles[handle]);
 #endif
 
-       if(open_handles[handle]==0) {
+       if (_wapi_shared_data[segment]->handles[idx].ref == 0) {
+               gboolean was_file;
+               dev_t device = 0;
+               ino_t inode = 0;
+               
                /* This client has released the handle */
                destroy=TRUE;
-       }
        
-       if(_wapi_shared_data[segment]->handles[idx].ref==0) {
-               if (open_handles[handle]!=0) {
-                       g_warning (G_GNUC_PRETTY_FUNCTION ": per-process open_handles mismatch, set to %d, should be 0", open_handles[handle]);
+               if (channel_data->open_handles[handle]!=0) {
+                       g_warning (G_GNUC_PRETTY_FUNCTION ": per-process open_handles mismatch, set to %d, should be 0", 
+                                       channel_data->open_handles[handle]);
                }
                
 #ifdef DEBUG
                g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle 0x%x",
                           handle);
 #endif
+
+               /* if this was a file handle, save the device and
+                * inode numbers so we can scan the share info data
+                * later to see if the last handle to a file has been
+                * closed, and delete the data if so.
+                */
+               was_file = (_wapi_shared_data[segment]->handles[idx].type == WAPI_HANDLE_FILE);
+               if (was_file) {
+                       struct _WapiHandle_file *file_handle;
+                       gboolean ok;
+                       
+                       ok = _wapi_lookup_handle (GUINT_TO_POINTER (handle),
+                                                 WAPI_HANDLE_FILE,
+                                                 (gpointer *)&file_handle,
+                                                 NULL);
+                       if (ok == FALSE) {
+                               g_warning (G_GNUC_PRETTY_FUNCTION
+                                          ": error looking up file handle %x",
+                                          handle);
+                       } else {
+                               device = file_handle->device;
+                               inode = file_handle->inode;
+                       }
+               }
                
                _wapi_handle_ops_close_shared (GUINT_TO_POINTER (handle));
                
@@ -316,9 +391,13 @@ static gboolean unref_handle (guint32 *open_handles, guint32 handle)
 
                memset (&_wapi_shared_data[segment]->handles[idx].u, '\0', sizeof(_wapi_shared_data[segment]->handles[idx].u));
                _wapi_shared_data[segment]->handles[idx].type=WAPI_HANDLE_UNUSED;
+
+               if (was_file) {
+                       check_sharing (device, inode);
+               }
        }
 
-       if(open_handles==daemon_handles) {
+       if(channel_data == daemon_channel_data) {
                /* The daemon released a reference, so see if it's
                 * ready to exit
                 */
@@ -334,9 +413,10 @@ static gboolean unref_handle (guint32 *open_handles, guint32 handle)
  *
  * Create a new GIOChannel, and add it to the main loop event sources.
  */
-static void add_fd(int fd)
+static void add_fd(int fd, GMainContext *context)
 {
        GIOChannel *io_channel;
+       GSource *source;
        guint32 *refs;
        
        io_channel=g_io_channel_unix_new (fd);
@@ -346,36 +426,42 @@ static void add_fd(int fd)
        g_io_channel_set_buffered (io_channel, FALSE);
        
        refs=g_new0 (guint32,_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT);
-       if(daemon_handles==NULL) {
-               /* We rely on the daemon channel being created first.
-                * That's safe, because every other channel is the
-                * result of an accept() on the daemon channel.
-                */
-               daemon_handles=refs;
-       }
 
-       if(fd>=client_handles_length) {
+       if(fd>=channels_length) {
                /* Add a bit of padding, so we dont resize for _every_
                 * new connection
                 */
-               int old_len=client_handles_length * sizeof(guint32 *);
+               int old_len=channels_length * sizeof(ChannelData);
                
-               client_handles_length=fd+10;
-               if(client_handles==NULL) {
-                       client_handles=g_new0 (guint32 *,
-                                              client_handles_length);
+               channels_length=fd+10;
+               if(channels==NULL) {
+                       channels=g_new0 (ChannelData, channels_length);
+                       /* We rely on the daemon channel being created first.
+                        * That's safe, because every other channel is the
+                        * result of an accept() on the daemon channel.
+                        */
+                       daemon_channel_data = &channels[fd];
                } else {
+                       int daemon_index=daemon_channel_data - channels;
+
                        /* Can't use g_renew here, because the unused
                         * elements must be NULL and g_renew doesn't
                         * initialise the memory it returns
                         */
-                       client_handles=_wapi_g_renew0 (client_handles, old_len, client_handles_length * sizeof(guint32 *));
+                       channels=_wapi_g_renew0 (channels, old_len, channels_length * sizeof(ChannelData));
+                       daemon_channel_data = channels + daemon_index;
                }
+
        }
-       client_handles[fd]=refs;
-       
-       g_io_add_watch (io_channel, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
-                       fd_activity, NULL);
+
+       channels[fd].open_handles=refs;
+
+       source = g_io_create_watch (io_channel, 
+                                   G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL);
+       g_source_set_callback (source, (GSourceFunc)fd_activity, 
+                              context, NULL);
+       channels[fd].io_source=g_source_attach (source, context);
+       g_source_unref (source);
 
        nfds++;
 }
@@ -387,7 +473,7 @@ static void add_fd(int fd)
  * Closes the IO channel. Closes all handles that it may have open. If
  * only main_sock is left, the daemon is shut down.
  */
-static void rem_fd(GIOChannel *channel, guint32 *open_handles)
+static void rem_fd(GIOChannel *channel, ChannelData *channel_data)
 {
        guint32 handle_count;
        int i, j, fd;
@@ -405,12 +491,22 @@ static void rem_fd(GIOChannel *channel, guint32 *open_handles)
        g_message (G_GNUC_PRETTY_FUNCTION ": Removing client fd %d", fd);
 #endif
 
+       if (channel_data->io_source == 0) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": channel already closed for fd %d", fd);
+#endif
+               return;
+       }
+
+
        g_io_channel_shutdown (channel, TRUE, NULL);
+       g_source_remove (channel_data->io_source);
+       g_io_channel_unref (channel);
 
        for(i=0;
            i<_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT;
            i++) {
-               handle_count=open_handles[i];
+               handle_count=channel_data->open_handles[i];
                
                for(j=0; j<handle_count; j++) {
 #ifdef DEBUG
@@ -419,12 +515,13 @@ static void rem_fd(GIOChannel *channel, guint32 *open_handles)
                        /* Ignore the hint to the client to destroy
                         * the handle private data
                         */
-                       unref_handle (open_handles, i);
+                       unref_handle (channel_data, i);
                }
        }
        
-       g_free (open_handles);
-       client_handles[fd]=NULL;
+       g_free (channel_data->open_handles);
+       channel_data->open_handles=NULL;
+       channel_data->io_source=0;
        
        nfds--;
        if(nfds==1) {
@@ -435,11 +532,110 @@ static void rem_fd(GIOChannel *channel, guint32 *open_handles)
        }
 }
 
+static void sharemode_set (dev_t device, ino_t inode, guint32 sharemode,
+                          guint32 access)
+{
+       ShareKey *sharekey;
+       ShareData *sharedata;
+       
+       sharekey = g_new (ShareKey, 1);
+       sharekey->device = device;
+       sharekey->inode = inode;
+
+       sharedata = g_new (ShareData, 1);
+       sharedata->sharemode = sharemode;
+       sharedata->access = access;
+       
+       /* Setting share mode to include all access bits is really
+        * removing the share info
+        */
+       if (sharemode == (FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE)) {
+               g_hash_table_remove (file_share_hash, sharekey);
+       } else {
+               g_hash_table_insert (file_share_hash, sharekey, sharedata);
+       }
+}
+
+static gboolean sharemode_get (dev_t device, ino_t inode, guint32 *sharemode,
+                              guint32 *access)
+{
+       ShareKey sharekey;
+       ShareData *sharedata;
+       
+       sharekey.device = device;
+       sharekey.inode = inode;
+       
+       sharedata = (ShareData *)g_hash_table_lookup (file_share_hash,
+                                                      &sharekey);
+       if (sharedata == NULL) {
+               return(FALSE);
+       }
+       
+       *sharemode = sharedata->sharemode;
+       *access = sharedata->access;
+       
+       return(TRUE);
+}
+
+static gboolean share_compare (gpointer handle, gpointer user_data)
+{
+       struct _WapiHandle_file *file_handle;
+       gboolean ok;
+       ShareKey *sharekey = (ShareKey *)user_data;
+       
+       ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE,
+                                 (gpointer *)&file_handle, NULL);
+       if (ok == FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up file handle %p", handle);
+               return(FALSE);
+       }
+       
+       if (file_handle->device == sharekey->device &&
+           file_handle->inode == sharekey->inode) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": found one, handle %p",
+                          handle);
+#endif
+               return(TRUE);
+       } else {
+               return(FALSE);
+       }
+}
+
+static void check_sharing (dev_t device, ino_t inode)
+{
+       ShareKey sharekey;
+       gpointer file_handle;
+       
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": Checking if anything has (dev 0x%llx, inode %lld) still open", device, inode);
+#endif
+
+       sharekey.device = device;
+       sharekey.inode = inode;
+       
+       file_handle = _wapi_search_handle (WAPI_HANDLE_FILE, share_compare,
+                                          &sharekey, NULL, NULL);
+
+       if (file_handle == NULL) {
+               /* Delete this share info, as the last handle to it
+                * has been closed
+                */
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": Deleting share data for (dev 0x%llx inode %lld)", device, inode);
+#endif
+               
+               g_hash_table_remove (file_share_hash, &sharekey);
+       }
+}
+
 static gboolean process_compare (gpointer handle, gpointer user_data)
 {
        struct _WapiHandle_process *process_handle;
        gboolean ok;
        pid_t pid;
+       guint32 segment, idx;
        
        ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
                                (gpointer *)&process_handle, NULL);
@@ -449,6 +645,11 @@ static gboolean process_compare (gpointer handle, gpointer user_data)
                return(FALSE);
        }
 
+       _wapi_handle_segment (handle, &segment, &idx);
+       if (_wapi_shared_data[segment]->handles[idx].signalled) {
+               return(FALSE);
+       }
+
        pid=GPOINTER_TO_UINT (user_data);
        if(process_handle->id==pid) {
                return(TRUE);
@@ -521,8 +722,14 @@ static void process_post_mortem (pid_t pid, int status)
                                            (gpointer *)&process_handle_data,
                                            NULL);
        if(process_handle==0) {
+#ifdef DEBUG
+               /*
+                * This may happen if we use Process.EnableRaisingEvents +
+                * process.Exited event and the parent has finished.
+                */
                g_warning (G_GNUC_PRETTY_FUNCTION
                           ": Couldn't find handle for process %d!", pid);
+#endif
        } else {
                /* Signal the handle.  Don't use
                 * _wapi_handle_set_signal_state() unless we have
@@ -536,11 +743,14 @@ static void process_post_mortem (pid_t pid, int status)
                           WEXITSTATUS (status));
 #endif
                
-               /* Technically WEXITSTATUS is only valid if the
-                * process exited normally, but I don't care if the
-                * process caught a signal or not.
+               /* If the child terminated due to the receipt of a signal,
+                * the exit status must be based on WTERMSIG, since WEXITSTATUS
+                * returns 0 in this case.
                 */
-               process_handle_data->exitstatus=WEXITSTATUS (status);
+               if (WIFSIGNALED(status))
+                       process_handle_data->exitstatus=128 + WTERMSIG (status);
+               else
+                       process_handle_data->exitstatus=WEXITSTATUS (status);
 
                /* Ignore errors */
                gettimeofday (&tv, NULL);
@@ -571,9 +781,9 @@ static void process_post_mortem (pid_t pid, int status)
        (void)_wapi_search_handle (WAPI_HANDLE_THREAD, process_thread_compare,
                                   process_handle, NULL, NULL);
 
-       unref_handle (daemon_handles,
+       unref_handle (daemon_channel_data,
                      GPOINTER_TO_UINT (process_handle_data->main_thread));
-       unref_handle (daemon_handles, GPOINTER_TO_UINT (process_handle));
+       unref_handle (daemon_channel_data, GPOINTER_TO_UINT (process_handle));
 }
 
 static void process_died (void)
@@ -620,23 +830,11 @@ static void send_reply (GIOChannel *channel, WapiHandleResponse *resp)
        _wapi_daemon_response (g_io_channel_unix_get_fd (channel), resp);
 }
 
-/*
- * process_new:
- * @channel: The client making the request
- * @open_handles: An array of handles referenced by this client
- * @type: type to init handle to
- *
- * Find a free handle and initialize it to 'type', increase refcnt and
- * send back a reply to the client.
- */
-static void process_new (GIOChannel *channel, guint32 *open_handles,
-                        WapiHandleType type)
+static guint32 new_handle_with_shared_check (WapiHandleType type)
 {
-       guint32 handle;
-       WapiHandleResponse resp={0};
-       
-       handle=_wapi_handle_new_internal (type);
-       if(handle==0) {
+       guint32 handle = 0;
+
+       while ((handle = _wapi_handle_new_internal (type)) == 0) {
                /* Try and allocate a new shared segment, and have
                 * another go
                 */
@@ -656,30 +854,44 @@ static void process_new (GIOChannel *channel, guint32 *open_handles,
                         * count arrays
                         */
 
-                       for(i=0; i<client_handles_length; i++) {
-                               if(client_handles[i]!=NULL) {
-                                       client_handles[i]=_wapi_g_renew0 (client_handles[i], old_len, new_len);
+                       for(i=0; i<channels_length; i++) {
+                               if(channels[i].open_handles!=NULL) {
+                                       channels[i].open_handles=_wapi_g_renew0 (channels[i].open_handles, old_len, new_len);
                                }
                        }
-
-                       /* And find the new location for open_handles... */
-                       i=g_io_channel_unix_get_fd (channel);
-                       open_handles=client_handles[i];
-                       daemon_handles=client_handles[main_sock];
-                       
-                       handle=_wapi_handle_new_internal (type);
                } else {
                        /* Map failed.  Just return 0 meaning "out of
                         * handles"
                         */
+                       break;
                }
        }
        
+       return(handle);
+}
+
+/*
+ * process_new:
+ * @channel: The client making the request
+ * @channel_data: Our data for this channel
+ * @type: type to init handle to
+ *
+ * Find a free handle and initialize it to 'type', increase refcnt and
+ * send back a reply to the client.
+ */
+static void process_new (GIOChannel *channel, ChannelData *channel_data,
+                        WapiHandleType type)
+{
+       guint32 handle;
+       WapiHandleResponse resp={0};
+       
+       handle = new_handle_with_shared_check (type);
+       
        /* handle might still be set to 0.  This is handled at the
         * client end
         */
 
-       ref_handle (open_handles, handle);
+       ref_handle (channel_data, handle);
 
 #ifdef DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION ": returning new handle 0x%x",
@@ -696,13 +908,13 @@ static void process_new (GIOChannel *channel, guint32 *open_handles,
 /*
  * process_open:
  * @channel: The client making the request
- * @open_handles: An array of handles referenced by this client
+ * @channel_data: Our data for this channel
  * @handle: handle no.
  *
  * Increase refcnt on a previously created handle and send back a
  * response to the client.
  */
-static void process_open (GIOChannel *channel, guint32 *open_handles,
+static void process_open (GIOChannel *channel, ChannelData *channel_data,
                          guint32 handle)
 {
        WapiHandleResponse resp={0};
@@ -713,7 +925,7 @@ static void process_open (GIOChannel *channel, guint32 *open_handles,
        shared=&_wapi_shared_data[segment]->handles[idx];
                
        if(shared->type!=WAPI_HANDLE_UNUSED && handle!=0) {
-               ref_handle (open_handles, handle);
+               ref_handle (channel_data, handle);
 
 #ifdef DEBUG
                g_message (G_GNUC_PRETTY_FUNCTION
@@ -738,19 +950,19 @@ static void process_open (GIOChannel *channel, guint32 *open_handles,
 /*
  * process_close:
  * @channel: The client making the request
- * @open_handles: An array of handles referenced by this client
+ * @channel_data: Our data for this channel
  * @handle: handle no.
  *
  * Decrease refcnt on a previously created handle and send back a
  * response to the client with notice of it being destroyed.
  */
-static void process_close (GIOChannel *channel, guint32 *open_handles,
+static void process_close (GIOChannel *channel, ChannelData *channel_data,
                           guint32 handle)
 {
        WapiHandleResponse resp={0};
        
        resp.type=WapiHandleResponseType_Close;
-       resp.u.close.destroy=unref_handle (open_handles, handle);
+       resp.u.close.destroy=unref_handle (channel_data, handle);
 
 #ifdef DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION ": unreffing handle 0x%x", handle);
@@ -802,17 +1014,46 @@ static void process_scratch_free (GIOChannel *channel, guint32 scratch_idx)
        send_reply (channel, &resp);
 }
 
+/*
+ * process_process_kill:
+ * @channel: The client making the request
+ * @process_kill: pid and signal to send to the pid.
+ *
+ * Sends the specified signal to the process.
+ */
+static void
+process_process_kill (GIOChannel *channel,
+                     WapiHandleRequest_ProcessKill process_kill)
+{
+       WapiHandleResponse resp = {0};
+
+       resp.type = WapiHandleResponseType_ProcessKill;
+
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": kill (%d, %d)",
+                  process_kill.pid, process_kill.signo);
+#endif
+       if (kill (process_kill.pid, process_kill.signo) == -1) {
+               resp.u.process_kill.err = errno;
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": kill (%d, %d) failed: %d",
+                  process_kill.pid, process_kill.signo, resp.u.process_kill.err);
+#endif
+       }
+
+       send_reply (channel, &resp);
+}
+
 /*
  * process_process_fork:
  * @channel: The client making the request
- * @open_handles: An array of handles referenced by this client
  * @process_fork: Describes the process to fork
  * @fds: stdin, stdout, and stderr for the new process
  *
  * Forks a new process, and returns the process and thread data to the
  * client.
  */
-static void process_process_fork (GIOChannel *channel, guint32 *open_handles,
+static void process_process_fork (GIOChannel *channel, ChannelData *channel_data,
                                  WapiHandleRequest_ProcessFork process_fork,
                                  int *fds)
 {
@@ -820,7 +1061,7 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles,
        guint32 process_handle, thread_handle;
        struct _WapiHandle_process *process_handle_data;
        struct _WapiHandle_thread *thread_handle_data;
-       pid_t pid;
+       pid_t pid = 0;
        
        resp.type=WapiHandleResponseType_ProcessFork;
        
@@ -830,20 +1071,20 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles,
         * client must check if either handle is 0 and take
         * appropriate error handling action.
         */
-       process_handle=_wapi_handle_new_internal (WAPI_HANDLE_PROCESS);
-       ref_handle (daemon_handles, process_handle);
-       ref_handle (open_handles, process_handle);
+       process_handle = new_handle_with_shared_check (WAPI_HANDLE_PROCESS);
+       ref_handle (daemon_channel_data, process_handle);
+       ref_handle (channel_data, process_handle);
        
-       thread_handle=_wapi_handle_new_internal (WAPI_HANDLE_THREAD);
-       ref_handle (daemon_handles, thread_handle);
-       ref_handle (open_handles, thread_handle);
+       thread_handle = new_handle_with_shared_check (WAPI_HANDLE_THREAD);
+       ref_handle (daemon_channel_data, thread_handle);
+       ref_handle (channel_data, thread_handle);
        
        if(process_handle==0 || thread_handle==0) {
                /* unref_handle() copes with the handle being 0 */
-               unref_handle (daemon_handles, process_handle);
-               unref_handle (open_handles, process_handle);
-               unref_handle (daemon_handles, thread_handle);
-               unref_handle (open_handles, thread_handle);
+               unref_handle (daemon_channel_data, process_handle);
+               unref_handle (channel_data, process_handle);
+               unref_handle (daemon_channel_data, thread_handle);
+               unref_handle (channel_data, thread_handle);
                process_handle=0;
                thread_handle=0;
        } else {
@@ -863,6 +1104,16 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles,
                cmd=_wapi_handle_scratch_lookup (process_fork.cmd);
                dir=_wapi_handle_scratch_lookup (process_fork.dir);
                env=_wapi_handle_scratch_lookup_string_array (process_fork.env);
+
+               _wapi_lookup_handle (GUINT_TO_POINTER (process_handle),
+                                    WAPI_HANDLE_PROCESS,
+                                    (gpointer *)&process_handle_data,
+                                    NULL);
+
+               _wapi_lookup_handle (GUINT_TO_POINTER (thread_handle),
+                                    WAPI_HANDLE_THREAD,
+                                    (gpointer *)&thread_handle_data,
+                                    NULL);
                
                ret=g_shell_parse_argv (cmd, NULL, &argv, &gerr);
                if(ret==FALSE) {
@@ -875,16 +1126,6 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles,
                        g_message (G_GNUC_PRETTY_FUNCTION ": forking");
 #endif
 
-                       _wapi_lookup_handle (GUINT_TO_POINTER (process_handle),
-                                            WAPI_HANDLE_PROCESS,
-                                            (gpointer *)&process_handle_data,
-                                            NULL);
-
-                       _wapi_lookup_handle (GUINT_TO_POINTER (thread_handle),
-                                            WAPI_HANDLE_THREAD,
-                                            (gpointer *)&thread_handle_data,
-                                            NULL);
-
                        /* Fork, exec cmd with args and optional env,
                         * and return the handles with pid and blank
                         * thread id
@@ -911,7 +1152,7 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles,
                                }
                                
                                /* Close all file descriptors */
-                               for(i=3; i<getdtablesize (); i++) {
+                               for (i = getdtablesize () - 1; i > 2; i--) {
                                        close (i);
                                }
                        
@@ -927,10 +1168,10 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles,
                                                env_count++;
                                        }
 
-                                       env=(char **)g_renew (char **, env, env_count+2);
+                                       env=(char **)g_renew (char **, env, env_count+3);
                                        
                                        env[env_count]=g_strdup_printf ("_WAPI_PROCESS_HANDLE=%d", process_handle);
-                                       env[env_count+1]=g_strdup_printf ("_WAPI_THREAD_HANDLE=%d", process_handle);
+                                       env[env_count+1]=g_strdup_printf ("_WAPI_THREAD_HANDLE=%d", thread_handle);
                                        env[env_count+2]=NULL;
                                }
 
@@ -973,12 +1214,20 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles,
 
                /* store process name, based on the last section of the cmd */
                {
-                       char *slash=strrchr (argv[0], '/');
+                       char *slash;
+                       
+                       /* This should never fail, but it seems it can...
+                        */
+                       if (argv[0] != NULL) {
+                               slash=strrchr (argv[0], '/');
                        
-                       if(slash!=NULL) {
-                               process_handle_data->proc_name=_wapi_handle_scratch_store (slash+1, strlen (slash+1));
+                               if(slash!=NULL) {
+                                       process_handle_data->proc_name=_wapi_handle_scratch_store (slash+1, strlen (slash+1));
+                               } else {
+                                       process_handle_data->proc_name=_wapi_handle_scratch_store (argv[0], strlen (argv[0]));
+                               }
                        } else {
-                               process_handle_data->proc_name=_wapi_handle_scratch_store (argv[0], strlen (argv[0]));
+                               process_handle_data->proc_name = _wapi_handle_scratch_store (cmd, strlen(cmd));
                        }
                }
                
@@ -1012,22 +1261,90 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles,
 
                resp.u.process_fork.pid=pid;
        }
-                       
+
        resp.u.process_fork.process_handle=process_handle;
        resp.u.process_fork.thread_handle=thread_handle;
 
        send_reply (channel, &resp);
 }
 
+/*
+ * process_set_share:
+ * @channel: The client making the request
+ * @channel_data: The channel data
+ * @set_share: Set share data passed from the client
+ *
+ * Sets file share info
+ */
+static void process_set_share (GIOChannel *channel, ChannelData *channel_data,
+                              WapiHandleRequest_SetShare set_share)
+{
+       WapiHandleResponse resp = {0};
+
+       resp.type = WapiHandleResponseType_SetShare;
+       
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": Setting share for file (dev:0x%llx, ino:%lld) mode 0x%x access 0x%x", set_share.device, set_share.inode, set_share.sharemode, set_share.access);
+#endif
+       
+       sharemode_set (set_share.device, set_share.inode, set_share.sharemode,
+                      set_share.access);
+       
+       send_reply (channel, &resp);
+}
+
+/*
+ * process_get_or_set_share:
+ * @channel: The client making the request
+ * @channel_data: The channel data
+ * @get_share: GetOrSetShare data passed from the client
+ *
+ * Gets a file share status, and sets the status if it doesn't already
+ * exist
+ */
+static void process_get_or_set_share (GIOChannel *channel,
+                                     ChannelData *channel_data,
+                                     WapiHandleRequest_GetOrSetShare get_share)
+{
+       WapiHandleResponse resp = {0};
+       
+       resp.type = WapiHandleResponseType_GetOrSetShare;
+       
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION
+                  ": Getting share status for file (dev:0x%llx, ino:%lld)",
+                  get_share.device, get_share.inode);
+#endif
+
+       resp.u.get_or_set_share.exists = sharemode_get (get_share.device, get_share.inode, &resp.u.get_or_set_share.sharemode, &resp.u.get_or_set_share.access);
+       
+       if (resp.u.get_or_set_share.exists) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": Share mode: 0x%x",
+                          resp.u.get_or_set_share.sharemode);
+#endif
+       } else {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION
+                          ": file share info not already known, setting");
+#endif
+               sharemode_set (get_share.device, get_share.inode,
+                              get_share.new_sharemode, get_share.new_access);
+       }
+       
+       send_reply (channel, &resp);
+}
+
 /*
  * read_message:
  * @channel: The client to read the request from
  * @open_handles: An array of handles referenced by this client
  *
  * Read a message (A WapiHandleRequest) from a client and dispatch
- * whatever it wants to the process_* calls.
+ * whatever it wants to the process_* calls.  Return TRUE if the message
+ * was read successfully, FALSE otherwise.
  */
-static void read_message (GIOChannel *channel, guint32 *open_handles)
+static gboolean read_message (GIOChannel *channel, ChannelData *channel_data)
 {
        WapiHandleRequest req;
        int fds[3]={0, 1, 2};
@@ -1043,26 +1360,28 @@ static void read_message (GIOChannel *channel, guint32 *open_handles)
                g_message ("Read 0 bytes on fd %d, closing it",
                           g_io_channel_unix_get_fd (channel));
 #endif
-
-               rem_fd (channel, open_handles);
-               return;
+               rem_fd (channel, channel_data);
+               return(FALSE);
        }
        
+#ifdef DEBUG
+       g_message ("Process request %d", req.type);
+#endif
        switch(req.type) {
        case WapiHandleRequestType_New:
-               process_new (channel, open_handles, req.u.new.type);
+               process_new (channel, channel_data, req.u.new.type);
                break;
        case WapiHandleRequestType_Open:
 #ifdef DEBUG
                g_assert(req.u.open.handle < _wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT);
 #endif
-               process_open (channel, open_handles, req.u.open.handle);
+               process_open (channel, channel_data, req.u.open.handle);
                break;
        case WapiHandleRequestType_Close:
 #ifdef DEBUG
                g_assert(req.u.close.handle < _wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT);
 #endif
-               process_close (channel, open_handles, req.u.close.handle);
+               process_close (channel, channel_data, req.u.close.handle);
                break;
        case WapiHandleRequestType_Scratch:
                process_scratch (channel, req.u.scratch.length);
@@ -1071,9 +1390,19 @@ static void read_message (GIOChannel *channel, guint32 *open_handles)
                process_scratch_free (channel, req.u.scratch_free.idx);
                break;
        case WapiHandleRequestType_ProcessFork:
-               process_process_fork (channel, open_handles,
+               process_process_fork (channel, channel_data,
                                      req.u.process_fork, fds);
                break;
+       case WapiHandleRequestType_ProcessKill:
+               process_process_kill (channel, req.u.process_kill);
+               break;
+       case WapiHandleRequestType_SetShare:
+               process_set_share (channel, channel_data, req.u.set_share);
+               break;
+       case WapiHandleRequestType_GetOrSetShare:
+               process_get_or_set_share (channel, channel_data,
+                                         req.u.get_or_set_share);
+               break;
        case WapiHandleRequestType_Error:
                /* fall through */
        default:
@@ -1093,6 +1422,8 @@ static void read_message (GIOChannel *channel, guint32 *open_handles)
                close (fds[1]);
                close (fds[2]);
        }
+
+       return(TRUE);
 }
 
 /*
@@ -1108,14 +1439,15 @@ static gboolean fd_activity (GIOChannel *channel, GIOCondition condition,
                             gpointer data)
 {
        int fd=g_io_channel_unix_get_fd (channel);
-       guint32 *open_handles=client_handles[fd];
+       ChannelData *channel_data=&channels[fd];
+       GMainContext *context=data;
        
        if(condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
 #ifdef DEBUG
                g_message ("fd %d error", fd);
 #endif
 
-               rem_fd (channel, open_handles);
+               rem_fd (channel, channel_data);
                return(FALSE);
        }
 
@@ -1137,13 +1469,13 @@ static gboolean fd_activity (GIOChannel *channel, GIOCondition condition,
                        g_message ("accept returning %d", newsock);
 #endif
 
-                       add_fd (newsock);
+                       add_fd (newsock, context);
                } else {
 #ifdef DEBUG
                        g_message ("reading data on fd %d", fd);
 #endif
 
-                       read_message (channel, open_handles);
+                       return(read_message (channel, channel_data));
                }
                return(TRUE);
        }
@@ -1161,6 +1493,7 @@ void _wapi_daemon_main(gpointer data, gpointer scratch)
 {
        struct sockaddr_un main_socket_address;
        int ret;
+       GMainContext *context;
 
 #ifdef DEBUG
        g_message ("Starting up...");
@@ -1173,6 +1506,9 @@ void _wapi_daemon_main(gpointer data, gpointer scratch)
        /* Note that we've got the starting segment already */
        _wapi_shared_data[0]->num_segments=1;
        _wapi_shm_mapped_segments=1;
+
+       _wapi_fd_offset_table_size=getdtablesize ();
+       _wapi_shared_data[0]->fd_offset_table_size = _wapi_fd_offset_table_size;
        
        startup ();
        
@@ -1205,7 +1541,9 @@ void _wapi_daemon_main(gpointer data, gpointer scratch)
        g_message("listening");
 #endif
 
-       add_fd(main_sock);
+       context = g_main_context_new ();
+
+       add_fd(main_sock, context);
 
        /* We're finished setting up, let everyone else know we're
         * ready.  From now on, it's up to us to delete the shared
@@ -1228,7 +1566,6 @@ void _wapi_daemon_main(gpointer data, gpointer scratch)
                 * processes as soon as they die, without burning cpu
                 * time by polling the flag.
                 */
-               g_main_context_iteration (g_main_context_default (), TRUE);
+               g_main_context_iteration (context, TRUE);
        }
 }
-