From fdc92bae070061cf0b31c87fd1a099dfed28d679 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Sun, 14 Feb 1999 18:03:15 +0000
Subject: [PATCH] Added server snapshot support (processes only for now).

---
 include/server.h         |  26 +++
 include/server/object.h  |  41 +----
 include/server/request.h |   6 +
 misc/toolhelp.c          | 362 +++++++--------------------------------
 server/Makefile.in       |   1 +
 server/process.c         |  27 ++-
 server/request.c         |  24 +++
 server/snapshot.c        | 110 ++++++++++++
 server/trace.c           |  34 ++++
 9 files changed, 298 insertions(+), 333 deletions(-)
 create mode 100644 server/snapshot.c

diff --git a/include/server.h b/include/server.h
index f63188e2ba3..899d4bf16d4 100644
--- a/include/server.h
+++ b/include/server.h
@@ -596,6 +596,32 @@ struct create_device_reply
 };
 
 
+/* Create a snapshot */
+struct create_snapshot_request
+{
+    int          inherit;       /* inherit flag */
+    int          flags;         /* snapshot flags (TH32CS_*) */
+};
+struct create_snapshot_reply
+{
+    int          handle;        /* handle to the snapshot */
+};
+
+
+/* Get the next process from a snapshot */
+struct next_process_request
+{
+    int          handle;        /* handle to the snapshot */
+    int          reset;         /* reset snapshot position? */
+};
+struct next_process_reply
+{
+    void*        pid;          /* process id */
+    int          threads;      /* number of threads */
+    int          priority;     /* process priority */
+};
+
+
 /* client-side functions */
 
 #ifndef __WINE_SERVER__
diff --git a/include/server/object.h b/include/server/object.h
index 7fd65b874f9..7cca59633ff 100644
--- a/include/server/object.h
+++ b/include/server/object.h
@@ -20,6 +20,7 @@
 struct object;
 struct object_name;
 struct thread;
+struct process;
 struct file;
 struct wait_queue_entry;
 
@@ -119,42 +120,6 @@ extern void set_timeout( int client_fd, struct timeval *when );
 extern int send_reply_v( int client_fd, int type, int pass_fd,
                          struct iovec *vec, int veclen );
 
-/* process functions */
-
-struct process;
-
-extern struct process *create_process(void);
-extern struct process *get_process_from_id( void *id );
-extern struct process *get_process_from_handle( int handle, unsigned int access );
-extern void add_process_thread( struct process *process,
-                                struct thread *thread );
-extern void remove_process_thread( struct process *process,
-                                   struct thread *thread );
-extern void kill_process( struct process *process, int exit_code );
-extern void get_process_info( struct process *process,
-                              struct get_process_info_reply *reply );
-extern void set_process_info( struct process *process,
-                              struct set_process_info_request *req );
-extern int alloc_console( struct process *process );
-extern int free_console( struct process *process );
-extern struct object *get_console( struct process *process, int output );
-
-/* handle functions */
-
-/* alloc_handle takes a void *obj for convenience, but you better make sure */
-/* that the thing pointed to starts with a struct object... */
-extern int alloc_handle( struct process *process, void *obj,
-                         unsigned int access, int inherit );
-extern int close_handle( struct process *process, int handle );
-extern int set_handle_info( struct process *process, int handle,
-                            int mask, int flags );
-extern struct object *get_handle_obj( struct process *process, int handle,
-                                      unsigned int access, const struct object_ops *ops );
-extern int duplicate_handle( struct process *src, int src_handle, struct process *dst,
-                             int dst_handle, unsigned int access, int inherit, int options );
-extern int open_object( const char *name, const struct object_ops *ops,
-                        unsigned int access, int inherit );
-
 /* event functions */
 
 extern struct object *create_event( const char *name, int manual_reset, int initial_state );
@@ -232,6 +197,10 @@ extern int get_mapping_info( int handle, struct get_mapping_info_reply *reply );
 extern struct object *create_device( int id );
 
 
+/* snapshot functions */
+extern struct object *create_snapshot( int flags );
+extern int snapshot_next_process( int handle, int reset, struct next_process_reply *reply );
+
 extern int debug_level;
 
 #endif  /* __WINE_SERVER_OBJECT_H */
