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)
28 /* global handle macros */
29 #define HANDLE_OBFUSCATOR 0x544a4def
30 #define HANDLE_IS_GLOBAL(h) (((h) ^ HANDLE_OBFUSCATOR) < 0x10000)
31 #define HANDLE_LOCAL_TO_GLOBAL(h) ((h) ^ HANDLE_OBFUSCATOR)
32 #define HANDLE_GLOBAL_TO_LOCAL(h) ((h) ^ HANDLE_OBFUSCATOR)
40 /* process structure; not much for now... */
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 */
61 static struct process
*first_process
;
62 static struct process
*initial_process
;
64 #define MIN_HANDLE_ENTRIES 32
66 /* process operations */
68 static void process_dump( struct object
*obj
, int verbose
);
69 static int process_signaled( struct object
*obj
, struct thread
*thread
);
70 static void process_destroy( struct object
*obj
);
71 static void free_handles( struct process
*process
);
72 static int copy_handle_table( struct process
*process
, struct process
*parent
);
74 static const struct object_ops process_ops
=
88 /* create a new process */
89 struct process
*create_process(void)
91 struct process
*process
, *parent
;
93 if (!(process
= mem_alloc( sizeof(*process
) ))) return NULL
;
95 parent
= current
? current
->process
: NULL
;
96 if (!copy_handle_table( process
, parent
))
101 init_object( &process
->obj
, &process_ops
, NULL
);
102 process
->next
= first_process
;
103 process
->prev
= NULL
;
104 process
->thread_list
= NULL
;
105 process
->exit_code
= 0x103; /* STILL_ACTIVE */
106 process
->running_threads
= 0;
107 process
->priority
= NORMAL_PRIORITY_CLASS
;
108 process
->affinity
= 1;
109 process
->console_in
= NULL
;
110 process
->console_out
= NULL
;
113 if (parent
->console_in
) process
->console_in
= grab_object( parent
->console_in
);
114 if (parent
->console_out
) process
->console_out
= grab_object( parent
->console_out
);
117 if (first_process
) first_process
->prev
= process
;
118 first_process
= process
;
119 if (!initial_process
)
121 initial_process
= process
;
122 grab_object( initial_process
); /* so that we never free it */
125 gettimeofday( &process
->start_time
, NULL
);
126 /* alloc a handle for the process itself */
127 alloc_handle( process
, process
, PROCESS_ALL_ACCESS
, 0 );
131 /* destroy a process when its refcount is 0 */
132 static void process_destroy( struct object
*obj
)
134 struct process
*process
= (struct process
*)obj
;
135 assert( obj
->ops
== &process_ops
);
136 assert( process
!= initial_process
);
138 /* we can't have a thread remaining */
139 assert( !process
->thread_list
);
140 if (process
->next
) process
->next
->prev
= process
->prev
;
141 if (process
->prev
) process
->prev
->next
= process
->next
;
142 else first_process
= process
->next
;
143 free_console( process
);
144 free_handles( process
);
145 if (debug_level
) memset( process
, 0xbb, sizeof(process
) ); /* catch errors */
149 /* dump a process on stdout for debugging purposes */
150 static void process_dump( struct object
*obj
, int verbose
)
152 struct process
*process
= (struct process
*)obj
;
153 assert( obj
->ops
== &process_ops
);
155 printf( "Process next=%p prev=%p\n", process
->next
, process
->prev
);
158 static int process_signaled( struct object
*obj
, struct thread
*thread
)
160 struct process
*process
= (struct process
*)obj
;
161 return (process
->running_threads
> 0);
164 /* get a process from an id (and increment the refcount) */
165 struct process
*get_process_from_id( void *id
)
167 struct process
*p
= first_process
;
168 while (p
&& (p
!= id
)) p
= p
->next
;
169 if (p
) grab_object( p
);
170 else SET_ERROR( ERROR_INVALID_PARAMETER
);
174 /* get a process from a handle (and increment the refcount) */
175 struct process
*get_process_from_handle( int handle
, unsigned int access
)
177 return (struct process
*)get_handle_obj( current
->process
, handle
,
178 access
, &process_ops
);
181 /* a process has been killed (i.e. its last thread died) */
182 static void process_killed( struct process
*process
, int exit_code
)
184 assert( !process
->thread_list
);
185 process
->exit_code
= exit_code
;
186 gettimeofday( &process
->end_time
, NULL
);
187 wake_up( &process
->obj
, 0 );
188 free_handles( process
);
191 /* free the process handle entries */
192 static void free_handles( struct process
*process
)
194 struct handle_entry
*entry
;
197 if (!(entry
= process
->entries
)) return;
198 for (handle
= 0; handle
<= process
->handle_last
; handle
++, entry
++)
200 struct object
*obj
= entry
->ptr
;
202 if (obj
) release_object( obj
);
204 free( process
->entries
);
205 process
->handle_count
= 0;
206 process
->handle_last
= -1;
207 process
->entries
= NULL
;
210 /* add a thread to a process running threads list */
211 void add_process_thread( struct process
*process
, struct thread
*thread
)
213 thread
->proc_next
= process
->thread_list
;
214 thread
->proc_prev
= NULL
;
215 if (thread
->proc_next
) thread
->proc_next
->proc_prev
= thread
;
216 process
->thread_list
= thread
;
217 process
->running_threads
++;
218 grab_object( thread
);
221 /* remove a thread from a process running threads list */
222 void remove_process_thread( struct process
*process
, struct thread
*thread
)
224 assert( process
->running_threads
> 0 );
225 assert( process
->thread_list
);
227 if (thread
->proc_next
) thread
->proc_next
->proc_prev
= thread
->proc_prev
;
228 if (thread
->proc_prev
) thread
->proc_prev
->proc_next
= thread
->proc_next
;
229 else process
->thread_list
= thread
->proc_next
;
231 if (!--process
->running_threads
)
233 /* we have removed the last running thread, exit the process */
234 process_killed( process
, thread
->exit_code
);
236 release_object( thread
);
239 /* grow a handle table */
240 /* return 1 if OK, 0 on error */
241 static int grow_handle_table( struct process
*process
)
243 struct handle_entry
*new_entries
;
244 int count
= process
->handle_count
;
246 if (count
>= INT_MAX
/ 2) return 0;
248 if (!(new_entries
= realloc( process
->entries
, count
* sizeof(struct handle_entry
) )))
250 SET_ERROR( ERROR_OUTOFMEMORY
);
253 process
->handle_count
= count
;
254 process
->entries
= new_entries
;
258 /* allocate a handle for an object, incrementing its refcount */
259 /* return the handle, or -1 on error */
260 int alloc_handle( struct process
*process
, void *obj
, unsigned int access
,
263 struct handle_entry
*entry
;
266 assert( !(access
& RESERVED_ALL
) );
267 if (inherit
) access
|= RESERVED_INHERIT
;
269 /* find the first free entry */
271 if (!(entry
= process
->entries
)) return -1;
272 for (handle
= 0; handle
<= process
->handle_last
; handle
++, entry
++)
273 if (!entry
->ptr
) goto found
;
275 if (handle
>= process
->handle_count
)
277 if (!grow_handle_table( process
)) return -1;
278 entry
= process
->entries
+ handle
; /* the table may have moved */
280 process
->handle_last
= handle
;
283 entry
->ptr
= grab_object( obj
);
284 entry
->access
= access
;
285 return handle
+ 1; /* avoid handle 0 */
288 /* allocate a specific handle for an object, incrementing its refcount */
289 static int alloc_specific_handle( struct process
*process
, void *obj
, int handle
,
290 unsigned int access
, int inherit
)
292 struct handle_entry
*entry
;
295 if (handle
== -1) return alloc_handle( process
, obj
, access
, inherit
);
297 assert( !(access
& RESERVED_ALL
) );
298 if (inherit
) access
|= RESERVED_INHERIT
;
300 handle
--; /* handles start at 1 */
301 if ((handle
< 0) || (handle
> process
->handle_last
))
303 SET_ERROR( ERROR_INVALID_HANDLE
);
306 entry
= process
->entries
+ handle
;
309 entry
->ptr
= grab_object( obj
);
310 entry
->access
= access
;
311 if (old
) release_object( old
);
315 /* return an handle entry, or NULL if the handle is invalid */
316 static struct handle_entry
*get_handle( struct process
*process
, int handle
)
318 struct handle_entry
*entry
;
320 if (HANDLE_IS_GLOBAL(handle
))
322 handle
= HANDLE_GLOBAL_TO_LOCAL(handle
);
323 process
= initial_process
;
325 handle
--; /* handles start at 1 */
326 if ((handle
< 0) || (handle
> process
->handle_last
)) goto error
;
327 entry
= process
->entries
+ handle
;
328 if (!entry
->ptr
) goto error
;
332 SET_ERROR( ERROR_INVALID_HANDLE
);
336 /* attempt to shrink a table */
337 /* return 1 if OK, 0 on error */
338 static int shrink_handle_table( struct process
*process
)
340 struct handle_entry
*new_entries
;
341 struct handle_entry
*entry
= process
->entries
+ process
->handle_last
;
342 int count
= process
->handle_count
;
344 while (process
->handle_last
>= 0)
346 if (entry
->ptr
) break;
347 process
->handle_last
--;
350 if (process
->handle_last
>= count
/ 4) return 1; /* no need to shrink */
351 if (count
< MIN_HANDLE_ENTRIES
* 2) return 1; /* too small to shrink */
353 if (!(new_entries
= realloc( process
->entries
,
354 count
* sizeof(struct handle_entry
) )))
356 process
->handle_count
= count
;
357 process
->entries
= new_entries
;
361 /* copy the handle table of the parent process */
362 /* return 1 if OK, 0 on error */
363 static int copy_handle_table( struct process
*process
, struct process
*parent
)
365 struct handle_entry
*ptr
;
368 if (!parent
) /* first process */
370 count
= MIN_HANDLE_ENTRIES
;
375 assert( parent
->entries
);
376 count
= parent
->handle_count
;
377 last
= parent
->handle_last
;
380 if (!(ptr
= mem_alloc( count
* sizeof(struct handle_entry
)))) return 0;
381 process
->entries
= ptr
;
382 process
->handle_count
= count
;
383 process
->handle_last
= last
;
387 memcpy( ptr
, parent
->entries
, (last
+ 1) * sizeof(struct handle_entry
) );
388 for (i
= 0; i
<= last
; i
++, ptr
++)
390 if (!ptr
->ptr
) continue;
391 if (ptr
->access
& RESERVED_INHERIT
) grab_object( ptr
->ptr
);
392 else ptr
->ptr
= NULL
; /* don't inherit this entry */
395 /* attempt to shrink the table */
396 shrink_handle_table( process
);
400 /* close a handle and decrement the refcount of the associated object */
401 /* return 1 if OK, 0 on error */
402 int close_handle( struct process
*process
, int handle
)
404 struct handle_entry
*entry
;
407 if (HANDLE_IS_GLOBAL(handle
))
409 handle
= HANDLE_GLOBAL_TO_LOCAL(handle
);
410 process
= initial_process
;
412 if (!(entry
= get_handle( process
, handle
))) return 0;
413 if (entry
->access
& RESERVED_CLOSE_PROTECT
) return 0; /* FIXME: error code */
416 if (handle
-1 == process
->handle_last
) shrink_handle_table( process
);
417 release_object( obj
);
421 /* retrieve the object corresponding to a handle, incrementing its refcount */
422 struct object
*get_handle_obj( struct process
*process
, int handle
,
423 unsigned int access
, const struct object_ops
*ops
)
425 struct handle_entry
*entry
;
430 case 0xfffffffe: /* current thread pseudo-handle */
433 case 0x7fffffff: /* current process pseudo-handle */
434 obj
= (struct object
*)current
->process
;
437 if (!(entry
= get_handle( process
, handle
))) return NULL
;
438 if ((entry
->access
& access
) != access
)
440 SET_ERROR( ERROR_ACCESS_DENIED
);
446 if (ops
&& (obj
->ops
!= ops
))
448 SET_ERROR( ERROR_INVALID_HANDLE
); /* not the right type */
451 return grab_object( obj
);
454 /* get/set the handle reserved flags */
455 /* return the new flags (or -1 on error) */
456 int set_handle_info( struct process
*process
, int handle
, int mask
, int flags
)
458 struct handle_entry
*entry
;
460 if (!(entry
= get_handle( process
, handle
))) return -1;
461 mask
= (mask
<< RESERVED_SHIFT
) & RESERVED_ALL
;
462 flags
= (flags
<< RESERVED_SHIFT
) & mask
;
463 entry
->access
= (entry
->access
& ~mask
) | flags
;
464 return (entry
->access
& RESERVED_ALL
) >> RESERVED_SHIFT
;
467 /* duplicate a handle */
468 int duplicate_handle( struct process
*src
, int src_handle
, struct process
*dst
,
469 int dst_handle
, unsigned int access
, int inherit
, int options
)
472 struct handle_entry
*entry
= get_handle( src
, src_handle
);
473 if (!entry
) return -1;
475 if (options
& DUP_HANDLE_SAME_ACCESS
) access
= entry
->access
;
476 if (options
& DUP_HANDLE_MAKE_GLOBAL
) dst
= initial_process
;
477 access
&= ~RESERVED_ALL
;
478 res
= alloc_specific_handle( dst
, entry
->ptr
, dst_handle
, access
, inherit
);
479 if (options
& DUP_HANDLE_MAKE_GLOBAL
) res
= HANDLE_LOCAL_TO_GLOBAL(res
);
483 /* open a new handle to an existing object */
484 int open_object( const char *name
, const struct object_ops
*ops
,
485 unsigned int access
, int inherit
)
487 struct object
*obj
= find_object( name
);
488 if (!obj
) return -1; /* FIXME: set error code */
489 if (ops
&& obj
->ops
!= ops
)
491 release_object( obj
);
492 return -1; /* FIXME: set error code */
494 return alloc_handle( current
->process
, obj
, access
, inherit
);
497 /* dump a handle table on stdout */
498 void dump_handles( struct process
*process
)
500 struct handle_entry
*entry
;
503 if (!process
->entries
) return;
504 entry
= process
->entries
;
505 for (i
= 0; i
<= process
->handle_last
; i
++, entry
++)
507 if (!entry
->ptr
) continue;
508 printf( "%5d: %p %08x ", i
+ 1, entry
->ptr
, entry
->access
);
509 entry
->ptr
->ops
->dump( entry
->ptr
, 0 );
513 /* kill a process on the spot */
514 void kill_process( struct process
*process
, int exit_code
)
516 while (process
->thread_list
)
517 kill_thread( process
->thread_list
, exit_code
);
520 /* get all information about a process */
521 void get_process_info( struct process
*process
,
522 struct get_process_info_reply
*reply
)
524 reply
->pid
= process
;
525 reply
->exit_code
= process
->exit_code
;
526 reply
->priority
= process
->priority
;
527 reply
->process_affinity
= process
->affinity
;
528 reply
->system_affinity
= 1;
531 /* set all information about a process */
532 void set_process_info( struct process
*process
,
533 struct set_process_info_request
*req
)
535 if (req
->mask
& SET_PROCESS_INFO_PRIORITY
)
536 process
->priority
= req
->priority
;
537 if (req
->mask
& SET_PROCESS_INFO_AFFINITY
)
539 if (req
->affinity
!= 1) SET_ERROR( ERROR_INVALID_PARAMETER
);
540 else process
->affinity
= req
->affinity
;
544 /* allocate a console for this process */
545 int alloc_console( struct process
*process
)
547 struct object
*obj
[2];
548 if (process
->console_in
|| process
->console_out
)
550 SET_ERROR( ERROR_ACCESS_DENIED
);
553 if (!create_console( -1, obj
)) return 0;
554 process
->console_in
= obj
[0];
555 process
->console_out
= obj
[1];
559 /* free the console for this process */
560 int free_console( struct process
*process
)
562 if (process
->console_in
) release_object( process
->console_in
);
563 if (process
->console_out
) release_object( process
->console_out
);
564 process
->console_in
= process
->console_out
= NULL
;
568 /* get the process console */
569 struct object
*get_console( struct process
*process
, int output
)
572 if (!(obj
= output
? process
->console_out
: process
->console_in
))
574 return grab_object( obj
);