Added new_process and init_process request.
[wine] / server / request.c
1 /*
2  * Server-side request handling
3  *
4  * Copyright (C) 1998 Alexandre Julliard
5  */
6
7 #include <assert.h>
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <signal.h>
13 #include <sys/types.h>
14 #include <sys/uio.h>
15 #include <unistd.h>
16
17 #include "winerror.h"
18 #include "winnt.h"
19 #include "winbase.h"
20 #include "wincon.h"
21 #define WANT_REQUEST_HANDLERS
22 #include "server.h"
23 #include "server/request.h"
24 #include "server/process.h"
25 #include "server/thread.h"
26
27 /* check that the string is NULL-terminated and that the len is correct */
28 #define CHECK_STRING(func,str,len) \
29   do { if (((str)[(len)-1] || strlen(str) != (len)-1)) \
30          fatal_protocol_error( "%s: invalid string '%.*s'\n", (func), (len), (str) ); \
31      } while(0)
32  
33 struct thread *current = NULL;  /* thread handling the current request */
34
35 /* complain about a protocol error and terminate the client connection */
36 static void fatal_protocol_error( const char *err, ... )
37 {
38     va_list args;
39
40     va_start( args, err );
41     fprintf( stderr, "Protocol error:%p: ", current );
42     vfprintf( stderr, err, args );
43     va_end( args );
44     remove_client( current->client_fd, -2 );
45 }
46
47 /* call a request handler */
48 void call_req_handler( struct thread *thread, enum request req,
49                        void *data, int len, int fd )
50 {
51     const struct handler *handler = &req_handlers[req];
52     char *ptr;
53
54     current = thread;
55     if ((req < 0) || (req >= REQ_NB_REQUESTS))
56     {
57         fatal_protocol_error( "unknown request %d\n", req );
58         return;
59     }
60
61     if (len < handler->min_size)
62     {
63         fatal_protocol_error( "req %d bad length %d < %d)\n", req, len, handler->min_size );
64         return;
65     }
66
67     /* now call the handler */
68     if (current)
69     {
70         CLEAR_ERROR();
71         if (debug_level) trace_request( req, data, len, fd );
72     }
73     len -= handler->min_size;
74     ptr = (char *)data + handler->min_size;
75     handler->handler( data, ptr, len, fd );
76     current = NULL;
77 }
78
79 /* handle a client timeout (unused for now) */
80 void call_timeout_handler( struct thread *thread )
81 {
82     current = thread;
83     if (debug_level) trace_timeout();
84     CLEAR_ERROR();
85     thread_timeout();
86     current = NULL;
87 }
88
89 /* a thread has been killed */
90 void call_kill_handler( struct thread *thread, int exit_code )
91 {
92     /* must be reentrant WRT call_req_handler */
93     struct thread *old_current = current;
94     current = thread;
95     if (current)
96     {
97         if (debug_level) trace_kill( exit_code );
98         thread_killed( current, exit_code );
99     }
100     current = (old_current != thread) ? old_current : NULL;
101 }
102
103
104 /* create a new process */
105 DECL_HANDLER(new_process)
106 {
107     struct new_process_reply reply;
108     struct process *process;
109
110     if ((process = create_process( req )))
111     {
112         reply.pid    = process;
113         reply.handle = alloc_handle( current->process, process,
114                                      PROCESS_ALL_ACCESS, req->inherit );
115         release_object( process );
116     }
117     else
118     {
119         reply.handle = -1;
120         reply.pid    = NULL;
121     }
122     send_reply( current, -1, 1, &reply, sizeof(reply) );
123 }
124
125 /* create a new thread */
126 DECL_HANDLER(new_thread)
127 {
128     struct new_thread_reply reply;
129     int new_fd;
130
131     if ((new_fd = dup(fd)) != -1)
132     {
133         reply.tid = create_thread( new_fd, req->pid, req->suspend,
134                                    req->inherit, &reply.handle );
135         if (!reply.tid) close( new_fd );
136     }
137     else
138         SET_ERROR( ERROR_TOO_MANY_OPEN_FILES );
139
140     send_reply( current, -1, 1, &reply, sizeof(reply) );
141 }
142
143 /* initialize a new process */
144 DECL_HANDLER(init_process)
145 {
146     struct init_process_reply reply;
147     if (current->state != RUNNING)
148     {
149         fatal_protocol_error( "init_process: init_thread not called yet\n" );
150         return;
151     }
152     if (!get_process_init_info( current->process, &reply ))
153     {
154         fatal_protocol_error( "init_process: called twice\n" );
155         return;
156     }
157     send_reply( current, -1, 1, &reply, sizeof(reply) );
158 }
159
160 /* initialize a new thread */
161 DECL_HANDLER(init_thread)
162 {
163     if (current->state != STARTING)
164     {
165         fatal_protocol_error( "init_thread: already running\n" );
166         return;
167     }
168     current->state    = RUNNING;
169     current->unix_pid = req->unix_pid;
170     if (current->suspend > 0)
171         kill( current->unix_pid, SIGSTOP );
172     send_reply( current, -1, 0 );
173 }
174
175 /* set the debug level */
176 DECL_HANDLER(set_debug)
177 {
178     debug_level = req->level;
179     /* Make sure last_req is initialized */
180     current->last_req = REQ_SET_DEBUG;
181     CLEAR_ERROR();
182     send_reply( current, -1, 0 );
183 }
184
185 /* terminate a process */
186 DECL_HANDLER(terminate_process)
187 {
188     struct process *process;
189
190     if ((process = get_process_from_handle( req->handle, PROCESS_TERMINATE )))
191     {
192         kill_process( process, req->exit_code );
193         release_object( process );
194     }
195     if (current) send_reply( current, -1, 0 );
196 }
197
198 /* terminate a thread */
199 DECL_HANDLER(terminate_thread)
200 {
201     struct thread *thread;
202
203     if ((thread = get_thread_from_handle( req->handle, THREAD_TERMINATE )))
204     {
205         kill_thread( thread, req->exit_code );
206         release_object( thread );
207     }
208     if (current) send_reply( current, -1, 0 );
209 }
210
211 /* close a handle */
212 DECL_HANDLER(close_handle)
213 {
214     close_handle( current->process, req->handle );
215     send_reply( current, -1, 0 );
216 }
217
218 /* get information about a handle */
219 DECL_HANDLER(get_handle_info)
220 {
221     struct get_handle_info_reply reply;
222     reply.flags = set_handle_info( current->process, req->handle, 0, 0 );
223     send_reply( current, -1, 1, &reply, sizeof(reply) );
224 }
225
226 /* set a handle information */
227 DECL_HANDLER(set_handle_info)
228 {
229     set_handle_info( current->process, req->handle, req->mask, req->flags );
230     send_reply( current, -1, 0 );
231 }
232
233 /* duplicate a handle */
234 DECL_HANDLER(dup_handle)
235 {
236     struct dup_handle_reply reply = { -1 };
237     struct process *src, *dst;
238
239     if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE )))
240     {
241         if (req->options & DUP_HANDLE_MAKE_GLOBAL)
242         {
243             reply.handle = duplicate_handle( src, req->src_handle, NULL,
244                                              req->access, req->inherit, req->options );
245         }
246         else if ((dst = get_process_from_handle( req->dst_process, PROCESS_DUP_HANDLE )))
247         {
248             reply.handle = duplicate_handle( src, req->src_handle, dst,
249                                              req->access, req->inherit, req->options );
250             release_object( dst );
251         }
252         /* close the handle no matter what happened */
253         if (req->options & DUP_HANDLE_CLOSE_SOURCE)
254             close_handle( src, req->src_handle );
255         release_object( src );
256     }
257     send_reply( current, -1, 1, &reply, sizeof(reply) );
258 }
259
260 /* fetch information about a process */
261 DECL_HANDLER(get_process_info)
262 {
263     struct process *process;
264     struct get_process_info_reply reply = { 0, 0, 0 };
265
266     if ((process = get_process_from_handle( req->handle, PROCESS_QUERY_INFORMATION )))
267     {
268         get_process_info( process, &reply );
269         release_object( process );
270     }
271     send_reply( current, -1, 1, &reply, sizeof(reply) );
272 }
273
274 /* set information about a process */
275 DECL_HANDLER(set_process_info)
276 {
277     struct process *process;
278
279     if ((process = get_process_from_handle( req->handle, PROCESS_SET_INFORMATION )))
280     {
281         set_process_info( process, req );
282         release_object( process );
283     }
284     send_reply( current, -1, 0 );
285 }
286
287 /* fetch information about a thread */
288 DECL_HANDLER(get_thread_info)
289 {
290     struct thread *thread;
291     struct get_thread_info_reply reply = { 0, 0 };
292
293     if ((thread = get_thread_from_handle( req->handle, THREAD_QUERY_INFORMATION )))
294     {
295         get_thread_info( thread, &reply );
296         release_object( thread );
297     }
298     send_reply( current, -1, 1, &reply, sizeof(reply) );
299 }
300
301 /* set information about a thread */
302 DECL_HANDLER(set_thread_info)
303 {
304     struct thread *thread;
305
306     if ((thread = get_thread_from_handle( req->handle, THREAD_SET_INFORMATION )))
307     {
308         set_thread_info( thread, req );
309         release_object( thread );
310     }
311     send_reply( current, -1, 0 );
312 }
313
314 /* suspend a thread */
315 DECL_HANDLER(suspend_thread)
316 {
317     struct thread *thread;
318     struct suspend_thread_reply reply = { -1 };
319     if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME )))
320     {
321         reply.count = suspend_thread( thread );
322         release_object( thread );
323     }
324     send_reply( current, -1, 1, &reply, sizeof(reply) );
325     
326 }
327
328 /* resume a thread */
329 DECL_HANDLER(resume_thread)
330 {
331     struct thread *thread;
332     struct resume_thread_reply reply = { -1 };
333     if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME )))
334     {
335         reply.count = resume_thread( thread );
336         release_object( thread );
337     }
338     send_reply( current, -1, 1, &reply, sizeof(reply) );
339     
340 }
341
342 /* queue an APC for a thread */
343 DECL_HANDLER(queue_apc)
344 {
345     struct thread *thread;
346     if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT )))
347     {
348         thread_queue_apc( thread, req->func, req->param );
349         release_object( thread );
350     }
351     send_reply( current, -1, 0 );
352 }
353
354 /* open a handle to a process */
355 DECL_HANDLER(open_process)
356 {
357     struct open_process_reply reply = { -1 };
358     struct process *process = get_process_from_id( req->pid );
359     if (process)
360     {
361         reply.handle = alloc_handle( current->process, process,
362                                      req->access, req->inherit );
363         release_object( process );
364     }
365     send_reply( current, -1, 1, &reply, sizeof(reply) );
366 }
367
368 /* select on a handle list */
369 DECL_HANDLER(select)
370 {
371     if (len != req->count * sizeof(int))
372         fatal_protocol_error( "select: bad length %d for %d handles\n",
373                               len, req->count );
374     sleep_on( current, req->count, (int *)data, req->flags, req->timeout );
375 }
376
377 /* create an event */
378 DECL_HANDLER(create_event)
379 {
380     struct create_event_reply reply = { -1 };
381     struct object *obj;
382     char *name = (char *)data;
383     if (!len) name = NULL;
384     else CHECK_STRING( "create_event", name, len );
385
386     obj = create_event( name, req->manual_reset, req->initial_state );
387     if (obj)
388     {
389         reply.handle = alloc_handle( current->process, obj, EVENT_ALL_ACCESS, req->inherit );
390         release_object( obj );
391     }
392     send_reply( current, -1, 1, &reply, sizeof(reply) );
393 }
394
395 /* do an event operation */
396 DECL_HANDLER(event_op)
397 {
398     switch(req->op)
399     {
400     case PULSE_EVENT:
401         pulse_event( req->handle );
402         break;
403     case SET_EVENT:
404         set_event( req->handle );
405         break;
406     case RESET_EVENT:
407         reset_event( req->handle );
408         break;
409     default:
410         fatal_protocol_error( "event_op: invalid operation %d\n", req->op );
411     }
412     send_reply( current, -1, 0 );
413 }
414
415 /* create a mutex */
416 DECL_HANDLER(create_mutex)
417 {
418     struct create_mutex_reply reply = { -1 };
419     struct object *obj;
420     char *name = (char *)data;
421     if (!len) name = NULL;
422     else CHECK_STRING( "create_mutex", name, len );
423
424     obj = create_mutex( name, req->owned );
425     if (obj)
426     {
427         reply.handle = alloc_handle( current->process, obj, MUTEX_ALL_ACCESS, req->inherit );
428         release_object( obj );
429     }
430     send_reply( current, -1, 1, &reply, sizeof(reply) );
431 }
432
433 /* release a mutex */
434 DECL_HANDLER(release_mutex)
435 {
436     if (release_mutex( req->handle )) CLEAR_ERROR();
437     send_reply( current, -1, 0 );
438 }
439
440 /* create a semaphore */
441 DECL_HANDLER(create_semaphore)
442 {
443     struct create_semaphore_reply reply = { -1 };
444     struct object *obj;
445     char *name = (char *)data;
446     if (!len) name = NULL;
447     else CHECK_STRING( "create_semaphore", name, len );
448
449     obj = create_semaphore( name, req->initial, req->max );
450     if (obj)
451     {
452         reply.handle = alloc_handle( current->process, obj, SEMAPHORE_ALL_ACCESS, req->inherit );
453         release_object( obj );
454     }
455     send_reply( current, -1, 1, &reply, sizeof(reply) );
456 }
457
458 /* release a semaphore */
459 DECL_HANDLER(release_semaphore)
460 {
461     struct release_semaphore_reply reply;
462     if (release_semaphore( req->handle, req->count, &reply.prev_count )) CLEAR_ERROR();
463     send_reply( current, -1, 1, &reply, sizeof(reply) );
464 }
465
466 /* open a handle to a named object (event, mutex, semaphore) */
467 DECL_HANDLER(open_named_obj)
468 {
469     struct open_named_obj_reply reply;
470     char *name = (char *)data;
471     if (!len) name = NULL;
472     else CHECK_STRING( "open_named_obj", name, len );
473
474     switch(req->type)
475     {
476     case OPEN_EVENT:
477         reply.handle = open_event( req->access, req->inherit, name );
478         break;
479     case OPEN_MUTEX:
480         reply.handle = open_mutex( req->access, req->inherit, name );
481         break;
482     case OPEN_SEMAPHORE:
483         reply.handle = open_semaphore( req->access, req->inherit, name );
484         break;
485     case OPEN_MAPPING:
486         reply.handle = open_mapping( req->access, req->inherit, name );
487         break;
488     default:
489         fatal_protocol_error( "open_named_obj: invalid type %d\n", req->type );
490     }
491     send_reply( current, -1, 1, &reply, sizeof(reply) );
492 }
493
494 /* create a file */
495 DECL_HANDLER(create_file)
496 {
497     struct create_file_reply reply = { -1 };
498     struct object *obj;
499     char *name = (char *)data;
500     if (!len) name = NULL;
501     else CHECK_STRING( "create_file", name, len );
502
503     if ((obj = create_file( fd, name, req->access,
504                             req->sharing, req->create, req->attrs )) != NULL)
505     {
506         reply.handle = alloc_handle( current->process, obj, req->access, req->inherit );
507         release_object( obj );
508     }
509     send_reply( current, -1, 1, &reply, sizeof(reply) );
510 }
511
512 /* get a Unix fd to read from a file */
513 DECL_HANDLER(get_read_fd)
514 {
515     struct object *obj;
516     int read_fd;
517
518     if ((obj = get_handle_obj( current->process, req->handle, GENERIC_READ, NULL )))
519     {
520         read_fd = obj->ops->get_read_fd( obj );
521         release_object( obj );
522     }
523     else read_fd = -1;
524     send_reply( current, read_fd, 0 );
525 }
526
527 /* get a Unix fd to write to a file */
528 DECL_HANDLER(get_write_fd)
529 {
530     struct object *obj;
531     int write_fd;
532
533     if ((obj = get_handle_obj( current->process, req->handle, GENERIC_WRITE, NULL )))
534     {
535         write_fd = obj->ops->get_write_fd( obj );
536         release_object( obj );
537     }
538     else write_fd = -1;
539     send_reply( current, write_fd, 0 );
540 }
541
542 /* set a file current position */
543 DECL_HANDLER(set_file_pointer)
544 {
545     struct set_file_pointer_reply reply = { req->low, req->high };
546     set_file_pointer( req->handle, &reply.low, &reply.high, req->whence );
547     send_reply( current, -1, 1, &reply, sizeof(reply) );
548 }
549
550 /* truncate (or extend) a file */
551 DECL_HANDLER(truncate_file)
552 {
553     truncate_file( req->handle );
554     send_reply( current, -1, 0 );
555 }
556
557 /* flush a file buffers */
558 DECL_HANDLER(flush_file)
559 {
560     struct object *obj;
561
562     if ((obj = get_handle_obj( current->process, req->handle, GENERIC_WRITE, NULL )))
563     {
564         obj->ops->flush( obj );
565         release_object( obj );
566     }
567     send_reply( current, -1, 0 );
568 }
569
570 /* set a file access and modification times */
571 DECL_HANDLER(set_file_time)
572 {
573     set_file_time( req->handle, req->access_time, req->write_time );
574     send_reply( current, -1, 0 );
575 }
576
577 /* get a file information */
578 DECL_HANDLER(get_file_info)
579 {
580     struct object *obj;
581     struct get_file_info_reply reply;
582
583     if ((obj = get_handle_obj( current->process, req->handle, 0, NULL )))
584     {
585         obj->ops->get_file_info( obj, &reply );
586         release_object( obj );
587     }
588     send_reply( current, -1, 1, &reply, sizeof(reply) );
589 }
590
591 /* lock a region of a file */
592 DECL_HANDLER(lock_file)
593 {
594     struct file *file;
595
596     if ((file = get_file_obj( current->process, req->handle, 0 )))
597     {
598         file_lock( file, req->offset_high, req->offset_low,
599                    req->count_high, req->count_low );
600         release_object( file );
601     }
602     send_reply( current, -1, 0 );
603 }
604
605
606 /* unlock a region of a file */
607 DECL_HANDLER(unlock_file)
608 {
609     struct file *file;
610
611     if ((file = get_file_obj( current->process, req->handle, 0 )))
612     {
613         file_unlock( file, req->offset_high, req->offset_low,
614                      req->count_high, req->count_low );
615         release_object( file );
616     }
617     send_reply( current, -1, 0 );
618 }
619
620
621 /* create an anonymous pipe */
622 DECL_HANDLER(create_pipe)
623 {
624     struct create_pipe_reply reply = { -1, -1 };
625     struct object *obj[2];
626     if (create_pipe( obj ))
627     {
628         reply.handle_read = alloc_handle( current->process, obj[0],
629                                           STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|GENERIC_READ,
630                                           req->inherit );
631         if (reply.handle_read != -1)
632         {
633             reply.handle_write = alloc_handle( current->process, obj[1],
634                                                STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|GENERIC_WRITE,
635                                                req->inherit );
636             if (reply.handle_write == -1)
637                 close_handle( current->process, reply.handle_read );
638         }
639         release_object( obj[0] );
640         release_object( obj[1] );
641     }
642     send_reply( current, -1, 1, &reply, sizeof(reply) );
643 }
644
645 /* allocate a console for the current process */
646 DECL_HANDLER(alloc_console)
647 {
648     alloc_console( current->process );
649     send_reply( current, -1, 0 );
650 }
651
652 /* free the console of the current process */
653 DECL_HANDLER(free_console)
654 {
655     free_console( current->process );
656     send_reply( current, -1, 0 );
657 }
658
659 /* open a handle to the process console */
660 DECL_HANDLER(open_console)
661 {
662     struct object *obj;
663     struct open_console_reply reply = { -1 };
664     if ((obj = get_console( current->process, req->output )))
665     {
666         reply.handle = alloc_handle( current->process, obj, req->access, req->inherit );
667         release_object( obj );
668     }
669     send_reply( current, -1, 1, &reply, sizeof(reply) );
670 }
671
672 /* set info about a console (output only) */
673 DECL_HANDLER(set_console_info)
674 {
675     char *name = (char *)data;
676     if (!len) name = NULL;
677     else CHECK_STRING( "set_console_info", name, len );
678     set_console_info( req->handle, req, name );
679     send_reply( current, -1, 0 );
680 }
681
682 /* get info about a console (output only) */
683 DECL_HANDLER(get_console_info)
684 {
685     struct get_console_info_reply reply;
686     const char *title;
687     get_console_info( req->handle, &reply, &title );
688     send_reply( current, -1, 2, &reply, sizeof(reply),
689                 title, title ? strlen(title)+1 : 0 );
690 }
691
692 /* set a console fd */
693 DECL_HANDLER(set_console_fd)
694 {
695     set_console_fd( req->handle, fd, req->pid );
696     send_reply( current, -1, 0 );
697 }
698
699 /* get a console mode (input or output) */
700 DECL_HANDLER(get_console_mode)
701 {
702     struct get_console_mode_reply reply;
703     get_console_mode( req->handle, &reply.mode );
704     send_reply( current, -1, 1, &reply, sizeof(reply) );
705 }
706
707 /* set a console mode (input or output) */
708 DECL_HANDLER(set_console_mode)
709 {
710     set_console_mode( req->handle, req->mode );
711     send_reply( current, -1, 0 );
712 }
713
714 /* add input records to a console input queue */
715 DECL_HANDLER(write_console_input)
716 {
717     struct write_console_input_reply reply;
718     INPUT_RECORD *records = (INPUT_RECORD *)data;
719
720     if (len != req->count * sizeof(INPUT_RECORD))
721         fatal_protocol_error( "write_console_input: bad length %d for %d records\n",
722                               len, req->count );
723     reply.written = write_console_input( req->handle, req->count, records );
724     send_reply( current, -1, 1, &reply, sizeof(reply) );
725 }
726
727 /* fetch input records from a console input queue */
728 DECL_HANDLER(read_console_input)
729 {
730     read_console_input( req->handle, req->count, req->flush );
731 }
732
733 /* create a change notification */
734 DECL_HANDLER(create_change_notification)
735 {
736     struct object *obj;
737     struct create_change_notification_reply reply = { -1 };
738
739     if ((obj = create_change_notification( req->subtree, req->filter )))
740     {
741         reply.handle = alloc_handle( current->process, obj,
742                                      STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE, 0 );
743         release_object( obj );
744     }
745     send_reply( current, -1, 1, &reply, sizeof(reply) );
746 }
747
748 /* create a file mapping */
749 DECL_HANDLER(create_mapping)
750 {
751     struct object *obj;
752     struct create_mapping_reply reply = { -1 };
753     char *name = (char *)data;
754     if (!len) name = NULL;
755     else CHECK_STRING( "create_mapping", name, len );
756
757     if ((obj = create_mapping( req->size_high, req->size_low,
758                                req->protect, req->handle, name )))
759     {
760         int access = FILE_MAP_ALL_ACCESS;
761         if (!(req->protect & VPROT_WRITE)) access &= ~FILE_MAP_WRITE;
762         reply.handle = alloc_handle( current->process, obj, access, req->inherit );
763         release_object( obj );
764     }
765     send_reply( current, -1, 1, &reply, sizeof(reply) );
766 }
767
768 /* get a mapping information */
769 DECL_HANDLER(get_mapping_info)
770 {
771     struct get_mapping_info_reply reply;
772     int map_fd = get_mapping_info( req->handle, &reply );
773     send_reply( current, map_fd, 1, &reply, sizeof(reply) );
774 }
775
776 /* create a device */
777 DECL_HANDLER(create_device)
778 {
779     struct object *obj;
780     struct create_device_reply reply = { -1 };
781
782     if ((obj = create_device( req->id )))
783     {
784         reply.handle = alloc_handle( current->process, obj,
785                                      req->access, req->inherit );
786         release_object( obj );
787     }
788     send_reply( current, -1, 1, &reply, sizeof(reply) );
789 }
790
791 /* create a snapshot */
792 DECL_HANDLER(create_snapshot)
793 {
794     struct object *obj;
795     struct create_snapshot_reply reply = { -1 };
796
797     if ((obj = create_snapshot( req->flags )))
798     {
799         reply.handle = alloc_handle( current->process, obj, 0, req->inherit );
800         release_object( obj );
801     }
802     send_reply( current, -1, 1, &reply, sizeof(reply) );
803 }
804
805 /* get the next process from a snapshot */
806 DECL_HANDLER(next_process)
807 {
808     struct next_process_reply reply;
809     snapshot_next_process( req->handle, req->reset, &reply );
810     send_reply( current, -1, 1, &reply, sizeof(reply) );
811 }
812