diff --git a/include/server/request.h b/include/server/request.h
index 4f6dc26b6a4..53dcc1b70bd 100644
--- a/include/server/request.h
+++ b/include/server/request.h
@@ -53,6 +53,8 @@ enum request
     REQ_CREATE_MAPPING,
     REQ_GET_MAPPING_INFO,
     REQ_CREATE_DEVICE,
+    REQ_CREATE_SNAPSHOT,
+    REQ_NEXT_PROCESS,
     REQ_NB_REQUESTS
 };
 
@@ -109,6 +111,8 @@ DECL_HANDLER(create_change_notification);
 DECL_HANDLER(create_mapping);
 DECL_HANDLER(get_mapping_info);
 DECL_HANDLER(create_device);
+DECL_HANDLER(create_snapshot);
+DECL_HANDLER(next_process);
 
 static const struct handler {
     void       (*handler)();
@@ -162,6 +166,8 @@ static const struct handler {
     { (void(*)())req_create_mapping, sizeof(struct create_mapping_request) },
     { (void(*)())req_get_mapping_info, sizeof(struct get_mapping_info_request) },
     { (void(*)())req_create_device, sizeof(struct create_device_request) },
+    { (void(*)())req_create_snapshot, sizeof(struct create_snapshot_request) },
+    { (void(*)())req_next_process, sizeof(struct next_process_request) },
 };
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/misc/toolhelp.c b/misc/toolhelp.c
index 10fb40877e5..d09f2a98b75 100644
--- a/misc/toolhelp.c
+++ b/misc/toolhelp.c
@@ -17,39 +17,15 @@
 #include "toolhelp.h"
 #include "heap.h"
 #include "k32obj.h"
+#include "server.h"
 #include "debug.h"
 
-/*
- * Support for toolhelp's snapshots.  They
- * are supposed to be Kernel32 Objects.
- * Only the Destroy() method is implemented
- */
-
-static void SNAPSHOT_Destroy( K32OBJ *obj );
-
-const K32OBJ_OPS SNAPSHOT_Ops =
-{
-  SNAPSHOT_Destroy	/* destroy */
-};
 
 /* The K32 snapshot object object */
-/* Process snapshot kernel32 object */
-typedef struct _Process32Snapshot
+typedef struct
 {
-  K32OBJ		header;
-
-  DWORD			numProcs;
-  DWORD			arrayCounter;
-  /*
-   * Store a reference to the PDB list.  
-   * Insuure in the alloc and dealloc routines for this structure that
-   * I increment and decrement the pdb->head.refcount, so that the
-   * original pdb will stay around for as long as I use it, but it's
-   * not locked forver into memory.
-   */
-  PDB32		**processArray;
-}
-SNAPSHOT_OBJECT;
+    K32OBJ header;
+} SNAPSHOT_OBJECT;
 
 /* FIXME: to make this working, we have to callback all these registered 
  * functions from all over the WINE code. Someone with more knowledge than
@@ -139,299 +115,93 @@ FARPROC16 tmp;
 	return tmp;
 }
 
-/***********************************************************************
- *	     SNAPSHOT_Destroy
- *
- * Deallocate K32 snapshot objects
- */
-static void SNAPSHOT_Destroy (K32OBJ *obj)
-{
-  int	i;
-  SNAPSHOT_OBJECT *snapshot = (SNAPSHOT_OBJECT *) obj;
-  assert (obj->type == K32OBJ_CHANGE);
-
-  if (snapshot->processArray)
-    {	
-      for (i = 0; snapshot->processArray[i] && i <snapshot->numProcs; i++)
-	{
-	  K32OBJ_DecCount (&snapshot->processArray[i]->header);
-	}
-      HeapFree (GetProcessHeap (), 0, snapshot->processArray);
-      snapshot->processArray = NULL;      
-    }	
-
-  obj->type = K32OBJ_UNKNOWN;
-  HeapFree (GetProcessHeap (), 0, snapshot);
-}
 
 /***********************************************************************
  *           CreateToolHelp32Snapshot			(KERNEL32.179)
- *	see "Undocumented Windows"
  */
