2 * Server-side process management
4 * Copyright (C) 1998 Alexandre Julliard
20 #include "server/thread.h"
22 /* reserved handle access rights */
23 #define RESERVED_SHIFT 25
24 #define RESERVED_INHERIT (HANDLE_FLAG_INHERIT << RESERVED_SHIFT)
25 #define RESERVED_CLOSE_PROTECT (HANDLE_FLAG_PROTECT_FROM_CLOSE << RESERVED_SHIFT)
26 #define RESERVED_ALL (RESERVED_INHERIT | RESERVED_CLOSE_PROTECT)
34 /* process structure; not much for now... */
38 struct object obj
; /* object header */
39 struct process
*next
; /* system-wide process list */
41 struct thread
*thread_list
; /* head of the thread list */
42 struct handle_entry
*entries
; /* handle entry table */
43 int handle_count
; /* nb of allocated handle entries */
44 int handle_last
; /* last used handle entry */
45 int exit_code
; /* process exit code */
46 int running_threads
; /* number of threads running in this process */
47 struct timeval start_time
; /* absolute time at process start */
48 struct timeval end_time
; /* absolute time at process end */
51 static struct process
*first_process
;
53 #define MIN_HANDLE_ENTRIES 32
55 /* process operations */
57 static void dump_process( struct object
*obj
, int verbose
);
58 static int process_signaled( struct object
*obj
, struct thread
*thread
);
59 static int process_satisfied( struct object
*obj
, struct thread
*thread
);
60 static void destroy_process( struct object
*obj
);
61 static void free_handles( struct process
*process
);
62 static int copy_handle_table( struct process
*process
, struct process
*parent
);
64 static const struct object_ops process_ops
=
72 /* create a new process */
73 struct process
*create_process(void)
75 struct process
*process
;
77 if (!(process
= mem_alloc( sizeof(*process
) ))) return NULL
;
79 if (!copy_handle_table( process
, current
? current
->process
: NULL
))
84 init_object( &process
->obj
, &process_ops
, NULL
);
85 process
->next
= first_process
;
87 process
->thread_list
= NULL
;
88 process
->exit_code
= 0x103; /* STILL_ACTIVE */
89 process
->running_threads
= 0;
91 if (first_process
) first_process
->prev
= process
;
92 first_process
= process
;
94 gettimeofday( &process
->start_time
, NULL
);
95 /* alloc a handle for the process itself */
96 alloc_handle( process
, process
, PROCESS_ALL_ACCESS
, 0 );
100 /* destroy a process when its refcount is 0 */
101 static void destroy_process( struct object
*obj
)
103 struct process
*process
= (struct process
*)obj
;
104 assert( obj
->ops
== &process_ops
);
106 /* we can't have a thread remaining */
107 assert( !process
->thread_list
);
108 if (process
->next
) process
->next
->prev
= process
->prev
;
109 if (process
->prev
) process
->prev
->next
= process
->next
;
110 else first_process
= process
->next
;
111 free_handles( process
);
112 if (debug_level
) memset( process
, 0xbb, sizeof(process
) ); /* catch errors */
116 /* dump a process on stdout for debugging purposes */
117 static void dump_process( struct object
*obj
, int verbose
)
119 struct process
*process
= (struct process
*)obj
;
120 assert( obj
->ops
== &process_ops
);
122 printf( "Process next=%p prev=%p\n", process
->next
, process
->prev
);
125 static int process_signaled( struct object
*obj
, struct thread
*thread
)
127 struct process
*process
= (struct process
*)obj
;
128 return (process
->running_threads
> 0);
131 static int process_satisfied( struct object
*obj
, struct thread
*thread
)
136 /* get a process from an id (and increment the refcount) */
137 struct process
*get_process_from_id( void *id
)
139 struct process
*p
= first_process
;
140 while (p
&& (p
!= id
)) p
= p
->next
;
141 if (p
) grab_object( p
);
142 else SET_ERROR( ERROR_INVALID_PARAMETER
);
146 /* get a process from a handle (and increment the refcount) */
147 struct process
*get_process_from_handle( int handle
, unsigned int access
)
149 return (struct process
*)get_handle_obj( current
->process
, handle
,
150 access
, &process_ops
);
153 /* a process has been killed (i.e. its last thread died) */
154 static void process_killed( struct process
*process
, int exit_code
)
156 assert( !process
->thread_list
);
157 process
->exit_code
= exit_code
;
158 gettimeofday( &process
->end_time
, NULL
);
159 wake_up( &process
->obj
, 0 );
160 free_handles( process
);
163 /* free the process handle entries */
164 static void free_handles( struct process
*process
)
166 struct handle_entry
*entry
;
169 if (!(entry
= process
->entries
)) return;
170 for (handle
= 0; handle
<= process
->handle_last
; handle
++, entry
++)
172 struct object
*obj
= entry
->ptr
;
174 if (obj
) release_object( obj
);
176 free( process
->entries
);
177 process
->handle_count
= 0;
178 process
->handle_last
= -1;
179 process
->entries
= NULL
;
182 /* add a thread to a process running threads list */
183 void add_process_thread( struct process
*process
, struct thread
*thread
)
185 thread
->proc_next
= process
->thread_list
;
186 thread
->proc_prev
= NULL
;
187 if (thread
->proc_next
) thread
->proc_next
->proc_prev
= thread
;
188 process
->thread_list
= thread
;
189 process
->running_threads
++;
190 grab_object( thread
);
193 /* remove a thread from a process running threads list */
194 void remove_process_thread( struct process
*process
, struct thread
*thread
)
196 assert( process
->running_threads
> 0 );
197 assert( process
->thread_list
);
199 if (thread
->proc_next
) thread
->proc_next
->proc_prev
= thread
->proc_prev
;
200 if (thread
->proc_prev
) thread
->proc_prev
->proc_next
= thread
->proc_next
;
201 else process
->thread_list
= thread
->proc_next
;
203 if (!--process
->running_threads
)
205 /* we have removed the last running thread, exit the process */
206 process_killed( process
, thread
->exit_code
);
208 release_object( thread
);
211 /* grow a handle table */
212 /* return 1 if OK, 0 on error */
213 static int grow_handle_table( struct process
*process
)
215 struct handle_entry
*new_entries
;
216 int count
= process
->handle_count
;
218 if (count
>= INT_MAX
/ 2) return 0;
220 if (!(new_entries
= realloc( process
->entries
, count
* sizeof(struct handle_entry
) )))
222 SET_ERROR( ERROR_OUTOFMEMORY
);
225 process
->handle_count
= count
;
226 process
->entries
= new_entries
;
230 /* allocate a handle for an object, incrementing its refcount */
231 /* return the handle, or -1 on error */
232 int alloc_handle( struct process
*process
, void *obj
, unsigned int access
,
235 struct handle_entry
*entry
;
238 assert( !(access
& RESERVED_ALL
) );
239 if (inherit
) access
|= RESERVED_INHERIT
;
241 /* find the first free entry */
243 if (!(entry
= process
->entries
)) return -1;
244 for (handle
= 0; handle
<= process
->handle_last
; handle
++, entry
++)
245 if (!entry
->ptr
) goto found
;
247 if (handle
>= process
->handle_count
)
249 if (!grow_handle_table( process
)) return -1;
250 entry
= process
->entries
+ handle
; /* the table may have moved */
252 process
->handle_last
= handle
;
255 entry
->ptr
= grab_object( obj
);
256 entry
->access
= access
;
257 return handle
+ 1; /* avoid handle 0 */
260 /* allocate a specific handle for an object, incrementing its refcount */
261 static int alloc_specific_handle( struct process
*process
, void *obj
, int handle
,
262 unsigned int access
, int inherit
)
264 struct handle_entry
*entry
;
267 if (handle
== -1) return alloc_handle( process
, obj
, access
, inherit
);
269 assert( !(access
& RESERVED_ALL
) );
270 if (inherit
) access
|= RESERVED_INHERIT
;
272 handle
--; /* handles start at 1 */
273 if ((handle
< 0) || (handle
> process
->handle_last
))
275 SET_ERROR( ERROR_INVALID_HANDLE
);
278 entry
= process
->entries
+ handle
;
281 entry
->ptr
= grab_object( obj
);
282 entry
->access
= access
;
283 if (old
) release_object( old
);
287 /* return an handle entry, or NULL if the handle is invalid */
288 static struct handle_entry
*get_handle( struct process
*process
, int handle
)
290 struct handle_entry
*entry
;
292 handle
--; /* handles start at 1 */
293 if ((handle
< 0) || (handle
> process
->handle_last
)) goto error
;
294 entry
= process
->entries
+ handle
;
295 if (!entry
->ptr
) goto error
;
299 SET_ERROR( ERROR_INVALID_HANDLE
);
303 /* attempt to shrink a table */
304 /* return 1 if OK, 0 on error */
305 static int shrink_handle_table( struct process
*process
)
307 struct handle_entry
*new_entries
;
308 struct handle_entry
*entry
= process
->entries
+ process
->handle_last
;
309 int count
= process
->handle_count
;
311 while (process
->handle_last
>= 0)
313 if (entry
->ptr
) break;
314 process
->handle_last
--;
317 if (process
->handle_last
>= count
/ 4) return 1; /* no need to shrink */
318 if (count
< MIN_HANDLE_ENTRIES
* 2) return 1; /* too small to shrink */
320 if (!(new_entries
= realloc( process
->entries
,
321 count
* sizeof(struct handle_entry
) )))
323 process
->handle_count
= count
;
324 process
->entries
= new_entries
;
328 /* copy the handle table of the parent process */
329 /* return 1 if OK, 0 on error */
330 static int copy_handle_table( struct process
*process
, struct process
*parent
)
332 struct handle_entry
*ptr
;
335 if (!parent
) /* first process */
337 count
= MIN_HANDLE_ENTRIES
;
342 assert( parent
->entries
);
343 count
= parent
->handle_count
;
344 last
= parent
->handle_last
;
347 if (!(ptr
= mem_alloc( count
* sizeof(struct handle_entry
)))) return 0;
348 process
->entries
= ptr
;
349 process
->handle_count
= count
;
350 process
->handle_last
= last
;
354 memcpy( ptr
, parent
->entries
, (last
+ 1) * sizeof(struct handle_entry
) );
355 for (i
= 0; i
<= last
; i
++, ptr
++)
357 if (!ptr
->ptr
) continue;
358 if (ptr
->access
& RESERVED_INHERIT
) grab_object( ptr
->ptr
);
359 else ptr
->ptr
= NULL
; /* don't inherit this entry */
362 /* attempt to shrink the table */
363 shrink_handle_table( process
);
367 /* close a handle and decrement the refcount of the associated object */
368 /* return 1 if OK, 0 on error */
369 int close_handle( struct process
*process
, int handle
)
371 struct handle_entry
*entry
;
374 if (!(entry
= get_handle( process
, handle
))) return 0;
375 if (entry
->access
& RESERVED_CLOSE_PROTECT
) return 0; /* FIXME: error code */
378 if (handle
-1 == process
->handle_last
) shrink_handle_table( process
);
379 release_object( obj
);
383 /* retrieve the object corresponding to a handle, incrementing its refcount */
384 struct object
*get_handle_obj( struct process
*process
, int handle
,
385 unsigned int access
, const struct object_ops
*ops
)
387 struct handle_entry
*entry
;
392 case 0xfffffffe: /* current thread pseudo-handle */
395 case 0x7fffffff: /* current process pseudo-handle */
396 obj
= (struct object
*)current
->process
;
399 if (!(entry
= get_handle( process
, handle
))) return NULL
;
400 if ((entry
->access
& access
) != access
)
402 SET_ERROR( ERROR_ACCESS_DENIED
);
408 if (ops
&& (obj
->ops
!= ops
))
410 SET_ERROR( ERROR_INVALID_HANDLE
); /* not the right type */
413 return grab_object( obj
);
416 /* get/set the handle reserved flags */
417 /* return the new flags (or -1 on error) */
418 int set_handle_info( struct process
*process
, int handle
, int mask
, int flags
)
420 struct handle_entry
*entry
;
422 if (!(entry
= get_handle( process
, handle
))) return -1;
423 mask
= (mask
<< RESERVED_SHIFT
) & RESERVED_ALL
;
424 flags
= (flags
<< RESERVED_SHIFT
) & mask
;
425 entry
->access
= (entry
->access
& ~mask
) | flags
;
426 return (entry
->access
& RESERVED_ALL
) >> RESERVED_SHIFT
;
429 /* duplicate a handle */
430 int duplicate_handle( struct process
*src
, int src_handle
, struct process
*dst
,
431 int dst_handle
, unsigned int access
, int inherit
, int options
)
433 struct handle_entry
*entry
= get_handle( src
, src_handle
);
434 if (!entry
) return -1;
436 if (options
& DUPLICATE_SAME_ACCESS
) access
= entry
->access
;
437 access
&= ~RESERVED_ALL
;
438 return alloc_specific_handle( dst
, entry
->ptr
, dst_handle
, access
, inherit
);
441 /* open a new handle to an existing object */
442 int open_object( const char *name
, const struct object_ops
*ops
,
443 unsigned int access
, int inherit
)
445 struct object
*obj
= find_object( name
);
446 if (!obj
) return -1; /* FIXME: set error code */
447 if (ops
&& obj
->ops
!= ops
)
449 release_object( obj
);
450 return -1; /* FIXME: set error code */
452 return alloc_handle( current
->process
, obj
, access
, inherit
);
455 /* dump a handle table on stdout */
456 void dump_handles( struct process
*process
)
458 struct handle_entry
*entry
;
461 if (!process
->entries
) return;
462 entry
= process
->entries
;
463 for (i
= 0; i
<= process
->handle_last
; i
++, entry
++)
465 if (!entry
->ptr
) continue;
466 printf( "%5d: %p %08x ", i
+ 1, entry
->ptr
, entry
->access
);
467 entry
->ptr
->ops
->dump( entry
->ptr
, 0 );
471 /* kill a process on the spot */
472 void kill_process( struct process
*process
, int exit_code
)
474 while (process
->thread_list
)
475 kill_thread( process
->thread_list
, exit_code
);
478 /* get all information about a process */
479 void get_process_info( struct process
*process
,
480 struct get_process_info_reply
*reply
)
482 reply
->pid
= process
;
483 reply
->exit_code
= process
->exit_code
;