#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
-#define DEBUG 1
/* The shared thread codepath doesn't seem to work yet... */
#undef _POSIX_THREAD_PROCESS_SHARED
/* 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.
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)
{
"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);
}
}
if(_wapi_shared_data[segment]->handles[idx].ref==0) {
+ gboolean was_file;
+ dev_t device = 0;
+ ino_t inode = 0;
+
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]);
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));
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(channel_data == daemon_channel_data) {
*
* 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);
}
channels[fd].open_handles=refs;
- channels[fd].io_source=g_io_add_watch (io_channel, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
- fd_activity, NULL);
+
+ 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++;
}
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);
}
}
+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;
(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
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);
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
*
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;
}
/* Close all file descriptors */
- for(i=3; i<getdtablesize (); i++) {
+ for (i = getdtablesize () - 1; i > 2; i--) {
close (i);
}
#endif
/* set cwd */
-printf("set cwd\n");
if(chdir (dir)==-1) {
process_handle_data->exec_errno=errno;
exit (-1);
}
-printf("do exec\n");
/* exec */
execve (argv[0], argv, env);
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, ChannelData *channel_data)
+static gboolean read_message (GIOChannel *channel, ChannelData *channel_data)
{
WapiHandleRequest req;
int fds[3]={0, 1, 2};
g_io_channel_unix_get_fd (channel));
#endif
rem_fd (channel, channel_data);
- return;
+ return(FALSE);
}
#ifdef DEBUG
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:
close (fds[1]);
close (fds[2]);
}
+
+ return(TRUE);
}
/*
{
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 ("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, channel_data);
+ return(read_message (channel, channel_data));
}
return(TRUE);
}
{
struct sockaddr_un main_socket_address;
int ret;
+ GMainContext *context;
#ifdef DEBUG
g_message ("Starting up...");
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
* 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);
}
}