-HANDLE32 WINAPI CreateToolhelp32Snapshot(DWORD dwFlags, DWORD
-					 th32ProcessID) 
+HANDLE32 WINAPI CreateToolhelp32Snapshot( DWORD flags, DWORD process ) 
 {
-  HANDLE32		ssHandle;
-  SNAPSHOT_OBJECT	*snapshot;
-  int			numProcesses;
-  int			i;
-  PDB32*		pdb;
-
-  TRACE(toolhelp, "%lx & TH32CS_INHERIT (%x) = %lx %s\n", dwFlags, 
-	  TH32CS_INHERIT,
-	  dwFlags & TH32CS_INHERIT, 
-	  dwFlags & TH32CS_INHERIT ? "TRUE" : "FALSE");  
-  TRACE(toolhelp, "%lx & TH32CS_SNAPHEAPLIST (%x) = %lx %s\n", dwFlags, 
-	  TH32CS_SNAPHEAPLIST,
-	  dwFlags & TH32CS_SNAPHEAPLIST, 
-	  dwFlags & TH32CS_SNAPHEAPLIST ? "TRUE" : "FALSE");  
-  TRACE(toolhelp, "%lx & TH32CS_SNAPMODULE (%x) = %lx %s\n", dwFlags, 
-	  TH32CS_SNAPMODULE,
-	  dwFlags & TH32CS_SNAPMODULE, 
-	  dwFlags & TH32CS_SNAPMODULE ? "TRUE" : "FALSE");  
-  TRACE(toolhelp, "%lx & TH32CS_SNAPPROCESS (%x) = %lx %s\n", dwFlags, 
-	  TH32CS_SNAPPROCESS,
-	  dwFlags & TH32CS_SNAPPROCESS, 
-	  dwFlags & TH32CS_SNAPPROCESS ? "TRUE" : "FALSE");  
-  TRACE(toolhelp, "%lx & TH32CS_SNAPTHREAD (%x) = %lx %s\n", dwFlags, 
-	  TH32CS_SNAPTHREAD,
-	  dwFlags & TH32CS_SNAPTHREAD,
-	  dwFlags & TH32CS_SNAPTHREAD ? "TRUE" : "FALSE");  
-
-  /**** FIXME: Not implmented ***/
-  if (dwFlags & TH32CS_INHERIT)
-    {
-      FIXME(toolhelp,"(0x%08lx (TH32CS_INHERIT),0x%08lx), stub!\n",
-	    dwFlags,th32ProcessID);
-
-      return INVALID_HANDLE_VALUE32;
-    }
-  if (dwFlags & TH32CS_SNAPHEAPLIST)
+    SNAPSHOT_OBJECT *snapshot;
+    struct create_snapshot_request req;
+    struct create_snapshot_reply reply;
+
+    TRACE( toolhelp, "%lx,%lx\n", flags, process );
+    if (flags & (TH32CS_SNAPHEAPLIST|TH32CS_SNAPMODULE|TH32CS_SNAPTHREAD))
+        FIXME( toolhelp, "flags %lx not implemented\n", flags );
+    if (!(flags & TH32CS_SNAPPROCESS))
     {
-      FIXME(toolhelp,"(0x%08lx (TH32CS_SNAPHEAPLIST),0x%08lx), stub!\n",
-	    dwFlags,th32ProcessID);
-      return INVALID_HANDLE_VALUE32;
-    }
-  if (dwFlags & TH32CS_SNAPMODULE)
+        SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
+        return INVALID_HANDLE_VALUE32;
+    }
+    /* Now do the snapshot */
+    if (!(snapshot = HeapAlloc( SystemHeap, 0, sizeof(*snapshot) )))
+        return INVALID_HANDLE_VALUE32;
+    snapshot->header.type = K32OBJ_TOOLHELP_SNAPSHOT;
+    snapshot->header.refcount = 1;
+
+    req.flags   = flags & ~TH32CS_INHERIT;
+    req.inherit = (flags & TH32CS_INHERIT) != 0;
+    CLIENT_SendRequest( REQ_CREATE_SNAPSHOT, -1, 1, &req, sizeof(req) );
+    if (CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL ))
     {
-      FIXME(toolhelp,"(0x%08lx (TH32CS_SNAPMODULE),0x%08lx), stub!\n",
-	    dwFlags,th32ProcessID);
-      return INVALID_HANDLE_VALUE32;
+        HeapFree( SystemHeap, 0, snapshot );
+        return INVALID_HANDLE_VALUE32;
     }
