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 process_dump( 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 process_destroy( 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
=
74 /* create a new process */
75 struct process
*create_process(void)
77 struct process
*process
;
79 if (!(process
= mem_alloc( sizeof(*process
) ))) return NULL
;
81 if (!copy_handle_table( process
, current
? current
->process
: NULL
))
86 init_object( &process
->obj
, &process_ops
, NULL
);
87 process
->next
= first_process
;
89 process
->thread_list
= NULL
;
90 process
->exit_code
= 0x103; /* STILL_ACTIVE */
91 process
->running_threads
= 0;
93 if (first_process
) first_process
->prev
= process
;
94 first_process
= process
;
96 gettimeofday( &process
->start_time
, NULL
);
97 /* alloc a handle for the process itself */
98 alloc_handle( process
, process
, PROCESS_ALL_ACCESS
, 0 );
102 /* destroy a process when its refcount is 0 */
103 static void process_destroy( struct object
*obj
)
105 struct process
*process
= (struct process
*)obj
;
106 assert( obj
->ops
== &process_ops
);
108 /* we can't have a thread remaining */
109 assert( !process
->thread_list
);
110 if (process
->next
) process
->next
->prev
= process
->prev
;
111 if (process
->prev
) process
->prev
->next
= process
->next
;
112 else first_process
= process
->next
;
113 free_handles( process
);
114 if (debug_level
) memset( process
, 0xbb, sizeof(process
) ); /* catch errors */
118 /* dump a process on stdout for debugging purposes */
119 static void process_dump( struct object
*obj
, int verbose
)
121 struct process
*process
= (struct process
*)obj
;
122 assert( obj
->ops
== &process_ops
);
124 printf( "Process next=%p prev=%p\n", process
->next
, process
->prev
);
127 static int process_signaled( struct object
*obj
, struct thread
*thread
)
129 struct process
*process
= (struct process
*)obj
;
130 return (process
->running_threads
> 0);
133 static int process_satisfied( struct object
*obj
, struct thread
*thread
)
138 /* get a process from an id (and increment the refcount) */
139 struct process
*get_process_from_id( void *id
)
141 struct process
*p
= first_process
;
142 while (p
&& (p
!= id
)) p
= p
->next
;
143 if (p
) grab_object( p
);
144 else SET_ERROR( ERROR_INVALID_PARAMETER
);
148 /* get a process from a handle (and increment the refcount) */
149 struct process
*get_process_from_handle( int handle
, unsigned int access
)
151 return (struct process
*)get_handle_obj( current
->process
, handle
,
152 access
, &process_ops
);
155 /* a process has been killed (i.e. its last thread died) */
156 static void process_killed( struct process
*process
, int exit_code
)
158 assert( !process
->thread_list
);
159 process
->exit_code
= exit_code
;
160 gettimeofday( &process
->end_time
, NULL
);
161 wake_up( &process
->obj
, 0 );
162 free_handles( process
);
165 /* free the process handle entries */
166 static void free_handles( struct process
*process
)
168 struct handle_entry
*entry
;
171 if (!(entry
= process
->entries
)) return;
172 for (handle
= 0; handle
<= process
->handle_last
; handle
++, entry
++)
174 struct object
*obj
= entry
->ptr
;
176 if (obj
) release_object( obj
);
178 free( process
->entries
);
179 process
->handle_count
= 0;
180 process
->handle_last
= -1;
181 process
->entries
= NULL
;
184 /* add a thread to a process running threads list */
185 void add_process_thread( struct process
*process
, struct thread
*thread
)
187 thread
->proc_next
= process
->thread_list
;
188 thread
->proc_prev
= NULL
;
189 if (thread
->proc_next
) thread
->proc_next
->proc_prev
= thread
;
190 process
->thread_list
= thread
;
191 process
->running_threads
++;
192 grab_object( thread
);
195 /* remove a thread from a process running threads list */
196 void remove_process_thread( struct process
*process
, struct thread
*thread
)
198 assert( process
->running_threads
> 0 );
199 assert( process
->thread_list
);
201 if (thread
->proc_next
) thread
->proc_next
->proc_prev
= thread
->proc_prev
;
202 if (thread
->proc_prev
) thread
->proc_prev
->proc_next
= thread
->proc_next
;
203 else process
->thread_list
= thread
->proc_next
;
205 if (!--process
->running_threads
)
207 /* we have removed the last running thread, exit the process */
208 process_killed( process
, thread
->exit_code
);
210 release_object( thread
);
213 /* grow a handle table */
214 /* return 1 if OK, 0 on error */
215 static int grow_handle_table( struct process
*process
)
217 struct handle_entry
*new_entries
;
218 int count
= process
->handle_count
;
220 if (count
>= INT_MAX
/ 2) return 0;
222 if (!(new_entries
= realloc( process
->entries
, count
* sizeof(struct handle_entry
) )))
224 SET_ERROR( ERROR_OUTOFMEMORY
);
227 process
->handle_count
= count
;
228 process
->entries
= new_entries
;
232 /* allocate a handle for an object, incrementing its refcount */
233 /* return the handle, or -1 on error */
234 int alloc_handle( struct process
*process
, void *obj
, unsigned int access
,
237 struct handle_entry
*entry
;
240 assert( !(access
& RESERVED_ALL
) );
241 if (inherit
) access
|= RESERVED_INHERIT
;
243 /* find the first free entry */
245 if (!(entry
= process
->entries
)) return -1;
246 for (handle
= 0; handle
<= process
->handle_last
; handle
++, entry
++)
247 if (!entry
->ptr
) goto found
;
249 if (handle
>= process
->handle_count
)
251 if (!grow_handle_table( process
)) return -1;
252 entry
= process
->entries
+ handle
; /* the table may have moved */
254 process
->handle_last
= handle
;
257 entry
->ptr
= grab_object( obj
);
258 entry
->access
= access
;
259 return handle
+ 1; /* avoid handle 0 */
262 /* allocate a specific handle for an object, incrementing its refcount */
263 static int alloc_specific_handle( struct process
*process
, void *obj
, int handle
,
264 unsigned int access
, int inherit
)
266 struct handle_entry
*entry
;
269 if (handle
== -1) return alloc_handle( process
, obj
, access
, inherit
);
271 assert( !(access
& RESERVED_ALL
) );
272 if (inherit
) access
|= RESERVED_INHERIT
;
274 handle
--; /* handles start at 1 */
275 if ((handle
< 0) || (handle
> process
->handle_last
))
277 SET_ERROR( ERROR_INVALID_HANDLE
);
280 entry
= process
->entries
+ handle
;
283 entry
->ptr
= grab_object( obj
);
284 entry
->access
= access
;
285 if (old
) release_object( old
);
289 /* return an handle entry, or NULL if the handle is invalid */
290 static struct handle_entry
*get_handle( struct process
*process
, int handle
)
292 struct handle_entry
*entry
;
294 handle
--; /* handles start at 1 */
295 if ((handle
< 0) || (handle
> process
->handle_last
)) goto error
;
296 entry
= process
->entries
+ handle
;
297 if (!entry
->ptr
) goto error
;
301 SET_ERROR( ERROR_INVALID_HANDLE
);
305 /* attempt to shrink a table */
306 /* return 1 if OK, 0 on error */
307 static int shrink_handle_table( struct process
*process
)
309 struct handle_entry
*new_entries
;
310 struct handle_entry
*entry
= process
->entries
+ process
->handle_last
;
311 int count
= process
->handle_count
;
313 while (process
->handle_last
>= 0)
315 if (entry
->ptr
) break;
316 process
->handle_last
--;
319 if (process
->handle_last
>= count
/ 4) return 1; /* no need to shrink */
320 if (count
< MIN_HANDLE_ENTRIES
* 2) return 1; /* too small to shrink */
322 if (!(new_entries
= realloc( process
->entries
,
323 count
* sizeof(struct handle_entry
) )))
325 process
->handle_count
= count
;
326 process
->entries
= new_entries
;
330 /* copy the handle table of the parent process */
331 /* return 1 if OK, 0 on error */
332 static int copy_handle_table( struct process
*process
, struct process
*parent
)
334 struct handle_entry
*ptr
;
337 if (!parent
) /* first process */
339 count
= MIN_HANDLE_ENTRIES
;
344 assert( parent
->entries
);
345 count
= parent
->handle_count
;
346 last
= parent
->handle_last
;
349 if (!(ptr
= mem_alloc( count
* sizeof(struct handle_entry
)))) return 0;
350 process
->entries
= ptr
;
351 process
->handle_count
= count
;
352 process
->handle_last
= last
;
356 memcpy( ptr
, parent
->entries
, (last
+ 1) * sizeof(struct handle_entry
) );
357 for (i
= 0; i
<= last
; i
++, ptr
++)
359 if (!ptr
->ptr
) continue;
360 if (ptr
->access
& RESERVED_INHERIT
) grab_object( ptr
->ptr
);
361 else ptr
->ptr
= NULL
; /* don't inherit this entry */
364 /* attempt to shrink the table */
365 shrink_handle_table( process
);
369 /* close a handle and decrement the refcount of the associated object */
370 /* return 1 if OK, 0 on error */
371 int close_handle( struct process
*process
, int handle
)
373 struct handle_entry
*entry
;
376 if (!(entry
= get_handle( process
, handle
))) return 0;
377 if (entry
->access
& RESERVED_CLOSE_PROTECT
) return 0; /* FIXME: error code */
380 if (handle
-1 == process
->handle_last
) shrink_handle_table( process
);
381 release_object( obj
);
385 /* retrieve the object corresponding to a handle, incrementing its refcount */
386 struct object
*get_handle_obj( struct process
*process
, int handle
,
387 unsigned int access
, const struct object_ops
*ops
)
389 struct handle_entry
*entry
;
394 case 0xfffffffe: /* current thread pseudo-handle */
397 case 0x7fffffff: /* current process pseudo-handle */
398 obj
= (struct object
*)current
->process
;
401 if (!(entry
= get_handle( process
, handle
))) return NULL
;
402 if ((entry
->access
& access
) != access
)
404 SET_ERROR( ERROR_ACCESS_DENIED
);
410 if (ops
&& (obj
->ops
!= ops
))
412 SET_ERROR( ERROR_INVALID_HANDLE
); /* not the right type */
415 return grab_object( obj
);
418 /* get/set the handle reserved flags */
419 /* return the new flags (or -1 on error) */
420 int set_handle_info( struct process
*process
, int handle
, int mask
, int flags
)
422 struct handle_entry
*entry
;
424 if (!(entry
= get_handle( process
, handle
))) return -1;
425 mask
= (mask
<< RESERVED_SHIFT
) & RESERVED_ALL
;
426 flags
= (flags
<< RESERVED_SHIFT
) & mask
;
427 entry
->access
= (entry
->access
& ~mask
) | flags
;
428 return (entry
->access
& RESERVED_ALL
) >> RESERVED_SHIFT
;
431 /* duplicate a handle */
432 int duplicate_handle( struct process
*src
, int src_handle
, struct process
*dst
,
433 int dst_handle
, unsigned int access
, int inherit
, int options
)
435 struct handle_entry
*entry
= get_handle( src
, src_handle
);
436 if (!entry
) return -1;
438 if (options
& DUPLICATE_SAME_ACCESS
) access
= entry
->access
;
439 access
&= ~RESERVED_ALL
;
440 return alloc_specific_handle( dst
, entry
->ptr
, dst_handle
, access
, inherit
);
443 /* open a new handle to an existing object */
444 int open_object( const char *name
, const struct object_ops
*ops
,
445 unsigned int access
, int inherit
)
447 struct object
*obj
= find_object( name
);
448 if (!obj
) return -1; /* FIXME: set error code */
449 if (ops
&& obj
->ops
!= ops
)
451 release_object( obj
);
452 return -1; /* FIXME: set error code */
454 return alloc_handle( current
->process
, obj
, access
, inherit
);
457 /* dump a handle table on stdout */
458 void dump_handles( struct process
*process
)
460 struct handle_entry
*entry
;
463 if (!process
->entries
) return;
464 entry
= process
->entries
;
465 for (i
= 0; i
<= process
->handle_last
; i
++, entry
++)
467 if (!entry
->ptr
) continue;
468 printf( "%5d: %p %08x ", i
+ 1, entry
->ptr
, entry
->access
);
469 entry
->ptr
->ops
->dump( entry
->ptr
, 0 );
473 /* kill a process on the spot */
474 void kill_process( struct process
*process
, int exit_code
)
476 while (process
->thread_list
)
477 kill_thread( process
->thread_list
, exit_code
);
480 /* get all information about a process */
481 void get_process_info( struct process
*process
,
482 struct get_process_info_reply
*reply
)
484 reply
->pid
= process
;
485 reply
->exit_code
= process
->exit_code
;