2 * Server-side process management
4 * Copyright (C) 1998 Alexandre Julliard
20 #include "server/process.h"
21 #include "server/thread.h"
23 /* reserved handle access rights */
24 #define RESERVED_SHIFT 25
25 #define RESERVED_INHERIT (HANDLE_FLAG_INHERIT << RESERVED_SHIFT)
26 #define RESERVED_CLOSE_PROTECT (HANDLE_FLAG_PROTECT_FROM_CLOSE << RESERVED_SHIFT)
27 #define RESERVED_ALL (RESERVED_INHERIT | RESERVED_CLOSE_PROTECT)
29 /* global handle macros */
30 #define HANDLE_OBFUSCATOR 0x544a4def
31 #define HANDLE_IS_GLOBAL(h) (((h) ^ HANDLE_OBFUSCATOR) < 0x10000)
32 #define HANDLE_LOCAL_TO_GLOBAL(h) ((h) ^ HANDLE_OBFUSCATOR)
33 #define HANDLE_GLOBAL_TO_LOCAL(h) ((h) ^ HANDLE_OBFUSCATOR)
41 /* process structure */
44 struct object obj
; /* object header */
45 struct process
*next
; /* system-wide process list */
47 struct thread
*thread_list
; /* head of the thread list */
48 struct handle_entry
*entries
; /* handle entry table */
49 int handle_count
; /* nb of allocated handle entries */
50 int handle_last
; /* last used handle entry */
51 int exit_code
; /* process exit code */
52 int running_threads
; /* number of threads running in this process */
53 struct timeval start_time
; /* absolute time at process start */
54 struct timeval end_time
; /* absolute time at process end */
55 int priority
; /* priority class */
56 int affinity
; /* process affinity mask */
57 struct object
*console_in
; /* console input */
58 struct object
*console_out
; /* console output */
59 struct new_process_request
*info
; /* startup info (freed after startup) */
63 static struct process initial_process
;
64 static struct process
*first_process
= &initial_process
;
65 static int running_processes
;
67 #define MIN_HANDLE_ENTRIES 32
69 /* process operations */
71 static void process_dump( struct object
*obj
, int verbose
);
72 static int process_signaled( struct object
*obj
, struct thread
*thread
);
73 static void process_destroy( struct object
*obj
);
74 static void free_handles( struct process
*process
);
75 static int copy_handle_table( struct process
*process
, struct process
*parent
);
77 static const struct object_ops process_ops
=
92 /* initialization of a process structure */
93 static void init_process( struct process
*process
)
95 init_object( &process
->obj
, &process_ops
, NULL
);
98 process
->thread_list
= NULL
;
99 process
->exit_code
= 0x103; /* STILL_ACTIVE */
100 process
->running_threads
= 0;
101 process
->priority
= NORMAL_PRIORITY_CLASS
;
102 process
->affinity
= 1;
103 process
->console_in
= NULL
;
104 process
->console_out
= NULL
;
105 process
->info
= NULL
;
106 gettimeofday( &process
->start_time
, NULL
);
107 /* alloc a handle for the process itself */
108 alloc_handle( process
, process
, PROCESS_ALL_ACCESS
, 0 );
111 /* create the initial process */
112 struct process
*create_initial_process(void)
114 struct new_process_request
*info
;
116 copy_handle_table( &initial_process
, NULL
);
117 init_process( &initial_process
);
119 if (!alloc_console( &initial_process
)) return NULL
;
120 if (!(info
= mem_alloc( sizeof(*info
) ))) return NULL
;
121 info
->start_flags
= STARTF_USESTDHANDLES
;
122 info
->hstdin
= alloc_handle( &initial_process
, initial_process
.console_in
,
123 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
, 1 );
124 info
->hstdout
= alloc_handle( &initial_process
, initial_process
.console_out
,
125 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
, 1 );
126 info
->hstderr
= alloc_handle( &initial_process
, initial_process
.console_out
,
127 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
, 1 );
128 info
->env_ptr
= NULL
;
129 initial_process
.info
= info
;
130 grab_object( &initial_process
); /* so that we never free it */
131 return &initial_process
;
135 /* create a new process */
136 struct process
*create_process( struct new_process_request
*req
)
138 struct process
*process
= NULL
;
139 struct process
*parent
= current
->process
;
141 if (!(process
= mem_alloc( sizeof(*process
) ))) return NULL
;
142 if (!copy_handle_table( process
, req
->inherit_all
? parent
: NULL
))
147 init_process( process
);
148 if (parent
->console_in
) process
->console_in
= grab_object( parent
->console_in
);
149 if (parent
->console_out
) process
->console_out
= grab_object( parent
->console_out
);
151 if (!(process
->info
= mem_alloc( sizeof(*process
->info
) ))) goto error
;
152 memcpy( process
->info
, req
, sizeof(*req
) );
154 if (!req
->inherit_all
&& !(req
->start_flags
& STARTF_USESTDHANDLES
))
156 process
->info
->hstdin
= duplicate_handle( parent
, req
->hstdin
, process
,
157 0, TRUE
, DUPLICATE_SAME_ACCESS
);
158 process
->info
->hstdout
= duplicate_handle( parent
, req
->hstdout
, process
,
159 0, TRUE
, DUPLICATE_SAME_ACCESS
);
160 process
->info
->hstderr
= duplicate_handle( parent
, req
->hstderr
, process
,
161 0, TRUE
, DUPLICATE_SAME_ACCESS
);
164 process
->next
= first_process
;
165 first_process
->prev
= process
;
166 first_process
= process
;
170 release_object( process
);
174 /* destroy a process when its refcount is 0 */
175 static void process_destroy( struct object
*obj
)
177 struct process
*process
= (struct process
*)obj
;
178 assert( obj
->ops
== &process_ops
);
179 assert( process
!= &initial_process
);
181 /* we can't have a thread remaining */
182 assert( !process
->thread_list
);
183 if (process
->next
) process
->next
->prev
= process
->prev
;
184 if (process
->prev
) process
->prev
->next
= process
->next
;
185 else first_process
= process
->next
;
186 free_console( process
);
187 free_handles( process
);
188 if (process
->info
) free( process
->info
);
189 if (debug_level
) memset( process
, 0xbb, sizeof(process
) ); /* catch errors */
193 /* dump a process on stdout for debugging purposes */
194 static void process_dump( struct object
*obj
, int verbose
)
196 struct process
*process
= (struct process
*)obj
;
197 assert( obj
->ops
== &process_ops
);
199 printf( "Process next=%p prev=%p\n", process
->next
, process
->prev
);
202 static int process_signaled( struct object
*obj
, struct thread
*thread
)
204 struct process
*process
= (struct process
*)obj
;
205 return !process
->running_threads
;
209 /* get a process from an id (and increment the refcount) */
210 struct process
*get_process_from_id( void *id
)
212 struct process
*p
= first_process
;
213 while (p
&& (p
!= id
)) p
= p
->next
;
214 if (p
) grab_object( p
);
215 else SET_ERROR( ERROR_INVALID_PARAMETER
);
219 /* get a process from a handle (and increment the refcount) */
220 struct process
*get_process_from_handle( int handle
, unsigned int access
)
222 return (struct process
*)get_handle_obj( current
->process
, handle
,
223 access
, &process_ops
);
226 /* retrieve the initialization info for a new process */
227 int get_process_init_info( struct process
*process
, struct init_process_reply
*reply
)
229 struct new_process_request
*info
;
230 if (!(info
= process
->info
)) return 0;
231 process
->info
= NULL
;
232 reply
->start_flags
= info
->start_flags
;
233 reply
->hstdin
= info
->hstdin
;
234 reply
->hstdout
= info
->hstdout
;
235 reply
->hstderr
= info
->hstderr
;
236 reply
->env_ptr
= info
->env_ptr
;
241 /* a process has been killed (i.e. its last thread died) */
242 static void process_killed( struct process
*process
, int exit_code
)
244 assert( !process
->thread_list
);
245 process
->exit_code
= exit_code
;
246 gettimeofday( &process
->end_time
, NULL
);
247 wake_up( &process
->obj
, 0 );
248 free_handles( process
);
251 /* free the process handle entries */
252 static void free_handles( struct process
*process
)
254 struct handle_entry
*entry
;
257 if (!(entry
= process
->entries
)) return;
258 for (handle
= 0; handle
<= process
->handle_last
; handle
++, entry
++)
260 struct object
*obj
= entry
->ptr
;
262 if (obj
) release_object( obj
);
264 free( process
->entries
);
265 process
->handle_count
= 0;
266 process
->handle_last
= -1;
267 process
->entries
= NULL
;
270 /* add a thread to a process running threads list */
271 void add_process_thread( struct process
*process
, struct thread
*thread
)
273 thread
->proc_next
= process
->thread_list
;
274 thread
->proc_prev
= NULL
;
275 if (thread
->proc_next
) thread
->proc_next
->proc_prev
= thread
;
276 process
->thread_list
= thread
;
277 if (!process
->running_threads
++) running_processes
++;
278 grab_object( thread
);
281 /* remove a thread from a process running threads list */
282 void remove_process_thread( struct process
*process
, struct thread
*thread
)
284 assert( process
->running_threads
> 0 );
285 assert( process
->thread_list
);
287 if (thread
->proc_next
) thread
->proc_next
->proc_prev
= thread
->proc_prev
;
288 if (thread
->proc_prev
) thread
->proc_prev
->proc_next
= thread
->proc_next
;
289 else process
->thread_list
= thread
->proc_next
;
291 if (!--process
->running_threads
)
293 /* we have removed the last running thread, exit the process */
295 process_killed( process
, thread
->exit_code
);
297 release_object( thread
);
300 /* grow a handle table */
301 /* return 1 if OK, 0 on error */
302 static int grow_handle_table( struct process
*process
)
304 struct handle_entry
*new_entries
;
305 int count
= process
->handle_count
;
307 if (count
>= INT_MAX
/ 2) return 0;
309 if (!(new_entries
= realloc( process
->entries
, count
* sizeof(struct handle_entry
) )))
311 SET_ERROR( ERROR_OUTOFMEMORY
);
314 process
->handle_count
= count
;
315 process
->entries
= new_entries
;
319 /* allocate a handle for an object, incrementing its refcount */
320 /* return the handle, or -1 on error */
321 int alloc_handle( struct process
*process
, void *obj
, unsigned int access
,
324 struct handle_entry
*entry
;
327 assert( !(access
& RESERVED_ALL
) );
328 if (inherit
) access
|= RESERVED_INHERIT
;
330 /* find the first free entry */
332 if (!(entry
= process
->entries
)) return -1;
333 for (handle
= 0; handle
<= process
->handle_last
; handle
++, entry
++)
334 if (!entry
->ptr
) goto found
;
336 if (handle
>= process
->handle_count
)
338 if (!grow_handle_table( process
)) return -1;
339 entry
= process
->entries
+ handle
; /* the table may have moved */
341 process
->handle_last
= handle
;
344 entry
->ptr
= grab_object( obj
);
345 entry
->access
= access
;
346 return handle
+ 1; /* avoid handle 0 */
349 /* return an handle entry, or NULL if the handle is invalid */
350 static struct handle_entry
*get_handle( struct process
*process
, int handle
)
352 struct handle_entry
*entry
;
354 if (HANDLE_IS_GLOBAL(handle
))
356 handle
= HANDLE_GLOBAL_TO_LOCAL(handle
);
357 process
= &initial_process
;
359 handle
--; /* handles start at 1 */
360 if ((handle
< 0) || (handle
> process
->handle_last
)) goto error
;
361 entry
= process
->entries
+ handle
;
362 if (!entry
->ptr
) goto error
;
366 SET_ERROR( ERROR_INVALID_HANDLE
);
370 /* attempt to shrink a table */
371 /* return 1 if OK, 0 on error */
372 static int shrink_handle_table( struct process
*process
)
374 struct handle_entry
*new_entries
;
375 struct handle_entry
*entry
= process
->entries
+ process
->handle_last
;
376 int count
= process
->handle_count
;
378 while (process
->handle_last
>= 0)
380 if (entry
->ptr
) break;
381 process
->handle_last
--;
384 if (process
->handle_last
>= count
/ 4) return 1; /* no need to shrink */
385 if (count
< MIN_HANDLE_ENTRIES
* 2) return 1; /* too small to shrink */
387 if (!(new_entries
= realloc( process
->entries
,
388 count
* sizeof(struct handle_entry
) )))
390 process
->handle_count
= count
;
391 process
->entries
= new_entries
;
395 /* copy the handle table of the parent process */
396 /* return 1 if OK, 0 on error */
397 static int copy_handle_table( struct process
*process
, struct process
*parent
)
399 struct handle_entry
*ptr
;
402 if (!parent
) /* first process */
404 count
= MIN_HANDLE_ENTRIES
;
409 assert( parent
->entries
);
410 count
= parent
->handle_count
;
411 last
= parent
->handle_last
;
414 if (!(ptr
= mem_alloc( count
* sizeof(struct handle_entry
)))) return 0;
415 process
->entries
= ptr
;
416 process
->handle_count
= count
;
417 process
->handle_last
= last
;
421 memcpy( ptr
, parent
->entries
, (last
+ 1) * sizeof(struct handle_entry
) );
422 for (i
= 0; i
<= last
; i
++, ptr
++)
424 if (!ptr
->ptr
) continue;
425 if (ptr
->access
& RESERVED_INHERIT
) grab_object( ptr
->ptr
);
426 else ptr
->ptr
= NULL
; /* don't inherit this entry */
429 /* attempt to shrink the table */
430 shrink_handle_table( process
);
434 /* close a handle and decrement the refcount of the associated object */
435 /* return 1 if OK, 0 on error */
436 int close_handle( struct process
*process
, int handle
)
438 struct handle_entry
*entry
;
441 if (HANDLE_IS_GLOBAL(handle
))
443 handle
= HANDLE_GLOBAL_TO_LOCAL(handle
);
444 process
= &initial_process
;
446 if (!(entry
= get_handle( process
, handle
))) return 0;
447 if (entry
->access
& RESERVED_CLOSE_PROTECT
) return 0; /* FIXME: error code */
450 if (handle
-1 == process
->handle_last
) shrink_handle_table( process
);
451 release_object( obj
);
455 /* retrieve the object corresponding to a handle, incrementing its refcount */
456 struct object
*get_handle_obj( struct process
*process
, int handle
,
457 unsigned int access
, const struct object_ops
*ops
)
459 struct handle_entry
*entry
;
464 case 0xfffffffe: /* current thread pseudo-handle */
467 case 0x7fffffff: /* current process pseudo-handle */
468 obj
= (struct object
*)current
->process
;
471 if (!(entry
= get_handle( process
, handle
))) return NULL
;
472 if ((entry
->access
& access
) != access
)
474 SET_ERROR( ERROR_ACCESS_DENIED
);
480 if (ops
&& (obj
->ops
!= ops
))
482 SET_ERROR( ERROR_INVALID_HANDLE
); /* not the right type */
485 return grab_object( obj
);
488 /* get/set the handle reserved flags */
489 /* return the new flags (or -1 on error) */
490 int set_handle_info( struct process
*process
, int handle
, int mask
, int flags
)
492 struct handle_entry
*entry
;
494 if (!(entry
= get_handle( process
, handle
))) return -1;
495 mask
= (mask
<< RESERVED_SHIFT
) & RESERVED_ALL
;
496 flags
= (flags
<< RESERVED_SHIFT
) & mask
;
497 entry
->access
= (entry
->access
& ~mask
) | flags
;
498 return (entry
->access
& RESERVED_ALL
) >> RESERVED_SHIFT
;
501 /* duplicate a handle */
502 int duplicate_handle( struct process
*src
, int src_handle
, struct process
*dst
,
503 unsigned int access
, int inherit
, int options
)
506 struct handle_entry
*entry
= get_handle( src
, src_handle
);
507 if (!entry
) return -1;
509 if (options
& DUP_HANDLE_SAME_ACCESS
) access
= entry
->access
;
510 if (options
& DUP_HANDLE_MAKE_GLOBAL
) dst
= &initial_process
;
511 access
&= ~RESERVED_ALL
;
512 res
= alloc_handle( dst
, entry
->ptr
, access
, inherit
);
513 if (options
& DUP_HANDLE_MAKE_GLOBAL
) res
= HANDLE_LOCAL_TO_GLOBAL(res
);
517 /* open a new handle to an existing object */
518 int open_object( const char *name
, const struct object_ops
*ops
,
519 unsigned int access
, int inherit
)
521 struct object
*obj
= find_object( name
);
524 SET_ERROR( ERROR_FILE_NOT_FOUND
);
527 if (ops
&& obj
->ops
!= ops
)
529 release_object( obj
);
530 SET_ERROR( ERROR_INVALID_HANDLE
); /* FIXME: not the right type */
533 return alloc_handle( current
->process
, obj
, access
, inherit
);
536 /* dump a handle table on stdout */
537 void dump_handles( struct process
*process
)
539 struct handle_entry
*entry
;
542 if (!process
->entries
) return;
543 entry
= process
->entries
;
544 for (i
= 0; i
<= process
->handle_last
; i
++, entry
++)
546 if (!entry
->ptr
) continue;
547 printf( "%5d: %p %08x ", i
+ 1, entry
->ptr
, entry
->access
);
548 entry
->ptr
->ops
->dump( entry
->ptr
, 0 );
552 /* kill a process on the spot */
553 void kill_process( struct process
*process
, int exit_code
)
555 while (process
->thread_list
)
556 kill_thread( process
->thread_list
, exit_code
);
559 /* get all information about a process */
560 void get_process_info( struct process
*process
,
561 struct get_process_info_reply
*reply
)
563 reply
->pid
= process
;
564 reply
->exit_code
= process
->exit_code
;
565 reply
->priority
= process
->priority
;
566 reply
->process_affinity
= process
->affinity
;
567 reply
->system_affinity
= 1;
570 /* set all information about a process */
571 void set_process_info( struct process
*process
,
572 struct set_process_info_request
*req
)
574 if (req
->mask
& SET_PROCESS_INFO_PRIORITY
)
575 process
->priority
= req
->priority
;
576 if (req
->mask
& SET_PROCESS_INFO_AFFINITY
)
578 if (req
->affinity
!= 1) SET_ERROR( ERROR_INVALID_PARAMETER
);
579 else process
->affinity
= req
->affinity
;
583 /* allocate a console for this process */
584 int alloc_console( struct process
*process
)
586 struct object
*obj
[2];
587 if (process
->console_in
|| process
->console_out
)
589 SET_ERROR( ERROR_ACCESS_DENIED
);
592 if (!create_console( -1, obj
)) return 0;
593 process
->console_in
= obj
[0];
594 process
->console_out
= obj
[1];
598 /* free the console for this process */
599 int free_console( struct process
*process
)
601 if (process
->console_in
) release_object( process
->console_in
);
602 if (process
->console_out
) release_object( process
->console_out
);
603 process
->console_in
= process
->console_out
= NULL
;
607 /* get the process console */
608 struct object
*get_console( struct process
*process
, int output
)
611 if (!(obj
= output
? process
->console_out
: process
->console_in
))
613 return grab_object( obj
);
616 /* take a snapshot of currently running processes */
617 struct process_snapshot
*process_snap( int *count
)
619 struct process_snapshot
*snapshot
, *ptr
;
620 struct process
*process
;
621 if (!running_processes
) return NULL
;
622 if (!(snapshot
= mem_alloc( sizeof(*snapshot
) * running_processes
)))
625 for (process
= first_process
; process
; process
= process
->next
)
627 if (!process
->running_threads
) continue;
628 ptr
->process
= process
;
629 ptr
->threads
= process
->running_threads
;
630 ptr
->priority
= process
->priority
;
631 grab_object( process
);
634 *count
= running_processes
;