+    return HANDLE_Alloc( PROCESS_Current(), &snapshot->header, 0, req.inherit, reply.handle );
+}
 
-  if (dwFlags & TH32CS_SNAPPROCESS)
-    {
-      TRACE (toolhelp, "(0x%08lx (TH32CS_SNAPMODULE),0x%08lx)\n",
-	    dwFlags,th32ProcessID);
-      snapshot = HeapAlloc (GetProcessHeap (), 0, sizeof
-			    (SNAPSHOT_OBJECT)); 
-      if (!snapshot)
-	{
-	  return INVALID_HANDLE_VALUE32;
-	}
-
-      snapshot->header.type = K32OBJ_TOOLHELP_SNAPSHOT;
-      snapshot->header.refcount = 1;
-      snapshot->arrayCounter = 0;
-  
-      /*
-       * Lock here, to prevent processes from being created or
-       * destroyed while the snapshot is gathered
-       */
-
-      SYSTEM_LOCK ();
-      numProcesses = PROCESS_PDBList_Getsize ();
-
-      snapshot->processArray = (PDB32**) 
-	HeapAlloc (GetProcessHeap (), 0, sizeof (PDB32*) * numProcesses); 
-
-      if (!snapshot->processArray)
-        {
-          HeapFree (GetProcessHeap (), 0, snapshot->processArray);
-	  SetLastError (INVALID_HANDLE_VALUE32);
-	  ERR (toolhelp, "Error allocating %d bytes for snapshot\n", 
-	       sizeof (PDB32*) * numProcesses); 	       
-          return INVALID_HANDLE_VALUE32;
-        }
-
-      snapshot->numProcs = numProcesses;
-
-      pdb = PROCESS_PDBList_Getfirst ();
-      for (i = 0; pdb && i < numProcesses; i++)
-	{
-	  TRACE (toolhelp, "Saving ref to pdb %ld\n", PDB_TO_PROCESS_ID(pdb));
-	  snapshot->processArray[i] = pdb;
-	  K32OBJ_IncCount (&pdb->header);
-	  pdb = PROCESS_PDBList_Getnext (pdb);
-	}
-      SYSTEM_UNLOCK ();
 
-      ssHandle = HANDLE_Alloc (PROCESS_Current (), &snapshot->header,
-			       FILE_ALL_ACCESS, TRUE, -1);
-      if (ssHandle == INVALID_HANDLE_VALUE32) 
-	{
-	  /*  HANDLE_Alloc is supposed to deallocate the 
-	   *  heap memory if it fails.  This code doesn't need to.
-	   */
-	  SetLastError (INVALID_HANDLE_VALUE32);
-	  ERR (toolhelp, "Error allocating handle\n");
-	  return INVALID_HANDLE_VALUE32;
-	}
-      
-      TRACE (toolhelp, "snapshotted %d processes, expected %d\n",
-	    i, numProcesses);
-      return ssHandle;
-    }
+/***********************************************************************
+ *		TOOLHELP_Process32Next
+ *
+ * Implementation of Process32First/Next
+ */
+static BOOL32 TOOLHELP_Process32Next( HANDLE32 handle, LPPROCESSENTRY32 lppe, BOOL32 first )
+{
+    struct next_process_request req;
+    struct next_process_reply reply;
 
-  if (dwFlags & TH32CS_SNAPTHREAD)
+    if (lppe->dwSize < sizeof (PROCESSENTRY32))
     {
-      FIXME(toolhelp,"(0x%08lx (TH32CS_SNAPMODULE),0x%08lx), stub!\n",
-	    dwFlags,th32ProcessID);
-      return INVALID_HANDLE_VALUE32;
+        SetLastError( ERROR_INSUFFICIENT_BUFFER );
+        ERR (toolhelp, "Result buffer too small\n");
+        return FALSE;
     }
-
-  return INVALID_HANDLE_VALUE32;
+    if ((req.handle = HANDLE_GetServerHandle( PROCESS_Current(), handle,
+                                              K32OBJ_TOOLHELP_SNAPSHOT, 0 )) == -1)
+        return FALSE;
+    req.reset = first;
+    CLIENT_SendRequest( REQ_NEXT_PROCESS, -1, 1, &req, sizeof(req) );
+    if (CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL )) return FALSE;
+    lppe->cntUsage            = 1;
+    lppe->th32ProcessID       = (DWORD)reply.pid;
+    lppe->th32DefaultHeapID   = 0;  /* FIXME */ 
+    lppe->th32ModuleID        = 0;  /* FIXME */
+    lppe->cntThreads          = reply.threads;
+    lppe->th32ParentProcessID = 0;  /* FIXME */
+    lppe->pcPriClassBase      = reply.priority;
+    lppe->dwFlags             = -1; /* FIXME */
+    lppe->szExeFile[0]        = 0;  /* FIXME */
+    return TRUE;
 }
 
