Added beginnings of server-side file handling.
[wine/multimedia.git] / server / process.c
blobda3f06c523c544cb013149066dd1ba89367caaf7
1 /*
2 * Server-side process management
4 * Copyright (C) 1998 Alexandre Julliard
5 */
7 #include <assert.h>
8 #include <limits.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/time.h>
13 #include <unistd.h>
15 #include "winerror.h"
16 #include "winbase.h"
17 #include "winnt.h"
19 #include "server.h"
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 struct handle_entry
30 struct object *ptr;
31 unsigned int access;
34 /* process structure; not much for now... */
36 struct process
38 struct object obj; /* object header */
39 struct process *next; /* system-wide process list */
40 struct process *prev;
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 =
66 process_dump,
67 add_queue,
68 remove_queue,
69 process_signaled,
70 process_satisfied,
71 process_destroy
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 ))
83 free( process );
84 return NULL;
86 init_object( &process->obj, &process_ops, NULL );
87 process->next = first_process;
88 process->prev = NULL;
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 );
99 return process;
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 */
115 free( process );
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 )
135 return 0;
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 );
145 return p;
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;
169 int handle;
171 if (!(entry = process->entries)) return;
172 for (handle = 0; handle <= process->handle_last; handle++, entry++)
174 struct object *obj = entry->ptr;
175 entry->ptr = NULL;
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;
221 count *= 2;
222 if (!(new_entries = realloc( process->entries, count * sizeof(struct handle_entry) )))
224 SET_ERROR( ERROR_OUTOFMEMORY );
225 return 0;
227 process->handle_count = count;
228 process->entries = new_entries;
229 return 1;
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,
235 int inherit )
237 struct handle_entry *entry;
238 int handle;
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;
256 found:
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;
267 struct object *old;
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 );
278 return -1;
280 entry = process->entries + handle;
282 old = entry->ptr;
283 entry->ptr = grab_object( obj );
284 entry->access = access;
285 if (old) release_object( old );
286 return handle + 1;
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;
298 return entry;
300 error:
301 SET_ERROR( ERROR_INVALID_HANDLE );
302 return NULL;
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--;
317 entry--;
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 */
321 count /= 2;
322 if (!(new_entries = realloc( process->entries,
323 count * sizeof(struct handle_entry) )))
324 return 0;
325 process->handle_count = count;
326 process->entries = new_entries;
327 return 1;
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;
335 int i, count, last;
337 if (!parent) /* first process */
339 count = MIN_HANDLE_ENTRIES;
340 last = -1;
342 else
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;
354 if (last >= 0)
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 );
366 return 1;
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;
374 struct object *obj;
376 if (!(entry = get_handle( process, handle ))) return 0;
377 if (entry->access & RESERVED_CLOSE_PROTECT) return 0; /* FIXME: error code */
378 obj = entry->ptr;
379 entry->ptr = NULL;
380 if (handle-1 == process->handle_last) shrink_handle_table( process );
381 release_object( obj );
382 return 1;
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;
390 struct object *obj;
392 switch( handle )
394 case 0xfffffffe: /* current thread pseudo-handle */
395 obj = &current->obj;
396 break;
397 case 0x7fffffff: /* current process pseudo-handle */
398 obj = (struct object *)current->process;
399 break;
400 default:
401 if (!(entry = get_handle( process, handle ))) return NULL;
402 if ((entry->access & access) != access)
404 SET_ERROR( ERROR_ACCESS_DENIED );
405 return NULL;
407 obj = entry->ptr;
408 break;
410 if (ops && (obj->ops != ops))
412 SET_ERROR( ERROR_INVALID_HANDLE ); /* not the right type */
413 return NULL;
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;
461 int i;
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;