#include <config.h>
#include <glib.h>
+#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <sched.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+/* sys/resource.h (for rusage) is required when using osx 10.3 (but not 10.4) */
+#ifdef __APPLE__
+#include <sys/resource.h>
+#endif
#include <mono/io-layer/wapi.h>
#include <mono/io-layer/wapi-private.h>
#include <mono/io-layer/timefuncs-private.h>
/* The process' environment strings */
+#ifdef __APPLE__
+/* Apple defines this in crt_externs.h but doesn't provide that header for
+ * arm-apple-darwin9. We'll manually define the symbol on Apple as it does
+ * in fact exist on all implementations (so far)
+ */
+gchar ***_NSGetEnviron();
+#define environ (*_NSGetEnviron())
+#else
extern char **environ;
+#endif
#undef DEBUG
gboolean ok;
int thr_ret;
+ g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
+
ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
(gpointer *)&process_handle);
if (ok == FALSE) {
process_handle->exitstatus = WEXITSTATUS(status);
}
_wapi_time_t_to_filetime (time(NULL), &process_handle->exit_time);
+
+ /* Don't set process_handle->waited here, it needs to only
+ * happen in the parent when wait() has been called.
+ */
#ifdef DEBUG
- g_message ("%s: Setting handle %p signalled", __func__, handle);
+ g_message ("%s: Setting handle %p pid %d signalled, exit status %d",
+ __func__, handle, process_handle->id,
+ process_handle->exitstatus);
#endif
_wapi_shared_handle_set_signal_state (handle, TRUE);
int status;
pid_t ret;
- if (_wapi_handle_issignalled (test)) {
- /* We've already done this one */
- return (FALSE);
- }
+ g_assert ((GPOINTER_TO_UINT (test) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
ok = _wapi_lookup_handle (test, WAPI_HANDLE_PROCESS,
(gpointer *)&process);
/* The handle must have been too old and was reaped */
return (FALSE);
}
+
+ if (process->waited) {
+ /* We've already done this one */
+ return(FALSE);
+ }
do {
ret = waitpid (process->id, &status, WNOHANG);
g_message ("%s: Process %d finished", __func__, ret);
#endif
+ process->waited = TRUE;
+
*(int *)user_data = status;
return (TRUE);
pid_t pid, ret;
int status;
+ g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
+
#ifdef DEBUG
g_message ("%s: Waiting for process %p", __func__, handle);
#endif
+
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
+ (gpointer *)&process_handle);
+ if (ok == FALSE) {
+ g_warning ("%s: error looking up process handle %p", __func__,
+ handle);
+ return(WAIT_FAILED);
+ }
- if (_wapi_handle_issignalled (handle)) {
+ if (process_handle->waited) {
/* We've already done this one */
#ifdef DEBUG
g_message ("%s: Process %p already signalled", __func__,
return (WAIT_OBJECT_0);
}
-
- ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
- (gpointer *)&process_handle);
- if (ok == FALSE) {
- g_warning ("%s: error looking up process handle %p", __func__,
- handle);
- return(WAIT_FAILED);
- }
pid = process_handle->id;
#endif
if (timeout == INFINITE) {
- while ((ret = waitpid (pid, &status, 0)) != pid) {
- if (ret == (pid_t)-1 && errno != EINTR) {
- return(WAIT_FAILED);
+ if (pid == _wapi_getpid ()) {
+ do {
+ Sleep (10000);
+ } while(1);
+ } else {
+ while ((ret = waitpid (pid, &status, 0)) != pid) {
+ if (ret == (pid_t)-1 && errno != EINTR) {
+ return(WAIT_FAILED);
+ }
}
}
} else if (timeout == 0) {
}
} else {
/* Poll in a loop */
- do {
- ret = waitpid (pid, &status, WNOHANG);
- if (ret == pid) {
- break;
- } else if (ret == (pid_t)-1 && errno != EINTR) {
- return(WAIT_FAILED);
- }
+ if (pid == _wapi_getpid ()) {
+ Sleep (timeout);
+ return(WAIT_TIMEOUT);
+ } else {
+ do {
+ ret = waitpid (pid, &status, WNOHANG);
+#ifdef DEBUG
+ g_message ("%s: waitpid returns: %d, timeout is %d", __func__, ret, timeout);
+#endif
+
+ if (ret == pid) {
+ break;
+ } else if (ret == (pid_t)-1 &&
+ errno != EINTR) {
+#ifdef DEBUG
+ g_message ("%s: waitpid failure: %s",
+ __func__,
+ g_strerror (errno));
+#endif
- _wapi_handle_spin (100);
- timeout -= 100;
- } while (timeout > 0);
+ if (errno == ECHILD &&
+ process_handle->waited) {
+ /* The background
+ * process reaper must
+ * have got this one
+ */
+#ifdef DEBUG
+ g_message ("%s: Process %p already reaped", __func__, handle);
+#endif
+ return(WAIT_OBJECT_0);
+ } else {
+ return(WAIT_FAILED);
+ }
+ }
+
+ _wapi_handle_spin (100);
+ timeout -= 100;
+ } while (timeout > 0);
+ }
+
if (timeout <= 0) {
return(WAIT_TIMEOUT);
}
SetLastError (ERROR_OUTOFMEMORY);
return (WAIT_FAILED);
}
-
+ process_handle->waited = TRUE;
+
return(WAIT_OBJECT_0);
}
process_handle->min_working_set = 204800;
process_handle->max_working_set = 1413120;
+ process_handle->waited = FALSE;
+
_wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
}
+static int
+len16 (const gunichar2 *str)
+{
+ int len = 0;
+
+ while (*str++ != 0)
+ len++;
+
+ return len;
+}
+
+static gunichar2 *
+utf16_concat (const gunichar2 *first, ...)
+{
+ va_list args;
+ int total = 0, i;
+ const gunichar2 *s;
+ gunichar2 *ret;
+
+ va_start (args, first);
+ total += len16 (first);
+ for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *)){
+ total += len16 (s);
+ }
+ va_end (args);
+
+ ret = g_new (gunichar2, total + 1);
+ if (ret == NULL)
+ return NULL;
+
+ ret [total] = 0;
+ i = 0;
+ for (s = first; *s != 0; s++)
+ ret [i++] = *s;
+ va_start (args, first);
+ for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg (args, gunichar2 *)){
+ const gunichar2 *p;
+
+ for (p = s; *p != 0; p++)
+ ret [i++] = *p;
+ }
+ va_end (args);
+
+ return ret;
+}
+
+static const gunichar2 utf16_space_bytes [2] = { 0x20, 0 };
+static const gunichar2 *utf16_space = utf16_space_bytes;
+static const gunichar2 utf16_quote_bytes [2] = { 0x22, 0 };
+static const gunichar2 *utf16_quote = utf16_quote_bytes;
+
/* Implemented as just a wrapper around CreateProcess () */
gboolean ShellExecuteEx (WapiShellExecuteInfo *sei)
{
gboolean ret;
WapiProcessInformation process_info;
gunichar2 *args;
- gchar *u8file, *u8params, *u8args;
if (sei == NULL) {
/* w2k just segfaults here, but we can do better than
* into and back out of utf8 is because there is no
* g_strdup_printf () equivalent for gunichar2 :-(
*/
- u8file = g_utf16_to_utf8 (sei->lpFile, -1, NULL, NULL, NULL);
- if (u8file == NULL) {
+ args = utf16_concat (sei->lpFile, sei->lpParameters == NULL ? NULL : utf16_space, sei->lpParameters, NULL);
+ if (args == NULL){
SetLastError (ERROR_INVALID_DATA);
return (FALSE);
}
-
- if (sei->lpParameters != NULL) {
- u8params = g_utf16_to_utf8 (sei->lpParameters, -1, NULL, NULL, NULL);
- if (u8params == NULL) {
- SetLastError (ERROR_INVALID_DATA);
- g_free (u8file);
- return (FALSE);
- }
-
- u8args = g_strdup_printf ("%s %s", u8file, u8params);
- if (u8args == NULL) {
- SetLastError (ERROR_INVALID_DATA);
- g_free (u8params);
- g_free (u8file);
- return (FALSE);
- }
-
- args = g_utf8_to_utf16 (u8args, -1, NULL, NULL, NULL);
-
- g_free (u8file);
- g_free (u8params);
- g_free (u8args);
- } else {
- args = g_utf8_to_utf16 (u8file, -1, NULL, NULL, NULL);
- }
-
- if (args == NULL) {
- SetLastError (ERROR_INVALID_DATA);
- return (FALSE);
- }
-
ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
CREATE_UNICODE_ENVIRONMENT, NULL,
sei->lpDirectory, NULL, &process_info);
g_free (args);
if (!ret) {
- return (FALSE);
+ static char *handler;
+ static gunichar2 *handler_utf16;
+
+ if (handler_utf16 == (gunichar2 *)-1)
+ return FALSE;
+
+#ifdef PLATFORM_MACOSX
+ handler = "/usr/bin/open";
+#else
+ /*
+ * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
+ * if that fails, try to use gnome-open, then kfmclient
+ */
+ handler = g_find_program_in_path ("xdg-open");
+ if (handler == NULL){
+ handler = g_find_program_in_path ("gnome-open");
+ if (handler == NULL){
+ handler = g_find_program_in_path ("kfmclient");
+ if (handler == NULL){
+ handler_utf16 = (gunichar2 *) -1;
+ return (FALSE);
+ } else {
+ /* kfmclient needs exec argument */
+ char *old = handler;
+ handler = g_strconcat (old, " exec",
+ NULL);
+ g_free (old);
+ }
+ }
+ }
+#endif
+ handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
+ g_free (handler);
+
+ /* Put quotes around the filename, in case it's a url
+ * that contains #'s (CreateProcess() calls
+ * g_shell_parse_argv(), which deliberately throws
+ * away anything after an unquoted #). Fixes bug
+ * 371567.
+ */
+ args = utf16_concat (handler_utf16, utf16_space, utf16_quote,
+ sei->lpFile, utf16_quote,
+ sei->lpParameters == NULL ? NULL : utf16_space,
+ sei->lpParameters, NULL);
+ if (args == NULL){
+ SetLastError (ERROR_INVALID_DATA);
+ return FALSE;
+ }
+ ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
+ CREATE_UNICODE_ENVIRONMENT, NULL,
+ sei->lpDirectory, NULL, &process_info);
+ g_free (args);
+ if (!ret){
+ SetLastError (ERROR_INVALID_DATA);
+ return FALSE;
+ }
}
if (sei->fMask & SEE_MASK_NOCLOSEPROCESS) {
return (ret);
}
+static gboolean
+is_managed_binary (const gchar *filename)
+{
+ int original_errno = errno;
+#if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
+ int file = open (filename, O_RDONLY | O_LARGEFILE);
+#else
+ int file = open (filename, O_RDONLY);
+#endif
+ off_t new_offset;
+ unsigned char buffer[8];
+ off_t file_size, optional_header_offset;
+ off_t pe_header_offset;
+ gboolean managed = FALSE;
+ int num_read;
+ guint32 first_word, second_word;
+
+ /* If we are unable to open the file, then we definitely
+ * can't say that it is managed. The child mono process
+ * probably wouldn't be able to open it anyway.
+ */
+ if (file < 0) {
+ errno = original_errno;
+ return FALSE;
+ }
+
+ /* Retrieve the length of the file for future sanity checks. */
+ file_size = lseek (file, 0, SEEK_END);
+ lseek (file, 0, SEEK_SET);
+
+ /* We know we need to read a header field at offset 60. */
+ if (file_size < 64)
+ goto leave;
+
+ num_read = read (file, buffer, 2);
+
+ if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
+ goto leave;
+
+ new_offset = lseek (file, 60, SEEK_SET);
+
+ if (new_offset != 60)
+ goto leave;
+
+ num_read = read (file, buffer, 4);
+
+ if (num_read != 4)
+ goto leave;
+ pe_header_offset = buffer[0]
+ | (buffer[1] << 8)
+ | (buffer[2] << 16)
+ | (buffer[3] << 24);
+
+ if (pe_header_offset + 24 > file_size)
+ goto leave;
+
+ new_offset = lseek (file, pe_header_offset, SEEK_SET);
+
+ if (new_offset != pe_header_offset)
+ goto leave;
+
+ num_read = read (file, buffer, 4);
+
+ if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
+ goto leave;
+
+ /*
+ * Verify that the header we want in the optional header data
+ * is present in this binary.
+ */
+ new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
+
+ if (new_offset != pe_header_offset + 20)
+ goto leave;
+
+ num_read = read (file, buffer, 2);
+
+ if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
+ goto leave;
+
+ /* Read the CLR header address and size fields. These will be
+ * zero if the binary is not managed.
+ */
+ optional_header_offset = pe_header_offset + 24;
+ new_offset = lseek (file, optional_header_offset + 208, SEEK_SET);
+
+ if (new_offset != optional_header_offset + 208)
+ goto leave;
+
+ num_read = read (file, buffer, 8);
+
+ /* We are not concerned with endianness, only with
+ * whether it is zero or not.
+ */
+ first_word = *(guint32 *)&buffer[0];
+ second_word = *(guint32 *)&buffer[4];
+
+ if ((num_read != 8) || (first_word == 0) || (second_word == 0))
+ goto leave;
+
+ managed = TRUE;
+
+leave:
+ close (file);
+ errno = original_errno;
+ return managed;
+}
+
+gboolean CreateProcessWithLogonW (const gunichar2 *username,
+ const gunichar2 *domain,
+ const gunichar2 *password,
+ const guint32 logonFlags,
+ const gunichar2 *appname,
+ const gunichar2 *cmdline,
+ guint32 create_flags,
+ gpointer env,
+ const gunichar2 *cwd,
+ WapiStartupInfo *startup,
+ WapiProcessInformation *process_info)
+{
+ /* FIXME: use user information */
+ return CreateProcess (appname, cmdline, NULL, NULL, FALSE, create_flags, env, cwd, startup, process_info);
+}
+
+static gboolean
+is_executable (const char *prog)
+{
+ struct stat buf;
+ if (access (prog, X_OK) != 0)
+ return FALSE;
+ if (stat (prog, &buf))
+ return FALSE;
+ if (S_ISREG (buf.st_mode))
+ return TRUE;
+ return FALSE;
+}
+
gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
WapiSecurityAttributes *process_attrs G_GNUC_UNUSED,
WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED,
WapiStartupInfo *startup,
WapiProcessInformation *process_info)
{
- gchar *cmd=NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL, *dir = NULL, **env_strings = NULL, **argv;
+ gchar *cmd=NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL, *dir = NULL, **env_strings = NULL, **argv = NULL;
guint32 i, env_count = 0;
gboolean ret = FALSE;
gpointer handle;
#endif
SetLastError (ERROR_PATH_NOT_FOUND);
- goto cleanup;
+ goto free_strings;
}
/* Turn all the slashes round the right way */
#endif
SetLastError (ERROR_PATH_NOT_FOUND);
- goto cleanup;
+ goto free_strings;
}
}
#endif
SetLastError (ERROR_PATH_NOT_FOUND);
- goto cleanup;
+ goto free_strings;
}
/* Turn all the slashes round the right way */
prog = g_strdup (unquoted);
/* Executable existing ? */
- if (access (prog, X_OK) != 0) {
+ if (!is_executable (prog)) {
#ifdef DEBUG
g_message ("%s: Couldn't find executable %s",
__func__, prog);
#endif
- g_free (prog);
g_free (unquoted);
SetLastError (ERROR_FILE_NOT_FOUND);
- goto cleanup;
+ goto free_strings;
}
} else {
/* Search for file named by cmd in the current
char *curdir = g_get_current_dir ();
prog = g_strdup_printf ("%s/%s", curdir, unquoted);
- g_free (unquoted);
g_free (curdir);
/* And make sure it's executable */
- if (access (prog, X_OK) != 0) {
+ if (!is_executable (prog)) {
#ifdef DEBUG
g_message ("%s: Couldn't find executable %s",
__func__, prog);
#endif
- g_free (prog);
+ g_free (unquoted);
SetLastError (ERROR_FILE_NOT_FOUND);
- goto cleanup;
+ goto free_strings;
}
}
+ g_free (unquoted);
args_after_prog = args;
} else {
#endif
SetLastError (ERROR_PATH_NOT_FOUND);
- goto cleanup;
+ goto free_strings;
}
/* Turn all the slashes round the right way. Only for
prog = g_strdup (token);
/* Executable existing ? */
- if (access (prog, X_OK) != 0) {
- g_free (prog);
+ if (!is_executable (prog)) {
#ifdef DEBUG
g_message ("%s: Couldn't find executable %s",
__func__, token);
#endif
g_free (token);
SetLastError (ERROR_FILE_NOT_FOUND);
- goto cleanup;
+ goto free_strings;
}
} else {
/* I assume X_OK is the criterion to use,
* rather than F_OK
*/
- if (access (prog, X_OK) != 0) {
+ if (!is_executable (prog)) {
g_free (prog);
prog = g_find_program_in_path (token);
if (prog == NULL) {
g_free (token);
SetLastError (ERROR_FILE_NOT_FOUND);
- goto cleanup;
+ goto free_strings;
}
}
}
args_after_prog);
#endif
+ /* Check for CLR binaries; if found, we will try to invoke
+ * them using the same mono binary that started us.
+ */
+ if (is_managed_binary (prog)) {
+ gunichar2 *newapp, *newcmd;
+ gsize bytes_ignored;
+
+ newapp = mono_unicode_from_external ("mono", &bytes_ignored);
+
+ if (newapp != NULL) {
+ if (appname != NULL) {
+ newcmd = utf16_concat (newapp, utf16_space,
+ appname, utf16_space,
+ cmdline, NULL);
+ } else {
+ newcmd = utf16_concat (newapp, utf16_space,
+ cmdline, NULL);
+ }
+
+ g_free ((gunichar2 *)newapp);
+
+ if (newcmd != NULL) {
+ ret = CreateProcess (NULL, newcmd,
+ process_attrs,
+ thread_attrs,
+ inherit_handles,
+ create_flags, new_environ,
+ cwd, startup,
+ process_info);
+
+ g_free ((gunichar2 *)newcmd);
+
+ goto free_strings;
+ }
+ }
+ }
+
if (args_after_prog != NULL && *args_after_prog) {
gchar *qprog;
g_warning ("%s: error creating process handle", __func__);
SetLastError (ERROR_PATH_NOT_FOUND);
- goto cleanup;
+ goto free_strings;
}
/* Hold another reference so the process has somewhere to
} else if (pid == 0) {
/* Child */
- /* Wait for the parent to finish setting up the
- * handle. The semaphore lock is safe because the
- * sem_undo structures of a semaphore aren't inherited
- * across a fork ()
- */
- thr_ret = _wapi_handle_lock_shared_handles ();
- g_assert (thr_ret == 0);
+ if (_wapi_shm_disabled == FALSE) {
+ /* Wait for the parent to finish setting up
+ * the handle. The semaphore lock is safe
+ * because the sem_undo structures of a
+ * semaphore aren't inherited across a fork
+ * (), but we can't do this if we're not using
+ * the shared memory
+ */
+ thr_ret = _wapi_handle_lock_shared_handles ();
+ g_assert (thr_ret == 0);
- _wapi_handle_unlock_shared_handles ();
+ _wapi_handle_unlock_shared_handles ();
+ }
/* should we detach from the process group? */
cleanup:
_wapi_handle_unlock_shared_handles ();
+free_strings:
if (cmd != NULL) {
g_free (cmd);
}
if (full_prog != NULL) {
+ g_free (full_prog);
+ }
+ if (prog != NULL) {
g_free (prog);
}
if (args != NULL) {
if(env_strings != NULL) {
g_strfreev (env_strings);
}
+ if (argv != NULL) {
+ g_strfreev (argv);
+ }
+#ifdef DEBUG
+ g_message ("%s: returning handle %p for pid %d", __func__, handle,
+ pid);
+#endif
+
return(ret);
}
const char *handle_env;
struct _WapiHandle_process process_handle = {0};
+ mono_once (&process_ops_once, process_ops_init);
+
handle_env = g_getenv ("_WAPI_PROCESS_HANDLE_OFFSET");
g_unsetenv ("_WAPI_PROCESS_HANDLE_OFFSET");
{
struct _WapiHandle_process *process_handle;
gboolean ok;
+
+ if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle */
+ return(GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+ }
ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
(gpointer *)&process_handle);
struct _WapiHandle_process *process_handle;
gboolean ok;
+ g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
+
/* Make sure the process is signalled if it has exited - if
* the parent process didn't wait for it then it won't be
*/
return (process_handle->id);
}
+#ifdef UNUSED_CODE
static gboolean process_enum (gpointer handle, gpointer user_data)
{
GArray *processes=user_data;
/* Return false to keep searching */
return(FALSE);
}
+#endif /* UNUSED_CODE */
gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
{
GArray *processes = g_array_new (FALSE, FALSE, sizeof(pid_t));
guint32 fit, i, j;
+ DIR *dir;
+ struct dirent *entry;
mono_once (&process_current_once, process_set_current);
-
- _wapi_search_handle (WAPI_HANDLE_PROCESS, process_enum, processes,
- NULL, TRUE);
-
+
+ dir = opendir ("/proc");
+ if (dir == NULL) {
+ return(FALSE);
+ }
+ while((entry = readdir (dir)) != NULL) {
+ if (isdigit (entry->d_name[0])) {
+ char *endptr;
+ pid_t pid = (pid_t)strtol (entry->d_name, &endptr, 10);
+
+ if (*endptr == '\0') {
+ /* Name was entirely numeric, so was a
+ * process ID
+ */
+ g_array_append_val (processes, pid);
+ }
+ }
+ }
+ closedir (dir);
+
fit=len/sizeof(guint32);
for (i = 0, j = 0; j < fit && i < processes->len; i++) {
pids[j++] = g_array_index (processes, pid_t, i);
}
}
-gpointer OpenProcess (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
+gpointer OpenProcess (guint32 req_access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
{
/* Find the process handle that corresponds to pid */
gpointer handle;
process_open_compare,
GUINT_TO_POINTER (pid), NULL, TRUE);
if (handle == 0) {
+ gchar *dir = g_strdup_printf ("/proc/%d", pid);
+ if (!access (dir, F_OK)) {
+ /* Return a pseudo handle for processes we
+ * don't have handles for
+ */
+ return((gpointer)(_WAPI_PROCESS_UNHANDLED + pid));
+ } else {
#ifdef DEBUG
- g_message ("%s: Can't find pid %d", __func__, pid);
+ g_message ("%s: Can't find pid %d", __func__, pid);
#endif
- SetLastError (ERROR_PROC_NOT_FOUND);
+ SetLastError (ERROR_PROC_NOT_FOUND);
- return(NULL);
+ return(NULL);
+ }
}
_wapi_handle_ref (handle);
return(FALSE);
}
+ if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle, so we don't know what the
+ * exit code was
+ */
+ return(FALSE);
+ }
+
ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
(gpointer *)&process_handle);
if(ok==FALSE) {
/* A process handle is only signalled if the process has exited
* and has been waited for */
- if (_wapi_handle_issignalled (process) == TRUE ||
- process_wait (process, 0) == WAIT_OBJECT_0) {
+
+ /* Make sure any process exit has been noticed, before
+ * checking if the process is signalled. Fixes bug 325463.
+ */
+ process_wait (process, 0);
+
+ if (_wapi_handle_issignalled (process) == TRUE) {
*code = process_handle->exitstatus;
} else {
*code = STILL_ACTIVE;
{
struct _WapiHandle_process *process_handle;
gboolean ok;
+ gboolean ku_times_set = FALSE;
mono_once (&process_current_once, process_set_current);
return(FALSE);
}
+ if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle, so just fail for now
+ */
+ return(FALSE);
+ }
+
ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
(gpointer *)&process_handle);
if(ok==FALSE) {
if(_wapi_handle_issignalled (process)==TRUE) {
*exit_time=process_handle->exit_time;
}
-
+
+#ifdef HAVE_GETRUSAGE
+ if (process_handle->id == getpid ()) {
+ struct rusage time_data;
+ if (getrusage (RUSAGE_SELF, &time_data) == 0) {
+ gint64 tick_val;
+ gint64 *tick_val_ptr;
+ ku_times_set = TRUE;
+ tick_val = time_data.ru_utime.tv_sec * 10000000 + time_data.ru_utime.tv_usec * 10;
+ tick_val_ptr = (gint64*)user_time;
+ *tick_val_ptr = tick_val;
+ tick_val = time_data.ru_stime.tv_sec * 10000000 + time_data.ru_stime.tv_usec * 10;
+ tick_val_ptr = (gint64*)kernel_time;
+ *tick_val_ptr = tick_val;
+ }
+ }
+#endif
+ if (!ku_times_set) {
+ memset (kernel_time, 0, sizeof (WapiFileTime));
+ memset (user_time, 0, sizeof (WapiFileTime));
+ }
+
return(TRUE);
}
+typedef struct
+{
+ gpointer address_start;
+ gpointer address_end;
+ gchar *perms;
+ gpointer address_offset;
+ dev_t device;
+ ino_t inode;
+ gchar *filename;
+} WapiProcModule;
+
+static void free_procmodule (WapiProcModule *mod)
+{
+ if (mod->perms != NULL) {
+ g_free (mod->perms);
+ }
+ if (mod->filename != NULL) {
+ g_free (mod->filename);
+ }
+ g_free (mod);
+}
+
+static gint find_procmodule (gconstpointer a, gconstpointer b)
+{
+ WapiProcModule *want = (WapiProcModule *)a;
+ WapiProcModule *compare = (WapiProcModule *)b;
+
+ if ((want->device == compare->device) &&
+ (want->inode == compare->inode)) {
+ return(0);
+ } else {
+ return(1);
+ }
+}
+
+static GSList *load_modules (FILE *fp)
+{
+ GSList *ret = NULL;
+ WapiProcModule *mod;
+ gchar buf[MAXPATHLEN + 1], *p, *endp;
+ gchar *start_start, *end_start, *prot_start, *offset_start;
+ gchar *maj_dev_start, *min_dev_start, *inode_start, prot_buf[5];
+ gpointer address_start, address_end, address_offset;
+ guint32 maj_dev, min_dev;
+ ino_t inode;
+ dev_t device;
+
+ while (fgets (buf, sizeof(buf), fp)) {
+ p = buf;
+ while (g_ascii_isspace (*p)) ++p;
+ start_start = p;
+ if (!g_ascii_isxdigit (*start_start)) {
+ continue;
+ }
+ address_start = (gpointer)strtoul (start_start, &endp, 16);
+ p = endp;
+ if (*p != '-') {
+ continue;
+ }
+
+ ++p;
+ end_start = p;
+ if (!g_ascii_isxdigit (*end_start)) {
+ continue;
+ }
+ address_end = (gpointer)strtoul (end_start, &endp, 16);
+ p = endp;
+ if (!g_ascii_isspace (*p)) {
+ continue;
+ }
+
+ while (g_ascii_isspace (*p)) ++p;
+ prot_start = p;
+ if (*prot_start != 'r' && *prot_start != '-') {
+ continue;
+ }
+ memcpy (prot_buf, prot_start, 4);
+ prot_buf[4] = '\0';
+ while (!g_ascii_isspace (*p)) ++p;
+
+ while (g_ascii_isspace (*p)) ++p;
+ offset_start = p;
+ if (!g_ascii_isxdigit (*offset_start)) {
+ continue;
+ }
+ address_offset = (gpointer)strtoul (offset_start, &endp, 16);
+ p = endp;
+ if (!g_ascii_isspace (*p)) {
+ continue;
+ }
+
+ while(g_ascii_isspace (*p)) ++p;
+ maj_dev_start = p;
+ if (!g_ascii_isxdigit (*maj_dev_start)) {
+ continue;
+ }
+ maj_dev = strtoul (maj_dev_start, &endp, 16);
+ p = endp;
+ if (*p != ':') {
+ continue;
+ }
+
+ ++p;
+ min_dev_start = p;
+ if (!g_ascii_isxdigit (*min_dev_start)) {
+ continue;
+ }
+ min_dev = strtoul (min_dev_start, &endp, 16);
+ p = endp;
+ if (!g_ascii_isspace (*p)) {
+ continue;
+ }
+
+ while (g_ascii_isspace (*p)) ++p;
+ inode_start = p;
+ if (!g_ascii_isxdigit (*inode_start)) {
+ continue;
+ }
+ inode = (ino_t)strtol (inode_start, &endp, 10);
+ p = endp;
+ if (!g_ascii_isspace (*p)) {
+ continue;
+ }
+
+ device = makedev ((int)maj_dev, (int)min_dev);
+ if ((device == 0) &&
+ (inode == 0)) {
+ continue;
+ }
+
+ while(g_ascii_isspace (*p)) ++p;
+ /* p now points to the filename */
+
+ mod = g_new0 (WapiProcModule, 1);
+ mod->address_start = address_start;
+ mod->address_end = address_end;
+ mod->perms = g_strdup (prot_buf);
+ mod->address_offset = address_offset;
+ mod->device = device;
+ mod->inode = inode;
+ mod->filename = g_strdup (g_strstrip (p));
+
+ if (g_slist_find_custom (ret, mod, find_procmodule) == NULL) {
+ ret = g_slist_prepend (ret, mod);
+ } else {
+ free_procmodule (mod);
+ }
+ }
+
+ ret = g_slist_reverse (ret);
+
+ return(ret);
+}
+
+static gboolean match_procname_to_modulename (gchar *procname, gchar *modulename)
+{
+ char* lastsep = NULL;
+
+ if (procname == NULL || modulename == NULL)
+ return (FALSE);
+
+ if (!strcmp (procname, modulename))
+ return (TRUE);
+
+ lastsep = strrchr (modulename, '/');
+ if (lastsep) {
+ if (!strcmp (lastsep+1, procname))
+ return (TRUE);
+ return (FALSE);
+ }
+
+ return (FALSE);
+}
+
gboolean EnumProcessModules (gpointer process, gpointer *modules,
guint32 size, guint32 *needed)
{
+ struct _WapiHandle_process *process_handle;
+ gboolean ok;
+ gchar *filename;
+ FILE *fp;
+ GSList *mods = NULL;
+ WapiProcModule *module;
+ guint32 count, avail = size / sizeof(gpointer);
+ int i;
+ pid_t pid;
+ gchar *proc_name = NULL;
+
/* Store modules in an array of pointers (main module as
* modules[0]), using the load address for each module as a
* token. (Use 'NULL' as an alternative for the main module
* other systems will have to implement /dev/kmem reading or
* whatever other horrid technique is needed.
*/
- if(size<sizeof(gpointer)) {
+ if (size < sizeof(gpointer)) {
return(FALSE);
}
-
-#ifdef linux
- modules[0]=NULL;
- *needed=sizeof(gpointer);
-#else
- modules[0]=NULL;
- *needed=sizeof(gpointer);
+
+ if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle */
+ pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+ } else {
+ ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+ (gpointer *)&process_handle);
+ if (ok == FALSE) {
+#ifdef DEBUG
+ g_message ("%s: Can't find process %p", __func__, process);
#endif
+
+ return(FALSE);
+ }
+ pid = process_handle->id;
+ proc_name = process_handle->proc_name;
+ }
+
+ filename = g_strdup_printf ("/proc/%d/maps", pid);
+ if ((fp = fopen (filename, "r")) == NULL) {
+ /* No /proc/<pid>/maps so just return the main module
+ * shortcut for now
+ */
+ modules[0] = NULL;
+ *needed = sizeof(gpointer);
+ } else {
+ mods = load_modules (fp);
+ fclose (fp);
+ count = g_slist_length (mods);
+
+ /* count + 1 to leave slot 0 for the main module */
+ *needed = sizeof(gpointer) * (count + 1);
+
+ /* Use the NULL shortcut, as the first line in
+ * /proc/<pid>/maps isn't the executable, and we need
+ * that first in the returned list. Check the module name
+ * to see if it ends with the proc name and substitute
+ * the first entry with it. FIXME if this turns out to
+ * be a problem.
+ */
+ modules[0] = NULL;
+ for (i = 0; i < (avail - 1) && i < count; i++) {
+ module = (WapiProcModule *)g_slist_nth_data (mods, i);
+ if (modules[0] != NULL)
+ modules[i] = module->address_start;
+ else if (match_procname_to_modulename (proc_name, module->filename))
+ modules[0] = module->address_start;
+ else
+ modules[i + 1] = module->address_start;
+ }
+
+ for (i = 0; i < count; i++) {
+ free_procmodule (g_slist_nth_data (mods, i));
+ }
+ g_slist_free (mods);
+ }
+
+ g_free (filename);
return(TRUE);
}
-guint32 GetModuleBaseName (gpointer process, gpointer module,
- gunichar2 *basename, guint32 size)
+static gchar *get_process_name_from_proc (pid_t pid)
+{
+ gchar *filename;
+ gchar *ret = NULL;
+ gchar buf[256];
+ FILE *fp;
+
+ memset (buf, '\0', sizeof(buf));
+
+ filename = g_strdup_printf ("/proc/%d/exe", pid);
+ if (readlink (filename, buf, 255) > 0) {
+ ret = g_strdup (buf);
+ }
+ g_free (filename);
+
+ if (ret != NULL) {
+ return(ret);
+ }
+
+ filename = g_strdup_printf ("/proc/%d/cmdline", pid);
+ if ((fp = fopen (filename, "r")) != NULL) {
+ if (fgets (buf, 256, fp) != NULL) {
+ ret = g_strdup (buf);
+ }
+
+ fclose (fp);
+ }
+ g_free (filename);
+
+ if (ret != NULL) {
+ return(ret);
+ }
+
+ filename = g_strdup_printf ("/proc/%d/stat", pid);
+ if ((fp = fopen (filename, "r")) != NULL) {
+ if (fgets (buf, 256, fp) != NULL) {
+ gchar *start, *end;
+
+ start = strchr (buf, '(');
+ if (start != NULL) {
+ end = strchr (start + 1, ')');
+
+ if (end != NULL) {
+ ret = g_strndup (start + 1,
+ end - start - 1);
+ }
+ }
+ }
+
+ fclose (fp);
+ }
+ g_free (filename);
+
+ if (ret != NULL) {
+ return(ret);
+ }
+
+ return(NULL);
+}
+
+static guint32 get_module_name (gpointer process, gpointer module,
+ gunichar2 *basename, guint32 size,
+ gboolean base)
{
struct _WapiHandle_process *process_handle;
gboolean ok;
+ pid_t pid;
+ gunichar2 *procname;
+ gchar *procname_ext = NULL;
+ glong len;
+ gsize bytes;
+ gchar *filename;
+ FILE *fp;
+ GSList *mods = NULL;
+ WapiProcModule *found_module;
+ guint32 count;
+ int i;
+ gchar *proc_name = NULL;
mono_once (&process_current_once, process_set_current);
__func__, process, module);
#endif
- if(basename==NULL || size==0) {
- return(FALSE);
+ if (basename == NULL || size == 0) {
+ return(0);
}
- ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
- (gpointer *)&process_handle);
- if(ok==FALSE) {
+ if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle */
+ pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+ proc_name = get_process_name_from_proc (pid);
+ } else {
+ ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+ (gpointer *)&process_handle);
+ if (ok == FALSE) {
#ifdef DEBUG
- g_message ("%s: Can't find process %p", __func__, process);
+ g_message ("%s: Can't find process %p", __func__,
+ process);
#endif
-
- return(FALSE);
+
+ return(0);
+ }
+ pid = process_handle->id;
+ proc_name = g_strdup (process_handle->proc_name);
}
- if(module==NULL) {
- /* Shorthand for the main module, which has the
- * process name recorded in the handle data
+ /* Look up the address in /proc/<pid>/maps */
+ filename = g_strdup_printf ("/proc/%d/maps", pid);
+ if ((fp = fopen (filename, "r")) == NULL) {
+ if (errno == EACCES && module == NULL && base == TRUE) {
+ procname_ext = get_process_name_from_proc (pid);
+ } else {
+ /* No /proc/<pid>/maps, so just return failure
+ * for now
+ */
+ g_free (proc_name);
+ g_free (filename);
+ return(0);
+ }
+ } else {
+ mods = load_modules (fp);
+ fclose (fp);
+ count = g_slist_length (mods);
+
+ /* If module != NULL compare the address.
+ * If module == NULL we are looking for the main module.
+ * The best we can do for now check it the module name end with the process name.
*/
- pid_t pid;
- gunichar2 *procname;
- gchar *procname_utf8 = NULL;
- glong len, bytes;
-
-#ifdef DEBUG
- g_message ("%s: Returning main module name", __func__);
-#endif
+ for (i = 0; i < count; i++) {
+ found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
+ if (procname_ext == NULL &&
+ ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
+ (module != NULL && found_module->address_start == module))) {
+ if (base) {
+ procname_ext = g_path_get_basename (found_module->filename);
+ } else {
+ procname_ext = g_strdup (found_module->filename);
+ }
+ }
- pid=process_handle->id;
- procname_utf8 = process_handle->proc_name;
-
+ free_procmodule (found_module);
+ }
+
+ if (procname_ext == NULL)
+ {
+ /* If it's *still* null, we might have hit the
+ * case where reading /proc/$pid/maps gives an
+ * empty file for this user.
+ */
+ procname_ext = get_process_name_from_proc (pid);
+ }
+
+ g_slist_free (mods);
+ g_free (filename);
+ g_free (proc_name);
+ }
+
+ if (procname_ext != NULL) {
#ifdef DEBUG
g_message ("%s: Process name is [%s]", __func__,
- procname_utf8);
+ procname_ext);
#endif
- procname = g_utf8_to_utf16 (procname_utf8, -1, NULL, &len,
- NULL);
+ procname = mono_unicode_from_external (procname_ext, &bytes);
if (procname == NULL) {
/* bugger */
+ g_free (procname_ext);
return(0);
}
-
- /* Add the terminator, and convert chars to bytes */
- bytes = (len + 1) * 2;
+
+ len = (bytes / 2);
+
+ /* Add the terminator */
+ bytes += 2;
if (size < bytes) {
#ifdef DEBUG
}
g_free (procname);
-
+ g_free (procname_ext);
+
return(len);
- } else {
- /* Look up the address in /proc/<pid>/maps */
}
return(0);
}
+guint32 GetModuleBaseName (gpointer process, gpointer module,
+ gunichar2 *basename, guint32 size)
+{
+ return(get_module_name (process, module, basename, size, TRUE));
+}
+
+guint32 GetModuleFileNameEx (gpointer process, gpointer module,
+ gunichar2 *filename, guint32 size)
+{
+ return(get_module_name (process, module, filename, size, FALSE));
+}
+
+gboolean GetModuleInformation (gpointer process, gpointer module,
+ WapiModuleInfo *modinfo, guint32 size)
+{
+ struct _WapiHandle_process *process_handle;
+ gboolean ok;
+ pid_t pid;
+ gchar *filename;
+ FILE *fp;
+ GSList *mods = NULL;
+ WapiProcModule *found_module;
+ guint32 count;
+ int i;
+ gboolean ret = FALSE;
+ gchar *proc_name = NULL;
+
+ mono_once (&process_current_once, process_set_current);
+
+#ifdef DEBUG
+ g_message ("%s: Getting module info, process handle %p module %p",
+ __func__, process, module);
+#endif
+
+ if (modinfo == NULL || size < sizeof(WapiModuleInfo)) {
+ return(FALSE);
+ }
+
+ if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle */
+ pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+ proc_name = get_process_name_from_proc (pid);
+ } else {
+ ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+ (gpointer *)&process_handle);
+ if (ok == FALSE) {
+#ifdef DEBUG
+ g_message ("%s: Can't find process %p", __func__,
+ process);
+#endif
+
+ return(FALSE);
+ }
+ pid = process_handle->id;
+ proc_name = g_strdup (process_handle->proc_name);
+ }
+
+ /* Look up the address in /proc/<pid>/maps */
+ filename = g_strdup_printf ("/proc/%d/maps", pid);
+ if ((fp = fopen (filename, "r")) == NULL) {
+ /* No /proc/<pid>/maps, so just return failure
+ * for now
+ */
+ g_free (proc_name);
+ g_free (filename);
+ return(FALSE);
+ } else {
+ mods = load_modules (fp);
+ fclose (fp);
+ count = g_slist_length (mods);
+
+ /* If module != NULL compare the address.
+ * If module == NULL we are looking for the main module.
+ * The best we can do for now check it the module name end with the process name.
+ */
+ for (i = 0; i < count; i++) {
+ found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
+ if ( ret == FALSE &&
+ ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
+ (module != NULL && found_module->address_start == module))) {
+ modinfo->lpBaseOfDll = found_module->address_start;
+ modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start);
+ modinfo->EntryPoint = found_module->address_offset;
+ ret = TRUE;
+ }
+
+ free_procmodule (found_module);
+ }
+
+ g_slist_free (mods);
+ g_free (filename);
+ g_free (proc_name);
+ }
+
+ return(ret);
+}
+
gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
{
struct _WapiHandle_process *process_handle;
return(FALSE);
}
+ if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle, so just fail for now
+ */
+ return(FALSE);
+ }
+
ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
(gpointer *)&process_handle);
if(ok==FALSE) {
gboolean ok;
mono_once (&process_current_once, process_set_current);
+
+ if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle, so just fail for now
+ */
+ return(FALSE);
+ }
ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
(gpointer *)&process_handle);
gboolean ok;
int signo;
int ret;
+ pid_t pid;
+
+ if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle */
+ pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+ } else {
+ ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+ (gpointer *) &process_handle);
- ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
- (gpointer *) &process_handle);
-
- if (ok == FALSE) {
+ if (ok == FALSE) {
#ifdef DEBUG
- g_message ("%s: Can't find process %p", __func__, process);
+ g_message ("%s: Can't find process %p", __func__,
+ process);
#endif
- SetLastError (ERROR_INVALID_HANDLE);
- return FALSE;
+ SetLastError (ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+ pid = process_handle->id;
}
signo = (exitCode == -1) ? SIGKILL : SIGTERM;
- ret = kill (process_handle->id, signo);
+ ret = kill (pid, signo);
if (ret == -1) {
switch (errno) {
case EINVAL:
return (ret == 0);
}
+guint32
+GetPriorityClass (gpointer process)
+{
+#ifdef HAVE_GETPRIORITY
+ struct _WapiHandle_process *process_handle;
+ gboolean ok;
+ int ret;
+ pid_t pid;
+
+ if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle */
+ pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+ } else {
+ ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+ (gpointer *) &process_handle);
+
+ if (!ok) {
+ SetLastError (ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+ pid = process_handle->id;
+ }
+
+ errno = 0;
+ ret = getpriority (PRIO_PROCESS, pid);
+ if (ret == -1 && errno != 0) {
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ SetLastError (ERROR_ACCESS_DENIED);
+ break;
+ case ESRCH:
+ SetLastError (ERROR_PROC_NOT_FOUND);
+ break;
+ default:
+ SetLastError (ERROR_GEN_FAILURE);
+ }
+ return FALSE;
+ }
+
+ if (ret == 0)
+ return NORMAL_PRIORITY_CLASS;
+ else if (ret < -15)
+ return REALTIME_PRIORITY_CLASS;
+ else if (ret < -10)
+ return HIGH_PRIORITY_CLASS;
+ else if (ret < 0)
+ return ABOVE_NORMAL_PRIORITY_CLASS;
+ else if (ret > 10)
+ return IDLE_PRIORITY_CLASS;
+ else if (ret > 0)
+ return BELOW_NORMAL_PRIORITY_CLASS;
+
+ return NORMAL_PRIORITY_CLASS;
+#else
+ SetLastError (ERROR_NOT_SUPPORTED);
+ return 0;
+#endif
+}
+
+gboolean
+SetPriorityClass (gpointer process, guint32 priority_class)
+{
+#ifdef HAVE_SETPRIORITY
+ struct _WapiHandle_process *process_handle;
+ gboolean ok;
+ int ret;
+ int prio;
+ pid_t pid;
+
+ if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+ /* This is a pseudo handle */
+ pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+ } else {
+ ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+ (gpointer *) &process_handle);
+
+ if (!ok) {
+ SetLastError (ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+ pid = process_handle->id;
+ }
+
+ switch (priority_class) {
+ case IDLE_PRIORITY_CLASS:
+ prio = 19;
+ break;
+ case BELOW_NORMAL_PRIORITY_CLASS:
+ prio = 10;
+ break;
+ case NORMAL_PRIORITY_CLASS:
+ prio = 0;
+ break;
+ case ABOVE_NORMAL_PRIORITY_CLASS:
+ prio = -5;
+ break;
+ case HIGH_PRIORITY_CLASS:
+ prio = -11;
+ break;
+ case REALTIME_PRIORITY_CLASS:
+ prio = -20;
+ break;
+ default:
+ SetLastError (ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ ret = setpriority (PRIO_PROCESS, pid, prio);
+ if (ret == -1) {
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ SetLastError (ERROR_ACCESS_DENIED);
+ break;
+ case ESRCH:
+ SetLastError (ERROR_PROC_NOT_FOUND);
+ break;
+ default:
+ SetLastError (ERROR_GEN_FAILURE);
+ }
+ }
+
+ return ret == 0;
+#else
+ SetLastError (ERROR_NOT_SUPPORTED);
+ return FALSE;
+#endif
+}