+
 /***********************************************************************
- *		Process32First
+ *		Process32First    (KERNEL32.555)
+ *
  * Return info about the first process in a toolhelp32 snapshot
  */
 BOOL32 WINAPI Process32First(HANDLE32 hSnapshot, LPPROCESSENTRY32 lppe)
 {
-  PDB32           *pdb; 
-  SNAPSHOT_OBJECT *snapshot;
-  int             i;
-
-  TRACE (toolhelp, "(0x%08lx,0x%08lx)\n", (DWORD) hSnapshot,
-	 (DWORD) lppe);
-
-  if (lppe->dwSize < sizeof (PROCESSENTRY32))
-    {
-      SetLastError (ERROR_INSUFFICIENT_BUFFER);
-      ERR (toolhelp, "Result buffer too small\n");
-      return FALSE;
-    }
-
-  SYSTEM_LOCK ();
-  snapshot = (SNAPSHOT_OBJECT*) HANDLE_GetObjPtr (PROCESS_Current (),
-						  hSnapshot,
-						  K32OBJ_UNKNOWN,
-						  FILE_ALL_ACCESS,
-						  NULL); 
-  if (!snapshot)
-    {
-      SYSTEM_UNLOCK ();
-      SetLastError (ERROR_INVALID_HANDLE);
-      ERR (toolhelp, "Error retreiving snapshot\n");
-      return FALSE;
-    }
-
-  snapshot->arrayCounter = i = 0;
-  pdb = snapshot->processArray[i];
-
-  if (!pdb)
-    {
-      SetLastError (ERROR_NO_MORE_FILES);
-      ERR (toolhelp, "End of snapshot array\n");
-      return FALSE;
-    }
-
-  TRACE (toolhelp, "Returning info on process %d, id %ld\n", 
-	 i, PDB_TO_PROCESS_ID (pdb));
-
-  lppe->cntUsage = 1;
-  lppe->th32ProcessID = PDB_TO_PROCESS_ID (pdb);
-  lppe->th32DefaultHeapID = (DWORD) pdb->heap; 
-  lppe->cntThreads = pdb->threads; 
-  lppe->th32ParentProcessID = PDB_TO_PROCESS_ID (pdb->parent); 
-  lppe->pcPriClassBase = 6; /* FIXME: this is a made-up value */
-  lppe->dwFlags = -1;       /* FIXME: RESERVED by Microsoft :-) */
-  if (pdb->exe_modref) 
-    {
-      lppe->th32ModuleID = (DWORD) pdb->exe_modref->module;
-      strncpy (lppe->szExeFile, pdb->exe_modref->longname, 
-	       sizeof (lppe->szExeFile)); 
-    }
-  else
-    {
-      lppe->th32ModuleID = (DWORD) 0;
-      strcpy (lppe->szExeFile, "");
-    }
-
-  SYSTEM_UNLOCK ();
-
-  return TRUE;
+    return TOOLHELP_Process32Next( hSnapshot, lppe, TRUE );
 }
 
 /***********************************************************************
- *		Process32Next
+ *		Process32Next   (KERNEL32.556)
+ *
  * Return info about the "next" process in a toolhelp32 snapshot
  */
 BOOL32 WINAPI Process32Next(HANDLE32 hSnapshot, LPPROCESSENTRY32 lppe)
 {
-  PDB32           *pdb; 
-  SNAPSHOT_OBJECT *snapshot;
-  int             i;
-
-  TRACE (toolhelp, "(0x%08lx,0x%08lx)\n", (DWORD) hSnapshot,
-	 (DWORD) lppe);
-
-  if (lppe->dwSize < sizeof (PROCESSENTRY32))
-    {
-      SetLastError (ERROR_INSUFFICIENT_BUFFER);
-      ERR (toolhelp, "Result buffer too small\n");
-      return FALSE;
-    }
-
-  SYSTEM_LOCK ();
-  snapshot = (SNAPSHOT_OBJECT*) HANDLE_GetObjPtr (PROCESS_Current (),
-						  hSnapshot,
-						  K32OBJ_UNKNOWN,
-						  FILE_ALL_ACCESS,
-						  NULL); 
-  if (!snapshot)
-    {
-      SYSTEM_UNLOCK ();
-      SetLastError (ERROR_INVALID_HANDLE);
-      ERR (toolhelp, "Error retreiving snapshot\n");
-      return FALSE;
-    }
-
-  snapshot->arrayCounter ++;
-  i = snapshot->arrayCounter;
-  pdb = snapshot->processArray[i];
-
-  if (!pdb || snapshot->arrayCounter >= snapshot->numProcs)
-    {
-      SetLastError (ERROR_NO_MORE_FILES);
-      ERR (toolhelp, "End of snapshot array\n");
-      return FALSE;
-    }
-
-  TRACE (toolhelp, "Returning info on process %d, id %ld\n", 
-	 i, PDB_TO_PROCESS_ID (pdb));
-
-  lppe->cntUsage = 1;
-  lppe->th32ProcessID = PDB_TO_PROCESS_ID (pdb);
-  lppe->th32DefaultHeapID = (DWORD) pdb->heap; 
-  lppe->cntThreads = pdb->threads; 
-  lppe->th32ParentProcessID = PDB_TO_PROCESS_ID (pdb->parent); 
-  lppe->pcPriClassBase = 6; /* FIXME: this is a made-up value */
-  lppe->dwFlags = -1;       /* FIXME: RESERVED by Microsoft :-) */
-  if (pdb->exe_modref) 
-    {
-      lppe->th32ModuleID = (DWORD) pdb->exe_modref->module;
-      strncpy (lppe->szExeFile, pdb->exe_modref->longname, 
-	       sizeof (lppe->szExeFile)); 
-    }
-  else
-    {
-      lppe->th32ModuleID = (DWORD) 0;
-      strcpy (lppe->szExeFile, "");
-    }
-
-  SYSTEM_UNLOCK ();
-
-  return TRUE;
+    return TOOLHELP_Process32Next( hSnapshot, lppe, FALSE );
 }
