/* * processes.c: Process handles * * Author: * Dick Porter (dick@ximian.com) * * (C) 2002 Ximian, Inc. */ #include #if HAVE_BOEHM_GC #include #include "mono/utils/mono-hash.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* The process' environment strings */ extern char **environ; #undef DEBUG static void process_close_shared (gpointer handle); struct _WapiHandleOps _wapi_process_ops = { process_close_shared, /* close_shared */ NULL, /* close_private */ NULL, /* signal */ NULL, /* own */ NULL, /* is_owned */ }; static mono_once_t process_current_once=MONO_ONCE_INIT; static gpointer current_process=NULL; static mono_once_t process_ops_once=MONO_ONCE_INIT; static void process_ops_init (void) { _wapi_handle_register_capabilities (WAPI_HANDLE_PROCESS, WAPI_HANDLE_CAP_WAIT); } static void process_close_shared (gpointer handle G_GNUC_UNUSED) { struct _WapiHandle_process *process_handle; gboolean ok; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS, (gpointer *)&process_handle, NULL); if(ok==FALSE) { g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up process handle %p", handle); return; } #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": closing process handle %p with id %d", handle, process_handle->id); #endif if(process_handle->proc_name!=0) { _wapi_handle_scratch_delete (process_handle->proc_name); process_handle->proc_name=0; } } gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline, WapiSecurityAttributes *process_attrs G_GNUC_UNUSED, WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED, gboolean inherit_handles, guint32 create_flags, gpointer new_environ, const gunichar2 *cwd, WapiStartupInfo *startup, WapiProcessInformation *process_info) { gchar *cmd=NULL, *prog = NULL, *full_prog = NULL, *args=NULL, *args_after_prog=NULL, *dir=NULL; guint32 env=0, stored_dir=0, stored_prog=0, i; gboolean ret=FALSE; gpointer stdin_handle, stdout_handle, stderr_handle; guint32 pid, tid; gpointer process_handle, thread_handle; mono_once (&process_ops_once, process_ops_init); /* appname and cmdline specify the executable and its args: * * If appname is not NULL, it is the name of the executable. * Otherwise the executable is the first token in cmdline. * * Executable searching: * * If appname is not NULL, it can specify the full path and * file name, or else a partial name and the current directory * will be used. There is no additional searching. * * If appname is NULL, the first whitespace-delimited token in * cmdline is used. If the name does not contain a full * directory path, the search sequence is: * * 1) The directory containing the current process * 2) The current working directory * 3) The windows system directory (Ignored) * 4) The windows directory (Ignored) * 5) $PATH * * Just to make things more interesting, tokens can contain * white space if they are surrounded by quotation marks. I'm * beginning to understand just why windows apps are generally * so crap, with an API like this :-( */ if(appname!=NULL) { cmd=_wapi_unicode_to_utf8 (appname); if(cmd==NULL) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": unicode conversion returned NULL"); #endif SetLastError(ERROR_PATH_NOT_FOUND); goto cleanup; } /* Turn all the slashes round the right way */ for(i=0; idwFlags & STARTF_USESTDHANDLES) { stdin_handle=startup->hStdInput; stdout_handle=startup->hStdOutput; stderr_handle=startup->hStdError; } else { stdin_handle=GetStdHandle (STD_INPUT_HANDLE); stdout_handle=GetStdHandle (STD_OUTPUT_HANDLE); stderr_handle=GetStdHandle (STD_ERROR_HANDLE); } ret=_wapi_handle_process_fork (stored_prog, env, stored_dir, inherit_handles, create_flags, stdin_handle, stdout_handle, stderr_handle, &process_handle, &thread_handle, &pid, &tid); if(ret==TRUE && process_info!=NULL) { process_info->hProcess=process_handle; process_info->hThread=thread_handle; process_info->dwProcessId=pid; process_info->dwThreadId=tid; } else if(ret==FALSE) { /* FIXME: work out a better error code */ SetLastError(ERROR_PATH_NOT_FOUND); } cleanup: if(cmd!=NULL) { g_free (cmd); } if(full_prog!=NULL) { g_free (prog); } if(stored_prog!=0) { _wapi_handle_scratch_delete (stored_prog); } if(args!=NULL) { g_free (args); } if(dir!=NULL) { g_free (dir); } if(stored_dir!=0) { _wapi_handle_scratch_delete (stored_dir); } if(env!=0) { _wapi_handle_scratch_delete_string_array (env); } return(ret); } static gboolean process_compare (gpointer handle, gpointer user_data) { struct _WapiHandle_process *process_handle; gboolean ok; pid_t pid; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS, (gpointer *)&process_handle, NULL); if(ok==FALSE) { g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up process handle %p", handle); return(FALSE); } pid=GPOINTER_TO_UINT (user_data); if(process_handle->id==pid) { return(TRUE); } else { return(FALSE); } } static void process_set_current (void) { struct _WapiHandle_process *process_handle; gboolean ok; pid_t pid=getpid (); current_process=_wapi_search_handle (WAPI_HANDLE_PROCESS, process_compare, GUINT_TO_POINTER (pid), (gpointer *)&process_handle, NULL); if(current_process==0) { gchar *progname, *slash; #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Need to create my own process handle"); #endif current_process=_wapi_handle_new (WAPI_HANDLE_PROCESS); if(current_process==_WAPI_HANDLE_INVALID) { g_warning (G_GNUC_PRETTY_FUNCTION ": error creating process handle"); return; } ok=_wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS, (gpointer *)&process_handle, NULL); if(ok==FALSE) { g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up process handle %p", current_process); return; } process_handle->id=pid; /* These seem to be the defaults on w2k */ process_handle->min_working_set=204800; process_handle->max_working_set=1413120; progname=g_get_prgname (); #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": using [%s] as prog name", progname); #endif if(progname!=NULL) { slash=strrchr (progname, '/'); if(slash!=NULL) { process_handle->proc_name=_wapi_handle_scratch_store (slash+1, strlen (slash+1)); } else { process_handle->proc_name=_wapi_handle_scratch_store (progname, strlen (progname)); } } /* Make sure the new handle has a reference so it wont go away * until this process exits */ _wapi_handle_ref (current_process); } else { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Found my process handle"); #endif } } /* Returns a pseudo handle that doesn't need to be closed afterwards */ gpointer GetCurrentProcess (void) { mono_once (&process_current_once, process_set_current); return((gpointer)-1); } guint32 GetCurrentProcessId (void) { struct _WapiHandle_process *current_process_handle; gboolean ok; mono_once (&process_current_once, process_set_current); ok=_wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS, (gpointer *)¤t_process_handle, NULL); if(ok==FALSE) { g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up current process handle %p", current_process); /* No failure return is defined. PID 0 is invalid. * This should only be reached when something else has * gone badly wrong anyway. */ return(0); } return(current_process_handle->id); } static gboolean process_enum (gpointer handle, gpointer user_data) { GPtrArray *processes=user_data; /* Ignore processes that have already exited (ie they are signalled) */ if(_wapi_handle_issignalled (handle)==FALSE) { g_ptr_array_add (processes, handle); } /* Return false to keep searching */ return(FALSE); } gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed) { GPtrArray *processes=g_ptr_array_new (); guint32 fit, i; mono_once (&process_current_once, process_set_current); _wapi_search_handle (WAPI_HANDLE_PROCESS, process_enum, processes, NULL, NULL); fit=len/sizeof(guint32); for(i=0; ilen; i++) { struct _WapiHandle_process *process_handle; gboolean ok; ok=_wapi_lookup_handle (g_ptr_array_index (processes, i), WAPI_HANDLE_PROCESS, (gpointer *)&process_handle, NULL); if(ok==FALSE) { g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up process handle %p", g_ptr_array_index (processes, i)); g_ptr_array_free (processes, FALSE); return(FALSE); } pids[i]=process_handle->id; } g_ptr_array_free (processes, FALSE); *needed=i*sizeof(guint32); return(TRUE); } static gboolean process_open_compare (gpointer handle, gpointer user_data) { struct _WapiHandle_process *process_handle; gboolean ok; pid_t pid; ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS, (gpointer *)&process_handle, NULL); if(ok==FALSE) { g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up process handle %p", handle); return(FALSE); } pid=GPOINTER_TO_UINT (user_data); /* It's possible to have more than one process handle with the * same pid, but only the one running process can be * unsignalled */ if(process_handle->id==pid && _wapi_handle_issignalled (handle)==FALSE) { return(TRUE); } else { return(FALSE); } } gpointer OpenProcess (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid) { /* Find the process handle that corresponds to pid */ gpointer handle; mono_once (&process_current_once, process_set_current); handle=_wapi_search_handle (WAPI_HANDLE_PROCESS, process_open_compare, GUINT_TO_POINTER (pid), NULL, NULL); if(handle==0) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Can't find pid %d", pid); #endif /* Set an error code */ return(NULL); } _wapi_handle_ref (handle); return(handle); } gboolean GetExitCodeProcess (gpointer process, guint32 *code) { struct _WapiHandle_process *process_handle; gboolean ok; mono_once (&process_current_once, process_set_current); if(code==NULL) { return(FALSE); } ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS, (gpointer *)&process_handle, NULL); if(ok==FALSE) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p", process); #endif return(FALSE); } /* A process handle is only signalled if the process has exited */ if(_wapi_handle_issignalled (process)==TRUE) { *code=process_handle->exitstatus; } else { *code=STILL_ACTIVE; } return(TRUE); } gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time, WapiFileTime *exit_time, WapiFileTime *kernel_time, WapiFileTime *user_time) { struct _WapiHandle_process *process_handle; gboolean ok; mono_once (&process_current_once, process_set_current); if(create_time==NULL || exit_time==NULL || kernel_time==NULL || user_time==NULL) { /* Not sure if w32 allows NULLs here or not */ return(FALSE); } ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS, (gpointer *)&process_handle, NULL); if(ok==FALSE) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p", process); #endif return(FALSE); } *create_time=process_handle->create_time; /* A process handle is only signalled if the process has * exited. Otherwise exit_time isn't set */ if(_wapi_handle_issignalled (process)==TRUE) { *exit_time=process_handle->exit_time; } return(TRUE); } gboolean EnumProcessModules (gpointer process, gpointer *modules, guint32 size, guint32 *needed) { /* 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 * so that the simple implementation can just return one item * for now.) Get the info from /proc//maps on linux, * other systems will have to implement /dev/kmem reading or * whatever other horrid technique is needed. */ if(sizeid; procname_utf8=_wapi_handle_scratch_lookup_as_string (process_handle->proc_name); #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Process name is [%s]", procname_utf8); #endif procname=g_utf8_to_utf16 (procname_utf8, -1, NULL, &len, NULL); if(procname==NULL) { /* bugger */ g_free (procname_utf8); return(0); } /* Add the terminator, and convert chars to bytes */ bytes=(len+1)*2; if(size/maps */ } return(0); } gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max) { struct _WapiHandle_process *process_handle; gboolean ok; mono_once (&process_current_once, process_set_current); if(min==NULL || max==NULL) { /* Not sure if w32 allows NULLs here or not */ return(FALSE); } ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS, (gpointer *)&process_handle, NULL); if(ok==FALSE) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p", process); #endif return(FALSE); } *min=process_handle->min_working_set; *max=process_handle->max_working_set; return(TRUE); } gboolean SetProcessWorkingSetSize (gpointer process, size_t min, size_t max) { struct _WapiHandle_process *process_handle; gboolean ok; mono_once (&process_current_once, process_set_current); ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS, (gpointer *)&process_handle, NULL); if(ok==FALSE) { #ifdef DEBUG g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p", process); #endif return(FALSE); } process_handle->min_working_set=min; process_handle->max_working_set=max; return(TRUE); }