2 * processes.c: Process handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
12 #include <mono/os/gc_wrapper.h>
13 #include "mono/utils/mono-hash.h"
21 #include <sys/types.h>
24 #include <mono/io-layer/wapi.h>
25 #include <mono/io-layer/unicode.h>
26 #include <mono/io-layer/wapi-private.h>
27 #include <mono/io-layer/handles-private.h>
28 #include <mono/io-layer/misc-private.h>
29 #include <mono/io-layer/mono-mutex.h>
30 #include <mono/io-layer/process-private.h>
31 #include <mono/io-layer/threads.h>
35 static void process_close_shared (gpointer handle);
37 struct _WapiHandleOps _wapi_process_ops = {
38 process_close_shared, /* close_shared */
39 NULL, /* close_private */
45 static mono_once_t process_current_once=MONO_ONCE_INIT;
46 static gpointer current_process=NULL;
48 static mono_once_t process_ops_once=MONO_ONCE_INIT;
50 static void process_ops_init (void)
52 _wapi_handle_register_capabilities (WAPI_HANDLE_PROCESS,
53 WAPI_HANDLE_CAP_WAIT);
56 static void process_close_shared (gpointer handle G_GNUC_UNUSED)
59 struct _WapiHandle_process *process_handle;
62 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
63 (gpointer *)&process_handle, NULL);
65 g_warning (G_GNUC_PRETTY_FUNCTION
66 ": error looking up process handle %p", handle);
70 g_message (G_GNUC_PRETTY_FUNCTION
71 ": closing process handle %p with id %d", handle,
76 gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
77 WapiSecurityAttributes *process_attrs G_GNUC_UNUSED,
78 WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED,
79 gboolean inherit_handles, guint32 create_flags,
80 gpointer environ, const gunichar2 *cwd,
81 WapiStartupInfo *startup,
82 WapiProcessInformation *process_info)
84 gchar *cmd=NULL, *prog, *args=NULL, *dir=NULL;
86 guint32 env=0, stored_dir=0, stored_prog=0, stored_args=0;
87 guint32 env_count=0, i;
89 gpointer stdin_handle, stdout_handle, stderr_handle;
91 gpointer process_handle, thread_handle;
93 mono_once (&process_ops_once, process_ops_init);
95 /* appname and cmdline specify the executable and its args:
97 * If appname is not NULL, it is the name of the executable.
98 * Otherwise the executable is the first token in cmdline.
100 * Executable searching:
102 * If appname is not NULL, it can specify the full path and
103 * file name, or else a partial name and the current directory
104 * will be used. There is no additional searching.
106 * If appname is NULL, the first whitespace-delimited token in
107 * cmdline is used. If the name does not contain a full
108 * directory path, the search sequence is:
110 * 1) The directory containing the current process
111 * 2) The current working directory
112 * 3) The windows system directory (Ignored)
113 * 4) The windows directory (Ignored)
116 * Just to make things more interesting, tokens can contain
117 * white space if they are surrounded by quotation marks. I'm
118 * beginning to understand just why windows apps are generally
119 * so crap, with an API like this :-(
122 cmd=_wapi_unicode_to_utf8 (appname);
125 g_message (G_GNUC_PRETTY_FUNCTION
126 ": unicode conversion returned NULL");
132 /* Turn all the slashes round the right way */
133 for(i=0; i<strlen (cmd); i++) {
141 args=_wapi_unicode_to_utf8 (cmdline);
144 g_message (G_GNUC_PRETTY_FUNCTION
145 ": unicode conversion returned NULL");
151 /* Turn all the slashes round the right way */
152 for(i=0; i<strlen (args); i++) {
160 dir=_wapi_unicode_to_utf8 (cwd);
163 g_message (G_GNUC_PRETTY_FUNCTION
164 ": unicode conversion returned NULL");
170 /* Turn all the slashes round the right way */
171 for(i=0; i<strlen (dir); i++) {
176 stored_dir=_wapi_handle_scratch_store (dir, strlen (dir));
179 /* environ is a block of NULL-terminated strings, which is
180 * itself NULL-terminated. Of course, passing an array of
181 * string pointers would have made things too easy :-(
183 /* Not sure whether I should turn the w32 env block into
184 * proper env vars, or just leave it to be read back by other
185 * w32 emulation functions.
188 /* env_count counts bytes, not chars */
189 for(environp=(gunichar2 *)environ; *environp;
190 env_count+=2, environp++) {
197 env=_wapi_handle_scratch_store (environ, env_count);
200 /* We can't put off locating the executable any longer :-( */
202 if(g_ascii_isalpha (cmd[0]) && (cmd[1]==':')) {
203 /* Strip off the drive letter. I can't
204 * believe that CP/M holdover is still
207 memmove (cmd, cmd+2, strlen (cmd)-2);
208 cmd[strlen (cmd)-2]='\0';
212 /* Assume full path given */
215 /* Search for file named by cmd in the current
218 char *curdir=g_get_current_dir ();
220 prog=g_strdup_printf ("%s/%s", curdir, cmd);
226 /* Dig out the first token from args, taking quotation
230 /* FIXME: move the contents of args down when token
231 * has been set (otherwise argv[0] is duplicated)
233 /* Assume the opening quote will always be the first
237 for(i=1; args[i]!='\0' && args[i]!='\"'; i++);
238 if(g_ascii_isspace (args[i+1])) {
239 /* We found the first token */
240 token=g_strndup (args+1, i-1);
242 /* Quotation mark appeared in the
243 * middle of the token. Just give the
244 * whole first token, quotes and all,
251 /* No quote mark, or malformed */
252 for(i=0; args[i]!='\0'; i++) {
253 if(g_ascii_isspace (args[i])) {
254 token=g_strndup (args, i);
260 if(token==NULL && args[0]!='\0') {
261 /* Must be just one token in the string */
262 token=g_strdup (args);
268 g_message (G_GNUC_PRETTY_FUNCTION
269 ": Couldn't find what to exec");
275 if(g_ascii_isalpha (token[0]) && (token[1]==':')) {
276 /* Strip off the drive letter. I can't
277 * believe that CP/M holdover is still
280 memmove (token, token+2, strlen (token)-2);
281 token[strlen (token)-2]='\0';
285 /* Assume full path given */
286 prog=g_strdup (token);
288 char *curdir=g_get_current_dir ();
290 /* FIXME: Need to record the directory
291 * containing the current process, and check
292 * that for the new executable as the first
296 prog=g_strdup_printf ("%s/%s", curdir, token);
299 /* I assume X_OK is the criterion to use,
302 if(access (prog, X_OK)!=0) {
304 prog=g_find_program_in_path (token);
307 g_message (G_GNUC_PRETTY_FUNCTION ": Couldn't find executable %s", token);
320 g_message (G_GNUC_PRETTY_FUNCTION ": Exec prog [%s] args [%s]",
324 stored_prog=_wapi_handle_scratch_store (prog, strlen (prog));
325 stored_args=_wapi_handle_scratch_store (args, strlen (args));
327 stdin_handle=GetStdHandle (STD_INPUT_HANDLE);
328 stdout_handle=GetStdHandle (STD_OUTPUT_HANDLE);
329 stderr_handle=GetStdHandle (STD_ERROR_HANDLE);
332 if(startup->dwFlags & STARTF_USESTDHANDLES) {
333 stdin_handle=startup->hStdInput;
334 stdout_handle=startup->hStdOutput;
335 stderr_handle=startup->hStdError;
339 ret=_wapi_handle_process_fork (stored_prog, stored_args, env,
340 stored_dir, inherit_handles,
341 create_flags, stdin_handle,
342 stdout_handle, stderr_handle,
343 &process_handle, &thread_handle, &pid,
346 if(ret==TRUE && process_info!=NULL) {
347 process_info->hProcess=process_handle;
348 process_info->hThread=thread_handle;
349 process_info->dwProcessId=pid;
350 process_info->dwThreadId=tid;
361 _wapi_handle_scratch_delete (stored_prog);
367 _wapi_handle_scratch_delete (stored_args);
373 _wapi_handle_scratch_delete (stored_dir);
376 _wapi_handle_scratch_delete (env);
382 static gboolean process_compare (gpointer handle, gpointer user_data)
384 struct _WapiHandle_process *process_handle;
388 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
389 (gpointer *)&process_handle, NULL);
391 g_warning (G_GNUC_PRETTY_FUNCTION
392 ": error looking up process handle %p", handle);
396 pid=GPOINTER_TO_UINT (user_data);
397 if(process_handle->id==pid) {
404 static void process_set_current (void)
406 struct _WapiHandle_process *process_handle;
410 current_process=_wapi_search_handle (WAPI_HANDLE_PROCESS,
412 GUINT_TO_POINTER (pid),
413 (gpointer *)&process_handle,
415 if(current_process==0) {
417 g_message (G_GNUC_PRETTY_FUNCTION
418 ": Need to create my own process handle");
421 current_process=_wapi_handle_new (WAPI_HANDLE_PROCESS);
422 if(current_process==_WAPI_HANDLE_INVALID) {
423 g_warning (G_GNUC_PRETTY_FUNCTION
424 ": error creating process handle");
428 ok=_wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS,
429 (gpointer *)&process_handle, NULL);
431 g_warning (G_GNUC_PRETTY_FUNCTION
432 ": error looking up process handle %p",
437 process_handle->id=pid;
440 g_message (G_GNUC_PRETTY_FUNCTION ": Found my process handle");
445 /* Returns a pseudo handle that doesn't need to be closed afterwards */
446 gpointer GetCurrentProcess (void)
448 mono_once (&process_current_once, process_set_current);
450 return((gpointer)-1);
453 guint32 GetCurrentProcessId (void)
455 mono_once (&process_current_once, process_set_current);
460 static gboolean process_enum (gpointer handle, gpointer user_data)
462 GPtrArray *processes=user_data;
464 g_ptr_array_add (processes, handle);
466 /* Return false to keep searching */
470 gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
472 GPtrArray *processes=g_ptr_array_new ();
475 _wapi_search_handle (WAPI_HANDLE_PROCESS, process_enum, processes,
478 fit=len/sizeof(guint32);
479 for(i=0; i<fit && i<processes->len; i++) {
480 struct _WapiHandle_process *process_handle;
483 ok=_wapi_lookup_handle (g_ptr_array_index (processes, i),
485 (gpointer *)&process_handle, NULL);
487 g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up process handle %p", g_ptr_array_index (processes, i));
488 g_ptr_array_free (processes, FALSE);
492 pids[i]=process_handle->id;
495 g_ptr_array_free (processes, FALSE);
497 *needed=i*sizeof(guint32);
502 static gboolean process_open_compare (gpointer handle, gpointer user_data)
504 struct _WapiHandle_process *process_handle;
508 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
509 (gpointer *)&process_handle, NULL);
511 g_warning (G_GNUC_PRETTY_FUNCTION
512 ": error looking up process handle %p", handle);
516 pid=GPOINTER_TO_UINT (user_data);
518 /* It's possible to have more than one process handle with the
519 * same pid, but only the one running process can be
522 if(process_handle->id==pid &&
523 _wapi_handle_issignalled (handle)==FALSE) {
530 gpointer OpenProcess (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
532 /* Find the process handle that corresponds to pid */
535 mono_once (&process_current_once, process_set_current);
537 handle=_wapi_search_handle (WAPI_HANDLE_PROCESS, process_open_compare,
538 GUINT_TO_POINTER (pid), NULL, NULL);
541 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find pid %d", pid);
544 /* Set an error code */
549 _wapi_handle_ref (handle);
554 gboolean GetExitCodeProcess (gpointer process, guint32 *code)
556 struct _WapiHandle_process *process_handle;
559 mono_once (&process_current_once, process_set_current);
565 ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
566 (gpointer *)&process_handle, NULL);
569 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
576 /* A process handle is only signalled if the process has exited */
577 if(_wapi_handle_issignalled (process)==TRUE) {
578 *code=process_handle->exitstatus;
586 gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
587 WapiFileTime *exit_time, WapiFileTime *kernel_time,
588 WapiFileTime *user_time)
590 struct _WapiHandle_process *process_handle;
593 mono_once (&process_current_once, process_set_current);
595 if(create_time==NULL || exit_time==NULL || kernel_time==NULL ||
597 /* Not sure if w32 allows NULLs here or not */
601 ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
602 (gpointer *)&process_handle, NULL);
605 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
612 *create_time=process_handle->create_time;
614 /* A process handle is only signalled if the process has
615 * exited. Otherwise exit_time isn't set
617 if(_wapi_handle_issignalled (process)==TRUE) {
618 *exit_time=process_handle->exit_time;