diff --git a/server/Makefile.in b/server/Makefile.in
index b8438a78d5c..f062a95cee4 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -17,6 +17,7 @@ C_SRCS = \
 	pipe.c \
 	process.c \
 	request.c \
+	snapshot.c \
 	select.c \
 	semaphore.c \
 	socket.c \
diff --git a/server/process.c b/server/process.c
index 6cd3e581c09..6e65433ab64 100644
--- a/server/process.c
+++ b/server/process.c
@@ -17,6 +17,7 @@
 #include "winnt.h"
 
 #include "server.h"
+#include "server/process.h"
 #include "server/thread.h"
 
 /* reserved handle access rights */
@@ -60,6 +61,7 @@ struct process
 
 static struct process *first_process;
 static struct process *initial_process;
+static int running_processes;
 
 #define MIN_HANDLE_ENTRIES  32
 
@@ -214,7 +216,7 @@ void add_process_thread( struct process *process, struct thread *thread )
     thread->proc_prev = NULL;
     if (thread->proc_next) thread->proc_next->proc_prev = thread;
     process->thread_list = thread;
-    process->running_threads++;
+    if (!process->running_threads++) running_processes++;
     grab_object( thread );
 }
 
@@ -231,6 +233,7 @@ void remove_process_thread( struct process *process, struct thread *thread )
     if (!--process->running_threads)
     {
         /* we have removed the last running thread, exit the process */
+        running_processes--;
         process_killed( process, thread->exit_code );
     }
     release_object( thread );
