2 * Server-side process management
4 * Copyright (C) 1998 Alexandre Julliard
24 /* process structure */
28 struct object obj; /* object header */
29 struct process *next; /* system-wide process list */
31 struct thread *thread_list; /* head of the thread list */
32 struct handle_table handles; /* handle table */
33 int exit_code; /* process exit code */
34 int running_threads; /* number of threads running in this process */
35 struct timeval start_time; /* absolute time at process start */
36 struct timeval end_time; /* absolute time at process end */
37 int priority; /* priority class */
38 int affinity; /* process affinity mask */
39 struct object *console_in; /* console input */
40 struct object *console_out; /* console output */
41 struct new_process_request *info; /* startup info (freed after startup) */
44 static struct process initial_process;
45 static struct process *first_process = &initial_process;
46 static int running_processes;
48 /* process operations */
50 static void process_dump( struct object *obj, int verbose );
51 static int process_signaled( struct object *obj, struct thread *thread );
52 static void process_destroy( struct object *obj );
54 static const struct object_ops process_ops =
69 /* initialization of a process structure */
70 static void init_process( struct process *process )
72 init_object( &process->obj, &process_ops, NULL );
75 process->thread_list = NULL;
76 process->exit_code = 0x103; /* STILL_ACTIVE */
77 process->running_threads = 0;
78 process->priority = NORMAL_PRIORITY_CLASS;
79 process->affinity = 1;
80 process->console_in = NULL;
81 process->console_out = NULL;
83 gettimeofday( &process->start_time, NULL );
84 /* alloc a handle for the process itself */
85 alloc_handle( process, process, PROCESS_ALL_ACCESS, 0 );
88 /* create the initial process */
89 struct process *create_initial_process(void)
91 struct new_process_request *info;
93 copy_handle_table( &initial_process, NULL );
94 init_process( &initial_process );
96 if (!alloc_console( &initial_process )) return NULL;
97 if (!(info = mem_alloc( sizeof(*info) ))) return NULL;
98 info->start_flags = STARTF_USESTDHANDLES;
99 info->hstdin = alloc_handle( &initial_process, initial_process.console_in,
100 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
101 info->hstdout = alloc_handle( &initial_process, initial_process.console_out,
102 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
103 info->hstderr = alloc_handle( &initial_process, initial_process.console_out,
104 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 );
105 info->env_ptr = NULL;
106 initial_process.info = info;
107 grab_object( &initial_process ); /* so that we never free it */
108 return &initial_process;
112 /* create a new process */
113 static struct process *create_process( struct new_process_request *req )
115 struct process *process = NULL;
116 struct process *parent = current->process;
118 if (!(process = mem_alloc( sizeof(*process) ))) return NULL;
119 if (!copy_handle_table( process, req->inherit_all ? parent : NULL ))
124 init_process( process );
125 if (parent->console_in) process->console_in = grab_object( parent->console_in );
126 if (parent->console_out) process->console_out = grab_object( parent->console_out );
128 if (!(process->info = mem_alloc( sizeof(*process->info) ))) goto error;
129 memcpy( process->info, req, sizeof(*req) );
131 if (!req->inherit_all && !(req->start_flags & STARTF_USESTDHANDLES))
133 process->info->hstdin = duplicate_handle( parent, req->hstdin, process,
134 0, TRUE, DUPLICATE_SAME_ACCESS );
135 process->info->hstdout = duplicate_handle( parent, req->hstdout, process,
136 0, TRUE, DUPLICATE_SAME_ACCESS );
137 process->info->hstderr = duplicate_handle( parent, req->hstderr, process,
138 0, TRUE, DUPLICATE_SAME_ACCESS );
141 process->next = first_process;
142 first_process->prev = process;
143 first_process = process;
147 release_object( process );
151 /* destroy a process when its refcount is 0 */
152 static void process_destroy( struct object *obj )
154 struct process *process = (struct process *)obj;
155 assert( obj->ops == &process_ops );
156 assert( process != &initial_process );
158 /* we can't have a thread remaining */
159 assert( !process->thread_list );
160 if (process->next) process->next->prev = process->prev;
161 if (process->prev) process->prev->next = process->next;
162 else first_process = process->next;
163 free_console( process );
164 free_handles( process );
165 if (process->info) free( process->info );
166 if (debug_level) memset( process, 0xbb, sizeof(process) ); /* catch errors */
170 /* dump a process on stdout for debugging purposes */
171 static void process_dump( struct object *obj, int verbose )
173 struct process *process = (struct process *)obj;
174 assert( obj->ops == &process_ops );
176 printf( "Process next=%p prev=%p\n", process->next, process->prev );
179 static int process_signaled( struct object *obj, struct thread *thread )
181 struct process *process = (struct process *)obj;
182 return !process->running_threads;
186 /* get a process from an id (and increment the refcount) */
187 struct process *get_process_from_id( void *id )
189 struct process *p = first_process;
190 while (p && (p != id)) p = p->next;
191 if (p) grab_object( p );
192 else SET_ERROR( ERROR_INVALID_PARAMETER );
196 /* get a process from a handle (and increment the refcount) */
197 struct process *get_process_from_handle( int handle, unsigned int access )
199 return (struct process *)get_handle_obj( current->process, handle,
200 access, &process_ops );
203 /* get a pointer to the process handle table */
204 struct handle_table *get_process_handles( struct process *process )
206 return &process->handles;
209 /* retrieve the initialization info for a new process */
210 static int get_process_init_info( struct process *process, struct init_process_reply *reply )
212 struct new_process_request *info;
213 if (!(info = process->info)) return 0;
214 process->info = NULL;
215 reply->start_flags = info->start_flags;
216 reply->hstdin = info->hstdin;
217 reply->hstdout = info->hstdout;
218 reply->hstderr = info->hstderr;
219 reply->env_ptr = info->env_ptr;
224 /* a process has been killed (i.e. its last thread died) */
225 static void process_killed( struct process *process, int exit_code )
227 assert( !process->thread_list );
228 process->exit_code = exit_code;
229 gettimeofday( &process->end_time, NULL );
230 wake_up( &process->obj, 0 );
231 free_handles( process );
234 /* add a thread to a process running threads list */
235 void add_process_thread( struct process *process, struct thread *thread )
237 thread->proc_next = process->thread_list;
238 thread->proc_prev = NULL;
239 if (thread->proc_next) thread->proc_next->proc_prev = thread;
240 process->thread_list = thread;
241 if (!process->running_threads++) running_processes++;
242 grab_object( thread );
245 /* remove a thread from a process running threads list */
246 void remove_process_thread( struct process *process, struct thread *thread )
248 assert( process->running_threads > 0 );
249 assert( process->thread_list );
251 if (thread->proc_next) thread->proc_next->proc_prev = thread->proc_prev;
252 if (thread->proc_prev) thread->proc_prev->proc_next = thread->proc_next;
253 else process->thread_list = thread->proc_next;
255 if (!--process->running_threads)
257 /* we have removed the last running thread, exit the process */
259 process_killed( process, thread->exit_code );
261 release_object( thread );
264 /* kill a process on the spot */
265 static void kill_process( struct process *process, int exit_code )
267 while (process->thread_list)
268 kill_thread( process->thread_list, exit_code );
271 /* get all information about a process */
272 static void get_process_info( struct process *process,
273 struct get_process_info_reply *reply )
275 reply->pid = process;
276 reply->exit_code = process->exit_code;
277 reply->priority = process->priority;
278 reply->process_affinity = process->affinity;
279 reply->system_affinity = 1;
282 /* set all information about a process */
283 static void set_process_info( struct process *process,
284 struct set_process_info_request *req )
286 if (req->mask & SET_PROCESS_INFO_PRIORITY)
287 process->priority = req->priority;
288 if (req->mask & SET_PROCESS_INFO_AFFINITY)
290 if (req->affinity != 1) SET_ERROR( ERROR_INVALID_PARAMETER );
291 else process->affinity = req->affinity;
295 /* allocate a console for this process */
296 int alloc_console( struct process *process )
298 struct object *obj[2];
299 if (process->console_in || process->console_out)
301 SET_ERROR( ERROR_ACCESS_DENIED );
304 if (!create_console( -1, obj )) return 0;
305 process->console_in = obj[0];
306 process->console_out = obj[1];
310 /* free the console for this process */
311 int free_console( struct process *process )
313 if (process->console_in) release_object( process->console_in );
314 if (process->console_out) release_object( process->console_out );
315 process->console_in = process->console_out = NULL;
319 /* get the process console */
320 struct object *get_console( struct process *process, int output )
323 if (!(obj = output ? process->console_out : process->console_in))
325 return grab_object( obj );
328 /* take a snapshot of currently running processes */
329 struct process_snapshot *process_snap( int *count )
331 struct process_snapshot *snapshot, *ptr;
332 struct process *process;
333 if (!running_processes) return NULL;
334 if (!(snapshot = mem_alloc( sizeof(*snapshot) * running_processes )))
337 for (process = first_process; process; process = process->next)
339 if (!process->running_threads) continue;
340 ptr->process = process;
341 ptr->threads = process->running_threads;
342 ptr->priority = process->priority;
343 grab_object( process );
346 *count = running_processes;
350 /* create a new process */
351 DECL_HANDLER(new_process)
353 struct new_process_reply reply;
354 struct process *process;
356 if ((process = create_process( req )))
359 reply.handle = alloc_handle( current->process, process,
360 PROCESS_ALL_ACCESS, req->inherit );
361 release_object( process );
368 send_reply( current, -1, 1, &reply, sizeof(reply) );
371 /* initialize a new process */
372 DECL_HANDLER(init_process)
374 struct init_process_reply reply;
375 if (current->state != RUNNING)
377 fatal_protocol_error( "init_process: init_thread not called yet\n" );
380 if (!get_process_init_info( current->process, &reply ))
382 fatal_protocol_error( "init_process: called twice\n" );
385 send_reply( current, -1, 1, &reply, sizeof(reply) );
388 /* open a handle to a process */
389 DECL_HANDLER(open_process)
391 struct open_process_reply reply = { -1 };
392 struct process *process = get_process_from_id( req->pid );
395 reply.handle = alloc_handle( current->process, process,
396 req->access, req->inherit );
397 release_object( process );
399 send_reply( current, -1, 1, &reply, sizeof(reply) );
402 /* terminate a process */
403 DECL_HANDLER(terminate_process)
405 struct process *process;
407 if ((process = get_process_from_handle( req->handle, PROCESS_TERMINATE )))
409 kill_process( process, req->exit_code );
410 release_object( process );
412 if (current) send_reply( current, -1, 0 );
415 /* fetch information about a process */
416 DECL_HANDLER(get_process_info)
418 struct process *process;
419 struct get_process_info_reply reply = { 0, 0, 0 };
421 if ((process = get_process_from_handle( req->handle, PROCESS_QUERY_INFORMATION )))
423 get_process_info( process, &reply );
424 release_object( process );
426 send_reply( current, -1, 1, &reply, sizeof(reply) );
429 /* set information about a process */
430 DECL_HANDLER(set_process_info)
432 struct process *process;
434 if ((process = get_process_from_handle( req->handle, PROCESS_SET_INFORMATION )))
436 set_process_info( process, req );
437 release_object( process );
439 send_reply( current, -1, 0 );