Rewrote Unix process launching to allow passing startup information to
Winelib apps. Improved handling of execve() failures.
diff --git a/server/process.c b/server/process.c
index c84672d..57825e8 100644
--- a/server/process.c
+++ b/server/process.c
@@ -55,47 +55,54 @@
process_destroy /* destroy */
};
-/* set the process creation info */
-static int set_creation_info( struct process *process, struct new_process_request *req,
- const char *cmd_line, size_t len )
+/* process startup info */
+
+struct startup_info
{
- if (!(process->info = mem_alloc( sizeof(*process->info) + len ))) return 0;
- if (req)
- {
- /* copy the request structure */
- memcpy( process->info, req, sizeof(*req) );
- }
- else /* no request, use defaults */
- {
- req = process->info;
- req->pinherit = 0;
- req->tinherit = 0;
- req->inherit_all = 0;
- req->create_flags = CREATE_NEW_CONSOLE;
- req->start_flags = STARTF_USESTDHANDLES;
- req->exe_file = -1;
- req->hstdin = -1;
- req->hstdout = -1;
- req->hstderr = -1;
- req->cmd_show = 0;
- req->env_ptr = NULL;
- }
- memcpy( process->info->cmdline, cmd_line, len );
- process->info->cmdline[len] = 0;
- process->create_flags = process->info->create_flags;
- return 1;
-}
+ struct object obj; /* object header */
+ int inherit_all; /* inherit all handles from parent */
+ int create_flags; /* creation flags */
+ int start_flags; /* flags from startup info */
+ int exe_file; /* file handle for main exe */
+ int hstdin; /* handle for stdin */
+ int hstdout; /* handle for stdout */
+ int hstderr; /* handle for stderr */
+ int cmd_show; /* main window show mode */
+ struct process *process; /* created process */
+ struct thread *thread; /* created thread */
+};
+
+static void startup_info_dump( struct object *obj, int verbose );
+static int startup_info_signaled( struct object *obj, struct thread *thread );
+static void startup_info_destroy( struct object *obj );
+
+static const struct object_ops startup_info_ops =
+{
+ sizeof(struct startup_info), /* size */
+ startup_info_dump, /* dump */
+ add_queue, /* add_queue */
+ remove_queue, /* remove_queue */
+ startup_info_signaled, /* signaled */
+ no_satisfied, /* satisfied */
+ NULL, /* get_poll_events */
+ NULL, /* poll_event */
+ no_read_fd, /* get_read_fd */
+ no_write_fd, /* get_write_fd */
+ no_flush, /* flush */
+ no_get_file_info, /* get_file_info */
+ startup_info_destroy /* destroy */
+};
+
/* set the console and stdio handles for a newly created process */
-static int set_process_console( struct process *process, struct process *parent )
+static int set_process_console( struct process *process, struct process *parent,
+ struct startup_info *info, struct init_process_request *req )
{
- struct new_process_request *info = process->info;
-
if (process->create_flags & CREATE_NEW_CONSOLE)
{
if (!alloc_console( process )) return 0;
}
- else if (!(process->create_flags & DETACHED_PROCESS))
+ else if (parent && !(process->create_flags & DETACHED_PROCESS))
{
if (parent->console_in) process->console_in = grab_object( parent->console_in );
if (parent->console_out) process->console_out = grab_object( parent->console_out );
@@ -105,31 +112,35 @@
if (!info->inherit_all && !(info->start_flags & STARTF_USESTDHANDLES))
{
/* duplicate the handle from the parent into this process */
- info->hstdin = duplicate_handle( parent, info->hstdin, process,
- 0, TRUE, DUPLICATE_SAME_ACCESS );
- info->hstdout = duplicate_handle( parent, info->hstdout, process,
- 0, TRUE, DUPLICATE_SAME_ACCESS );
- info->hstderr = duplicate_handle( parent, info->hstderr, process,
- 0, TRUE, DUPLICATE_SAME_ACCESS );
+ req->hstdin = duplicate_handle( parent, info->hstdin, process,
+ 0, TRUE, DUPLICATE_SAME_ACCESS );
+ req->hstdout = duplicate_handle( parent, info->hstdout, process,
+ 0, TRUE, DUPLICATE_SAME_ACCESS );
+ req->hstderr = duplicate_handle( parent, info->hstderr, process,
+ 0, TRUE, DUPLICATE_SAME_ACCESS );
+ }
+ else
+ {
+ req->hstdin = info->hstdin;
+ req->hstdout = info->hstdout;
+ req->hstderr = info->hstderr;
}
}
else
{
/* no parent, use handles to the console for stdio */
- info->hstdin = alloc_handle( process, process->console_in,
- GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
- info->hstdout = alloc_handle( process, process->console_out,
- GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
- info->hstderr = alloc_handle( process, process->console_out,
- GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
+ req->hstdin = alloc_handle( process, process->console_in,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
+ req->hstdout = alloc_handle( process, process->console_out,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
+ req->hstderr = alloc_handle( process, process->console_out,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
}
return 1;
}
/* create a new process and its main thread */
-struct thread *create_process( int fd, struct process *parent,
- struct new_process_request *req,
- const char *cmd_line, size_t len )
+struct thread *create_process( int fd )
{
struct process *process;
struct thread *thread = NULL;
@@ -153,7 +164,6 @@
process->console_in = NULL;
process->console_out = NULL;
process->init_event = NULL;
- process->info = NULL;
process->ldt_copy = NULL;
process->ldt_flags = NULL;
process->exe.next = NULL;
@@ -165,54 +175,99 @@
if ((process->next = first_process) != NULL) process->next->prev = process;
first_process = process;
- /* copy the request structure */
- if (!set_creation_info( process, req, cmd_line, len )) goto error;
-
- if (process->info->inherit_all == 2) /* HACK! */
- process->handles = grab_object( parent->handles );
- else if (process->info->inherit_all)
- process->handles = copy_handle_table( process, parent );
- else
- process->handles = alloc_handle_table( process, 0 );
- if (!process->handles) goto error;
-
- /* retrieve the main exe file */
- if (process->info->exe_file != -1)
- {
- if (!(process->exe.file = get_file_obj( parent, process->info->exe_file,
- GENERIC_READ ))) goto error;
- process->info->exe_file = -1;
- }
-
/* create the main thread */
- if (!(thread = create_thread( fd, process, (process->create_flags & CREATE_SUSPENDED) != 0)))
- goto error;
+ if (!(thread = create_thread( fd, process ))) goto error;
/* create the init done event */
if (!(process->init_event = create_event( NULL, 0, 1, 0 ))) goto error;
- /* set the process console */
- if (!set_process_console( process, parent )) goto error;
-
- /* attach to the debugger if requested */
- if (process->create_flags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
- set_process_debugger( process, current );
- else if (parent && parent->debugger && !(parent->create_flags & DEBUG_ONLY_THIS_PROCESS))
- set_process_debugger( process, parent->debugger );
-
add_process_thread( process, thread );
release_object( process );
return thread;
error:
- close( fd );
- free_console( process );
- if (process->handles) release_object( process->handles );
if (thread) release_object( thread );
release_object( process );
return NULL;
}
+/* initialize the current process and fill in the request */
+static void init_process( int ppid, struct init_process_request *req )
+{
+ struct process *process = current->process;
+ struct thread *parent_thread = get_thread_from_pid( ppid );
+ struct process *parent = NULL;
+ struct startup_info *info = NULL;
+
+ if (parent_thread)
+ {
+ parent = parent_thread->process;
+ info = parent_thread->info;
+ if (!info)
+ {
+ fatal_protocol_error( current, "init_process: parent but no info\n" );
+ return;
+ }
+ if (info->thread)
+ {
+ fatal_protocol_error( current, "init_process: called twice?\n" );
+ return;
+ }
+ }
+
+ /* set the process flags */
+ process->create_flags = info ? info->create_flags : CREATE_NEW_CONSOLE;
+
+ /* create the handle table */
+ if (parent && info->inherit_all == 2) /* HACK! */
+ process->handles = grab_object( parent->handles );
+ else if (parent && info->inherit_all)
+ process->handles = copy_handle_table( process, parent );
+ else
+ process->handles = alloc_handle_table( process, 0 );
+ if (!process->handles) goto error;
+
+ /* retrieve the main exe file */
+ req->exe_file = -1;
+ if (parent && info->exe_file != -1)
+ {
+ if (!(process->exe.file = get_file_obj( parent, info->exe_file, GENERIC_READ )))
+ goto error;
+ if ((req->exe_file = alloc_handle( process, process->exe.file, GENERIC_READ, 0 )) == -1)
+ goto error;
+ }
+
+ /* set the process console */
+ if (!set_process_console( process, parent, info, req )) goto error;
+
+ if (parent)
+ {
+ /* attach to the debugger if requested */
+ if (process->create_flags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
+ set_process_debugger( process, parent_thread );
+ else if (parent && parent->debugger && !(parent->create_flags & DEBUG_ONLY_THIS_PROCESS))
+ set_process_debugger( process, parent->debugger );
+ }
+
+ /* thread will be actually suspended in init_done */
+ if (process->create_flags & CREATE_SUSPENDED) current->suspend++;
+
+ if (info)
+ {
+ req->start_flags = info->start_flags;
+ req->cmd_show = info->cmd_show;
+ info->process = (struct process *)grab_object( process );
+ info->thread = (struct thread *)grab_object( current );
+ wake_up( &info->obj, 0 );
+ }
+ else
+ {
+ req->start_flags = STARTF_USESTDHANDLES;
+ req->cmd_show = 0;
+ }
+ error:
+}
+
/* destroy a process when its refcount is 0 */
static void process_destroy( struct object *obj )
{
@@ -224,7 +279,6 @@
if (process->next) process->next->prev = process->prev;
if (process->prev) process->prev->next = process->next;
else first_process = process->next;
- if (process->info) free( process->info );
if (process->init_event) release_object( process->init_event );
if (process->exe.file) release_object( process->exe.file );
}
@@ -247,6 +301,57 @@
}
+static void startup_info_destroy( struct object *obj )
+{
+ struct startup_info *info = (struct startup_info *)obj;
+ assert( obj->ops == &startup_info_ops );
+ if (info->process) release_object( info->process );
+ if (info->thread) release_object( info->thread );
+}
+
+static void startup_info_dump( struct object *obj, int verbose )
+{
+ struct startup_info *info = (struct startup_info *)obj;
+ assert( obj->ops == &startup_info_ops );
+
+ fprintf( stderr, "Startup info flags=%x in=%d out=%d err=%d\n",
+ info->start_flags, info->hstdin, info->hstdout, info->hstderr );
+}
+
+static int startup_info_signaled( struct object *obj, struct thread *thread )
+{
+ struct startup_info *info = (struct startup_info *)obj;
+ return (info->thread != NULL);
+}
+
+
+/* build a reply for the wait_process request */
+static void build_wait_process_reply( struct thread *thread, struct object *obj, int signaled )
+{
+ struct wait_process_request *req = get_req_ptr( thread );
+ if (obj)
+ {
+ struct startup_info *info = (struct startup_info *)obj;
+ assert( obj->ops == &startup_info_ops );
+
+ req->pid = get_process_id( info->process );
+ req->tid = get_thread_id( info->thread );
+ req->phandle = alloc_handle( thread->process, info->process,
+ PROCESS_ALL_ACCESS, req->pinherit );
+ req->thandle = alloc_handle( thread->process, info->thread,
+ THREAD_ALL_ACCESS, req->tinherit );
+ if (info->process->init_event)
+ req->event = alloc_handle( thread->process, info->process->init_event,
+ EVENT_ALL_ACCESS, 0 );
+ else
+ req->event = -1;
+
+ /* FIXME: set_error */
+ }
+ release_object( thread->info );
+ thread->info = NULL;
+}
+
/* get a process from an id (and increment the refcount) */
struct process *get_process_from_id( void *id )
{
@@ -314,7 +419,7 @@
{
assert( !process->thread_list );
gettimeofday( &process->end_time, NULL );
- release_object( process->handles );
+ if (process->handles) release_object( process->handles );
process->handles = NULL;
free_console( process );
while (process->exe.next)
@@ -594,83 +699,83 @@
/* create a new process */
DECL_HANDLER(new_process)
{
- size_t len = get_req_strlen( req, req->cmdline );
- struct thread *thread;
+ struct startup_info *info;
int sock[2];
- int event = -1, phandle = -1;
- req->phandle = -1;
- req->thandle = -1;
- req->event = -1;
- req->pid = NULL;
- req->tid = NULL;
-
- if (socketpair( AF_UNIX, SOCK_STREAM, 0, sock ) == -1)
+ if (current->info)
{
- file_set_error();
+ fatal_protocol_error( current, "new_process: another process is being created\n" );
return;
}
- if (!(thread = create_process( sock[0], current->process, req, req->cmdline, len )))
- goto error;
+ /* build the startup info for a new process */
+ if (!(info = alloc_object( &startup_info_ops, -1 ))) return;
+ info->inherit_all = req->inherit_all;
+ info->create_flags = req->create_flags;
+ info->start_flags = req->start_flags;
+ info->exe_file = req->exe_file;
+ info->hstdin = req->hstdin;
+ info->hstdout = req->hstdout;
+ info->hstderr = req->hstderr;
+ info->cmd_show = req->cmd_show;
+ info->process = NULL;
+ info->thread = NULL;
- if ((event = alloc_handle( current->process, thread->process->init_event,
- EVENT_ALL_ACCESS, 0 )) == -1)
- goto error;
+ if (req->alloc_fd)
+ {
+ if (socketpair( AF_UNIX, SOCK_STREAM, 0, sock ) == -1)
+ {
+ file_set_error();
+ release_object( info );
+ return;
+ }
+ if (!create_process( sock[0] ))
+ {
+ release_object( info );
+ close( sock[1] );
+ return;
+ }
+ /* thread object will be released when the thread gets killed */
+ set_reply_fd( current, sock[1] );
+ }
+ current->info = info;
+}
- if ((phandle = alloc_handle( current->process, thread->process,
- PROCESS_ALL_ACCESS, req->pinherit )) == -1)
- goto error;
-
- if ((req->thandle = alloc_handle( current->process, thread,
- THREAD_ALL_ACCESS, req->tinherit )) == -1)
- goto error;
-
- /* thread object will be released when the thread gets killed */
- set_reply_fd( current, sock[1] );
- req->pid = get_process_id( thread->process );
- req->tid = get_thread_id( thread );
- req->phandle = phandle;
- req->event = event;
- return;
-
- error:
- if (phandle != -1) close_handle( current->process, phandle );
- if (event != -1) close_handle( current->process, event );
- if (thread) release_object( thread );
- close( sock[1] );
+/* Wait for the new process to start */
+DECL_HANDLER(wait_process)
+{
+ if (!current->info)
+ {
+ fatal_protocol_error( current, "wait_process: no process is being created\n" );
+ return;
+ }
+ req->pid = 0;
+ req->tid = 0;
+ req->phandle = -1;
+ req->thandle = -1;
+ if (req->cancel)
+ {
+ release_object( current->info );
+ current->info = NULL;
+ }
+ else
+ {
+ struct object *obj = ¤t->info->obj;
+ sleep_on( 1, &obj, SELECT_TIMEOUT, req->timeout, build_wait_process_reply );
+ }
}
/* initialize a new process */
DECL_HANDLER(init_process)
{
- struct new_process_request *info;
-
if (!current->unix_pid)
{
fatal_protocol_error( current, "init_process: init_thread not called yet\n" );
return;
}
- if (!(info = current->process->info))
- {
- fatal_protocol_error( current, "init_process: called twice\n" );
- return;
- }
current->process->ldt_copy = req->ldt_copy;
current->process->ldt_flags = req->ldt_flags;
- current->process->info = NULL;
- req->exe_file = -1;
- req->start_flags = info->start_flags;
- req->hstdin = info->hstdin;
- req->hstdout = info->hstdout;
- req->hstderr = info->hstderr;
- req->cmd_show = info->cmd_show;
- req->env_ptr = info->env_ptr;
- strcpy( req->cmdline, info->cmdline );
- if (current->process->exe.file)
- req->exe_file = alloc_handle( current->process, current->process->exe.file,
- GENERIC_READ, 0 );
- free( info );
+ init_process( req->ppid, req );
}
/* signal the end of the process initialization */