@@ -573,3 +576,25 @@ struct object *get_console( struct process *process, int output )
         return NULL;
     return grab_object( obj );
 }
+
+/* take a snapshot of currently running processes */
+struct process_snapshot *process_snap( int *count )
+{
+    struct process_snapshot *snapshot, *ptr;
+    struct process *process;
+    if (!running_processes) return NULL;
+    if (!(snapshot = mem_alloc( sizeof(*snapshot) * running_processes )))
+        return NULL;
+    ptr = snapshot;
+    for (process = first_process; process; process = process->next)
+    {
+        if (!process->running_threads) continue;
+        ptr->process  = process;
+        ptr->threads  = process->running_threads;
+        ptr->priority = process->priority;
+        grab_object( process );
+        ptr++;
+    }
+    *count = running_processes;
+    return snapshot;
+}
diff --git a/server/request.c b/server/request.c
index b4b071a14c8..c5fefd9176c 100644
--- a/server/request.c
+++ b/server/request.c
@@ -19,6 +19,7 @@
 #define WANT_REQUEST_HANDLERS
 #include "server.h"
 #include "server/request.h"
+#include "server/process.h"
 #include "server/thread.h"
 
 /* check that the string is NULL-terminated and that the len is correct */
@@ -754,3 +755,26 @@ DECL_HANDLER(create_device)
     }
     send_reply( current, -1, 1, &reply, sizeof(reply) );
 }
+
+/* create a snapshot */
+DECL_HANDLER(create_snapshot)
+{
+    struct object *obj;
+    struct create_snapshot_reply reply = { -1 };
+
+    if ((obj = create_snapshot( req->flags )))
+    {
+        reply.handle = alloc_handle( current->process, obj, 0, req->inherit );
+        release_object( obj );
+    }
+    send_reply( current, -1, 1, &reply, sizeof(reply) );
+}
+
+/* get the next process from a snapshot */
+DECL_HANDLER(next_process)
+{
+    struct next_process_reply reply;
+    snapshot_next_process( req->handle, req->reset, &reply );
+    send_reply( current, -1, 1, &reply, sizeof(reply) );
+}
+
diff --git a/server/snapshot.c b/server/snapshot.c
new file mode 100644
index 00000000000..ee189937658
--- /dev/null
+++ b/server/snapshot.c
@@ -0,0 +1,110 @@
+/*
+ * Server-side snapshots
+ *
+ * Copyright (C) 1999 Alexandre Julliard
+ *
+ * FIXME: only process snapshots implemented for now
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "winerror.h"
+#include "winnt.h"
+#include "tlhelp32.h"
+#include "server/process.h"
+#include "server/thread.h"
+
+
+struct snapshot
+{
+    struct object             obj;           /* object header */
+    struct process_snapshot  *process;       /* processes snapshot */
+    int                       process_count; /* count of processes */
+    int                       process_pos;   /* current position in proc snapshot */
+};
+
+static void snapshot_dump( struct object *obj, int verbose );
+static void snapshot_destroy( struct object *obj );
+
+static const struct object_ops snapshot_ops =
+{
+    snapshot_dump,
+    no_add_queue,
+    NULL,  /* should never get called */
+    NULL,  /* should never get called */
+    NULL,  /* should never get called */
+    no_read_fd,
+    no_write_fd,
+    no_flush,
+    no_get_file_info,
+    snapshot_destroy
+};
+
+
+/* create a new snapshot */
+struct object *create_snapshot( int flags )
+{
+    struct snapshot *snapshot;
+    if (!(snapshot = mem_alloc( sizeof(*snapshot) ))) return NULL;
+    init_object( &snapshot->obj, &snapshot_ops, NULL );
+    if (flags & TH32CS_SNAPPROCESS)
+        snapshot->process = process_snap( &snapshot->process_count );
+    else
+        snapshot->process_count = 0;
+
+    snapshot->process_pos = 0;
+    return &snapshot->obj;
+}
+
+/* get the next process in the snapshot */
+int snapshot_next_process( int handle, int reset, struct next_process_reply *reply )
+{
+    struct snapshot *snapshot;
+    struct process_snapshot *ptr;
+    if (!(snapshot = (struct snapshot *)get_handle_obj( current->process, handle,
+                                                        0, &snapshot_ops )))
+        return 0;
+    if (!snapshot->process_count)
+    {
+        SET_ERROR( ERROR_INVALID_PARAMETER );  /* FIXME */
+        release_object( snapshot );
+        return 0;
+    }
+    if (reset) snapshot->process_pos = 0;
+    else if (snapshot->process_pos >= snapshot->process_count)
+    {
+        SET_ERROR( ERROR_NO_MORE_FILES );
+        release_object( snapshot );
+        return 0;
+    }
+    ptr = &snapshot->process[snapshot->process_pos++];
+    reply->pid      = ptr->process;
+    reply->threads  = ptr->threads;
+    reply->priority = ptr->priority;
+    release_object( snapshot );
+    return 1;
+}
+
+static void snapshot_dump( struct object *obj, int verbose )
+{
+    struct snapshot *snapshot = (struct snapshot *)obj;
+    assert( obj->ops == &snapshot_ops );
+    fprintf( stderr, "Snapshot: %d processes\n",
+             snapshot->process_count );
+}
+
+static void snapshot_destroy( struct object *obj )
+{
+    int i;
+    struct snapshot *snapshot = (struct snapshot *)obj;
+    assert( obj->ops == &snapshot_ops );
+    if (snapshot->process_count)
+    {
+        for (i = 0; i < snapshot->process_count; i++)
+            release_object( snapshot->process[i].process );
+        free( snapshot->process );
+    }
+    free( snapshot );
+}
diff --git a/server/trace.c b/server/trace.c
index bf4a006ab5b..16ef4ff1983 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -541,6 +541,34 @@ static int dump_create_device_reply( struct create_device_reply *req, int len )
     return (int)sizeof(*req);
 }
 
