X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fio-layer%2Fdaemon.c;h=0f5ad38ab882c7136f58c7e02e21ca1e4594c979;hb=3c666ed0337fc5bbbbb5ec6322826573d5509297;hp=4a5a01f73723dc9f924c2c59bc7bbb86d94d242e;hpb=692505b29a582fcfd2c8a27927f40e6f4a0ec25f;p=mono.git diff --git a/mono/io-layer/daemon.c b/mono/io-layer/daemon.c index 4a5a01f7372..0f5ad38ab88 100644 --- a/mono/io-layer/daemon.c +++ b/mono/io-layer/daemon.c @@ -14,12 +14,17 @@ #include #include #include -#include #include #include #include #include #include +#include +#include + +#ifdef HAVE_POLL +#include +#endif #include #include @@ -27,6 +32,7 @@ #include #include #include +#include #undef DEBUG @@ -35,29 +41,63 @@ /* Keep track of the number of clients */ static int nfds=0; -/* Array to keep track of handles that have been referenced by the - * daemon. + +/* Arrays to keep track of channel data for the + * daemon and clients indexed by file descriptor + * value. */ -static guint32 *daemon_handles=NULL; + +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; /* 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. */ static void cleanup (void) { + int i; + #ifdef NEED_LINK_UNLINK - unlink(_wapi_shared_data->daemon); + unlink(_wapi_shared_data[0]->daemon); #endif - _wapi_shm_destroy (); + for(i=1; i<_wapi_shared_data[0]->num_segments; i++) { + unlink (_wapi_shm_file (WAPI_SHM_DATA, i)); + } + unlink (_wapi_shm_file (WAPI_SHM_DATA, 0)); + + /* There's only one scratch file */ + unlink (_wapi_shm_file (WAPI_SHM_SCRATCH, 0)); } /* If there is only one socket, and no child processes, we can exit. @@ -79,20 +119,64 @@ static void maybe_exit (void) return; } - for(i=0; i<_WAPI_MAX_HANDLES; i++) { - if(daemon_handles[i]>0) { + /* Prevent new clients from connecting... */ + _wapi_shared_data[0]->daemon_running=DAEMON_CLOSING; + + for(i=0; + i<_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT; + i++) { + if(daemon_channel_data->open_handles[i]>0) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Still got handle references"); #endif + + _wapi_shared_data[0]->daemon_running=DAEMON_RUNNING; return; } } +#ifdef HAVE_POLL + /* Last check, make sure no client came along while we were + * checking the handle lists. + * + * Use poll() directly here, as glib doesn't seem to have any + * exposed way of seeing if a file descriptor is ready + * (g_io_channel_get_buffer_condition() isn't it.) + * + * Crappy systems that don't have poll() will just have to + * lump it (for them there is still the very slight chance + * that someone tried to connect just as the DAEMON_CLOSING + * flag was being set.) + */ + { + struct pollfd fds[1]; + + fds[0].fd=main_sock; + fds[0].events=POLLIN; + fds[0].revents=0; + #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Byebye"); + g_message (G_GNUC_PRETTY_FUNCTION ": Last connect check"); +#endif + + if(poll (fds, 1, 0)>0) { + /* Someone did connect, so carry on running */ +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": Someone connected"); #endif + _wapi_shared_data[0]->daemon_running=DAEMON_RUNNING; + return; + } + } +#endif + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Byebye"); +#endif + cleanup (); exit (0); } @@ -103,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); } @@ -122,16 +209,31 @@ 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) { struct sigaction sa; - gboolean success; - int shm_id; sa.sa_handler=signal_handler; sigemptyset (&sa.sa_mask); @@ -148,15 +250,9 @@ static void startup (void) sa.sa_flags=SA_NOCLDSTOP; sigaction (SIGCHLD, &sa, NULL); - _wapi_shared_data=_wapi_shm_attach (TRUE, &success, &shm_id); - if(success==FALSE) { - g_error ("Failed to attach shared memory! (tried shared memory ID 0x%x)", shm_id); - exit (-1); - } - #ifdef NEED_LINK_UNLINK /* Here's a more portable method... */ - snprintf (_wapi_shared_data->daemon, MONO_SIZEOF_SUNPATH-1, + snprintf (_wapi_shared_data[0]->daemon, MONO_SIZEOF_SUNPATH-1, "/tmp/mono-handle-daemon-%d-%ld-%ld", getuid (), random (), time (NULL)); #else @@ -167,104 +263,141 @@ static void startup (void) * The name is intended to be unique, not cryptographically * secure... */ - snprintf (_wapi_shared_data->daemon+1, MONO_SIZEOF_SUNPATH-2, + snprintf (_wapi_shared_data[0]->daemon+1, MONO_SIZEOF_SUNPATH-2, "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; + if(handle==0) { return; } - _wapi_shared_data->handles[handle].ref++; - open_handles[handle]++; + _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx); + + _wapi_shared_data[segment]->handles[idx].ref++; + 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->handles[handle].ref, - open_handles[handle]); + _wapi_shared_data[segment]->handles[idx].ref, + 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 - * @daemon_initiated: set to %TRUE if the daemon is unreffing a - * handle. Controls whether or not to set the handle type to UNUSED. - * This must not happen here if a client process is unreffing a - * handle, because it has more cleaning up to do and must still be - * able to locate the handle and specify the type. * * 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, - gboolean daemon_initiated) +static gboolean unref_handle (ChannelData *channel_data, guint32 handle) { gboolean destroy=FALSE; + guint32 segment, idx; if(handle==0) { 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); return TRUE; } - _wapi_shared_data->handles[handle].ref--; - open_handles[handle]--; + _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx); + + _wapi_shared_data[segment]->handles[idx].ref--; + 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->handles[handle].ref, - open_handles[handle]); + _wapi_shared_data[segment]->handles[idx].ref, + 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->handles[handle].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)); - mono_mutex_destroy (&_wapi_shared_data->handles[handle].signal_mutex); - pthread_cond_destroy (&_wapi_shared_data->handles[handle].signal_cond); - memset (&_wapi_shared_data->handles[handle].u, '\0', sizeof(_wapi_shared_data->handles[handle].u)); - if(daemon_initiated) { - _wapi_shared_data->handles[handle].type=WAPI_HANDLE_UNUSED; +#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1 + mono_mutex_destroy (&_wapi_shared_data[segment]->handles[idx].signal_mutex); + pthread_cond_destroy (&_wapi_shared_data[segment]->handles[idx].signal_cond); +#endif + + 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 */ @@ -280,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); @@ -291,17 +425,43 @@ static void add_fd(int fd) g_io_channel_set_encoding (io_channel, NULL, NULL); g_io_channel_set_buffered (io_channel, FALSE); - refs=g_new0 (guint32, _WAPI_MAX_HANDLES); - 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. + refs=g_new0 (guint32,_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT); + + if(fd>=channels_length) { + /* Add a bit of padding, so we dont resize for _every_ + * new connection */ - daemon_handles=refs; + int old_len=channels_length * sizeof(ChannelData); + + 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 + */ + channels=_wapi_g_renew0 (channels, old_len, channels_length * sizeof(ChannelData)); + daemon_channel_data = channels + daemon_index; + } + } - - g_io_add_watch (io_channel, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, - fd_activity, refs); + + 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++; } @@ -313,12 +473,14 @@ 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; + int i, j, fd; + + fd=g_io_channel_unix_get_fd (channel); - if(g_io_channel_unix_get_fd (channel) == main_sock) { + if(fd == main_sock) { /* We shouldn't be deleting the daemon's fd */ g_warning (G_GNUC_PRETTY_FUNCTION ": Deleting daemon fd!"); cleanup (); @@ -326,14 +488,25 @@ static void rem_fd(GIOChannel *channel, guint32 *open_handles) } #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Removing client fd %d", - g_io_channel_unix_get_fd (channel)); + 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_MAX_HANDLES; i++) { - handle_count=open_handles[i]; + for(i=0; + i<_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT; + i++) { + handle_count=channel_data->open_handles[i]; for(j=0; jopen_handles); + channel_data->open_handles=NULL; + channel_data->io_source=0; nfds--; if(nfds==1) { @@ -357,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); @@ -371,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); @@ -383,6 +662,7 @@ static gboolean process_thread_compare (gpointer handle, gpointer user_data) { struct _WapiHandle_thread *thread_handle; gboolean ok; + guint32 segment, idx; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, (gpointer *)&thread_handle, NULL); @@ -403,7 +683,7 @@ static gboolean process_thread_compare (gpointer handle, gpointer user_data) thread_handle->exitstatus=0; -#ifdef _POSIX_THREAD_PROCESS_SHARED +#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1 _wapi_handle_lock_handle (handle); _wapi_handle_set_signal_state (handle, TRUE, TRUE); _wapi_handle_unlock_handle (handle); @@ -415,7 +695,8 @@ static gboolean process_thread_compare (gpointer handle, gpointer user_data) * to cope with missing condition signals for when * process-shared pthread support is missing. */ - _wapi_shared_data->handles[GPOINTER_TO_UINT (handle)].signalled=TRUE; + _wapi_handle_segment (handle, &segment, &idx); + _wapi_shared_data[segment]->handles[idx].signalled=TRUE; #endif /* _POSIX_THREAD_PROCESS_SHARED */ } @@ -433,6 +714,7 @@ static void process_post_mortem (pid_t pid, int status) { gpointer process_handle; struct _WapiHandle_process *process_handle_data; + guint32 segment, idx; process_handle=_wapi_search_handle (WAPI_HANDLE_PROCESS, process_compare, @@ -440,28 +722,42 @@ 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 * process-shared pthread support. */ + struct timeval tv; + #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Set process %d exitstatus to %d", pid, 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); - _wapi_time_t_to_filetime (time (NULL), - &process_handle_data->exit_time); + if (WIFSIGNALED(status)) + process_handle_data->exitstatus=128 + WTERMSIG (status); + else + process_handle_data->exitstatus=WEXITSTATUS (status); -#ifdef _POSIX_THREAD_PROCESS_SHARED + /* Ignore errors */ + gettimeofday (&tv, NULL); + _wapi_timeval_to_filetime (&tv, + &process_handle_data->exit_time); + +#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1 _wapi_handle_lock_handle (process_handle); _wapi_handle_set_signal_state (process_handle, TRUE, TRUE); _wapi_handle_unlock_handle (process_handle); @@ -473,7 +769,8 @@ static void process_post_mortem (pid_t pid, int status) * to cope with missing condition signals for when * process-shared pthread support is missing. */ - _wapi_shared_data->handles[GPOINTER_TO_UINT (process_handle)].signalled=TRUE; + _wapi_handle_segment (process_handle, &segment, &idx); + _wapi_shared_data[segment]->handles[idx].signalled=TRUE; #endif /* _POSIX_THREAD_PROCESS_SHARED */ } @@ -484,11 +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, - GPOINTER_TO_UINT (process_handle_data->main_thread), - TRUE); - unref_handle (daemon_handles, GPOINTER_TO_UINT (process_handle), - TRUE); + unref_handle (daemon_channel_data, + GPOINTER_TO_UINT (process_handle_data->main_thread)); + unref_handle (daemon_channel_data, GPOINTER_TO_UINT (process_handle)); } static void process_died (void) @@ -535,24 +830,68 @@ static void send_reply (GIOChannel *channel, WapiHandleResponse *resp) _wapi_daemon_response (g_io_channel_unix_get_fd (channel), resp); } +static guint32 new_handle_with_shared_check (WapiHandleType type) +{ + guint32 handle = 0; + + while ((handle = _wapi_handle_new_internal (type)) == 0) { + /* Try and allocate a new shared segment, and have + * another go + */ + guint32 segment=_wapi_shared_data[0]->num_segments; + int i; + + _wapi_handle_ensure_mapped (segment); + if(_wapi_shared_data[segment]!=NULL) { + /* Got a new segment */ + gulong old_len, new_len; + + old_len=_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT * sizeof(guint32); + _wapi_shared_data[0]->num_segments++; + new_len=_wapi_shared_data[0]->num_segments * _WAPI_HANDLES_PER_SEGMENT * sizeof(guint32); + + /* Need to expand all the handle reference + * count arrays + */ + + for(i=0; ihandles[handle]; + WapiHandleResponse resp={0}; + guint32 segment, idx; + struct _WapiHandleShared *shared; + + _wapi_handle_segment (GUINT_TO_POINTER (handle), &segment, &idx); + 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 @@ -607,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; + WapiHandleResponse resp={0}; resp.type=WapiHandleResponseType_Close; - resp.u.close.destroy=unref_handle (open_handles, handle, FALSE); + resp.u.close.destroy=unref_handle (channel_data, handle); #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": unreffing handle 0x%x", handle); @@ -637,11 +980,10 @@ static void process_close (GIOChannel *channel, guint32 *open_handles, */ static void process_scratch (GIOChannel *channel, guint32 length) { - WapiHandleResponse resp; + WapiHandleResponse resp={0}; resp.type=WapiHandleResponseType_Scratch; - resp.u.scratch.idx=_wapi_handle_scratch_store_internal (length); - + resp.u.scratch.idx=_wapi_handle_scratch_store_internal (length, &resp.u.scratch.remap); #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": allocating scratch index 0x%x", resp.u.scratch.idx); @@ -659,7 +1001,7 @@ static void process_scratch (GIOChannel *channel, guint32 length) */ static void process_scratch_free (GIOChannel *channel, guint32 scratch_idx) { - WapiHandleResponse resp; + WapiHandleResponse resp={0}; resp.type=WapiHandleResponseType_ScratchFree; _wapi_handle_scratch_delete_internal (scratch_idx); @@ -672,25 +1014,54 @@ 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) { - WapiHandleResponse resp; + WapiHandleResponse resp={0}; 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; @@ -700,27 +1071,28 @@ 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, TRUE); - unref_handle (open_handles, process_handle, TRUE); - unref_handle (daemon_handles, thread_handle, TRUE); - unref_handle (open_handles, thread_handle, TRUE); + 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 { char *cmd=NULL, *dir=NULL, **argv, **env; GError *gerr=NULL; gboolean ret; - + struct timeval tv; + /* Get usable copies of the cmd, dir and env now * rather than in the child process. This is to * prevent the race condition where the parent can @@ -729,9 +1101,19 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles, * gets to see it. Also explode argv here so we can * use it to set the process name. */ - cmd=_wapi_handle_scratch_lookup_as_string (process_fork.cmd); - dir=_wapi_handle_scratch_lookup_as_string (process_fork.dir); + 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) { @@ -744,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 @@ -780,10 +1152,29 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles, } /* Close all file descriptors */ - for(i=3; i 2; i--) { close (i); } + /* pass process and thread handle info + * to the child, so it doesn't have to + * do an expensive search over the + * whole list + */ + { + guint env_count=0; + + while(env[env_count]!=NULL) { + env_count++; + } + + 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", thread_handle); + env[env_count+2]=NULL; + } + #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": exec()ing [%s] in dir [%s]", @@ -810,7 +1201,7 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles, process_handle_data->exec_errno=errno; exit (-1); } - + /* exec */ execve (argv[0], argv, env); @@ -823,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)); } } @@ -848,8 +1247,10 @@ static void process_process_fork (GIOChannel *channel, guint32 *open_handles, /* store pid */ process_handle_data->id=pid; process_handle_data->main_thread=GUINT_TO_POINTER (thread_handle); - _wapi_time_t_to_filetime (time (NULL), - &process_handle_data->create_time); + /* Ignore errors */ + gettimeofday (&tv, NULL); + _wapi_timeval_to_filetime (&tv, + &process_handle_data->create_time); /* FIXME: if env==0, inherit the env from the current * process @@ -860,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}; @@ -891,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_MAX_HANDLES); + 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_MAX_HANDLES); + 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); @@ -919,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: @@ -941,6 +1422,8 @@ static void read_message (GIOChannel *channel, guint32 *open_handles) close (fds[1]); close (fds[2]); } + + return(TRUE); } /* @@ -955,24 +1438,29 @@ static void read_message (GIOChannel *channel, guint32 *open_handles) static gboolean fd_activity (GIOChannel *channel, GIOCondition condition, gpointer data) { + int fd=g_io_channel_unix_get_fd (channel); + 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", g_io_channel_unix_get_fd (channel)); + g_message ("fd %d error", fd); #endif - rem_fd (channel, data); + rem_fd (channel, channel_data); return(FALSE); } if(condition & (G_IO_IN | G_IO_PRI)) { - if(g_io_channel_unix_get_fd (channel)==main_sock) { + if(fd==main_sock) { int newsock; struct sockaddr addr; socklen_t addrlen=sizeof(struct sockaddr); newsock=accept (main_sock, &addr, &addrlen); if(newsock==-1) { - g_critical ("accept error: %s", strerror (errno)); + g_critical ("accept error: %s", + g_strerror (errno)); cleanup (); exit (-1); } @@ -981,14 +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", - g_io_channel_unix_get_fd (channel)); + g_message ("reading data on fd %d", fd); #endif - read_message (channel, data); + return(read_message (channel, channel_data)); } return(TRUE); } @@ -1002,28 +1489,40 @@ static gboolean fd_activity (GIOChannel *channel, GIOCondition condition, * Open socket, create shared mem segment and begin listening for * clients. */ -void _wapi_daemon_main(void) +void _wapi_daemon_main(gpointer data, gpointer scratch) { struct sockaddr_un main_socket_address; int ret; + GMainContext *context; #ifdef DEBUG g_message ("Starting up..."); #endif + _wapi_shared_data[0]=data; + _wapi_shared_scratch=scratch; + _wapi_shared_scratch->is_shared=TRUE; + + /* 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 (); main_sock=socket(PF_UNIX, SOCK_STREAM, 0); main_socket_address.sun_family=AF_UNIX; - memcpy(main_socket_address.sun_path, _wapi_shared_data->daemon, + memcpy(main_socket_address.sun_path, _wapi_shared_data[0]->daemon, MONO_SIZEOF_SUNPATH); ret=bind(main_sock, (struct sockaddr *)&main_socket_address, sizeof(struct sockaddr_un)); if(ret==-1) { - g_critical ("bind failed: %s", strerror (errno)); - _wapi_shared_data->daemon_running=DAEMON_DIED_AT_STARTUP; + g_critical ("bind failed: %s", g_strerror (errno)); + _wapi_shared_data[0]->daemon_running=DAEMON_DIED_AT_STARTUP; exit(-1); } @@ -1033,8 +1532,8 @@ void _wapi_daemon_main(void) ret=listen(main_sock, 5); if(ret==-1) { - g_critical ("listen failed: %s", strerror (errno)); - _wapi_shared_data->daemon_running=DAEMON_DIED_AT_STARTUP; + g_critical ("listen failed: %s", g_strerror (errno)); + _wapi_shared_data[0]->daemon_running=DAEMON_DIED_AT_STARTUP; exit(-1); } @@ -1042,13 +1541,15 @@ void _wapi_daemon_main(void) 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 * memory segment when appropriate. */ - _wapi_shared_data->daemon_running=DAEMON_RUNNING; + _wapi_shared_data[0]->daemon_running=DAEMON_RUNNING; while(TRUE) { if(check_processes==TRUE) { @@ -1065,7 +1566,6 @@ void _wapi_daemon_main(void) * 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); } } -