+static int dump_create_snapshot_request( struct create_snapshot_request *req, int len )
+{
+    fprintf( stderr, " inherit=%d,", req->inherit );
+    fprintf( stderr, " flags=%d", req->flags );
+    return (int)sizeof(*req);
+}
+
+static int dump_create_snapshot_reply( struct create_snapshot_reply *req, int len )
+{
+    fprintf( stderr, " handle=%d", req->handle );
+    return (int)sizeof(*req);
+}
+
+static int dump_next_process_request( struct next_process_request *req, int len )
+{
+    fprintf( stderr, " handle=%d,", req->handle );
+    fprintf( stderr, " reset=%d", req->reset );
+    return (int)sizeof(*req);
+}
+
+static int dump_next_process_reply( struct next_process_reply *req, int len )
+{
+    fprintf( stderr, " pid=%p,", req->pid );
+    fprintf( stderr, " threads=%d,", req->threads );
+    fprintf( stderr, " priority=%d", req->priority );
+    return (int)sizeof(*req);
+}
+
 struct dumper
 {
     int (*dump_req)( void *data, int len );
@@ -645,6 +673,10 @@ static const struct dumper dumpers[REQ_NB_REQUESTS] =
       (void(*)())dump_get_mapping_info_reply },
     { (int(*)(void *,int))dump_create_device_request,
       (void(*)())dump_create_device_reply },
+    { (int(*)(void *,int))dump_create_snapshot_request,
+      (void(*)())dump_create_snapshot_reply },
+    { (int(*)(void *,int))dump_next_process_request,
+      (void(*)())dump_next_process_reply },
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] =
@@ -697,6 +729,8 @@ static const char * const req_names[REQ_NB_REQUESTS] =
     "create_mapping",
     "get_mapping_info",
     "create_device",
+    "create_snapshot",
+    "next_process",
 };
 
 void trace_request( enum request req, void *data, int len, int fd )
-- 
GitLab