SM: simplify and fix client (subsystem servers) management.
[reactos.git] / reactos / subsys / smss / client.c
index def3407..21dc329 100644 (file)
-/* $Id: smss.c 12852 2005-01-06 13:58:04Z mf $\r
- *\r
- * client.c - Session Manager client Management\r
- * \r
- * ReactOS Operating System\r
- * \r
- * --------------------------------------------------------------------\r
- *\r
- * This software is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU General Public License as\r
- * published by the Free Software Foundation; either version 2 of the\r
- * License, or (at your option) any later version.\r
- *\r
- * This software is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
- * General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this software; see the file COPYING.LIB. If not, write\r
- * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,\r
- * MA 02139, USA.  \r
- *\r
- * --------------------------------------------------------------------\r
- */\r
-#define NTOS_MODE_USER\r
-#include <ntos.h>\r
-#include <sm/api.h>\r
-#include "smss.h"\r
-\r
-/* Private ADT */\r
-\r
-typedef struct _SM_CLIENT_DATA\r
-{\r
-       USHORT  SubsystemId;\r
-       BOOL    Initialized;\r
-       HANDLE  ServerProcess;\r
-       HANDLE  ApiPort;\r
-       HANDLE  SbApiPort;\r
-       struct _SM_CLIENT_DATA * Next;\r
-       \r
-} SM_CLIENT_DATA, *PSM_CLIENT_DATA;\r
-\r
-\r
-struct _SM_CLIENT_DIRECTORY\r
-{\r
-       RTL_CRITICAL_SECTION  Lock;\r
-       ULONG                 Count;\r
-       PSM_CLIENT_DATA       Client;\r
-\r
-} SmpClientDirectory;\r
-\r
-/**********************************************************************\r
- *     SmpInitializeClientManagement/0\r
- */\r
-VOID STDCALL\r
-SmpInitializeClientManagement (VOID)\r
-{\r
-       RtlInitializeCriticalSection(& SmpClientDirectory.Lock);\r
-       SmpClientDirectory.Count = 0;\r
-       SmpClientDirectory.Client = NULL;\r
-}\r
-\r
-/**********************************************************************\r
- *     SmpCreateClient/1\r
- */\r
-NTSTATUS STDCALL\r
-SmpCreateClient(SM_PORT_MESSAGE Request)\r
-{\r
-       PSM_CLIENT_DATA pClient = NULL;\r
-\r
-       /*\r
-        * Allocate the storage for client data\r
-        */\r
-       pClient = RtlAllocateHeap (SmpHeap,\r
-                                  HEAP_ZERO_MEMORY,\r
-                                  sizeof (SM_CLIENT_DATA));\r
-       if (NULL == pClient) return STATUS_NO_MEMORY;\r
-       /*\r
-        * Initialize the client data\r
-        */\r
-       // TODO\r
-       /*\r
-        * Insert the new descriptor in the\r
-        * client directory.\r
-        */\r
-       RtlEnterCriticalSection (& SmpClientDirectory.Lock);\r
-       if (NULL == SmpClientDirectory.Client)\r
-       {\r
-               SmpClientDirectory.Client = pClient;\r
-       } else {\r
-               PSM_CLIENT_DATA pCD = NULL;\r
-\r
-               for (pCD=SmpClientDirectory.Client;\r
-                       (NULL != pCD->Next);\r
-                       pCD = pCD->Next);\r
-               pCD->Next = pClient;\r
-       }\r
-       ++ SmpClientDirectory.Count;\r
-       RtlLeaveCriticalSection (& SmpClientDirectory.Lock);\r
-       return STATUS_SUCCESS;\r
-}\r
-\r
-/**********************************************************************\r
- *     SmpDestroyClient/1\r
- */\r
-NTSTATUS STDCALL\r
-SmpDestroyClient (ULONG SubsystemId)\r
-{\r
-       RtlEnterCriticalSection (& SmpClientDirectory.Lock);\r
-       /* TODO */\r
-       RtlLeaveCriticalSection (& SmpClientDirectory.Lock);\r
-       return STATUS_SUCCESS;\r
-}\r
-\r
-/* EOF */\r
+/* $Id$
+ *
+ * client.c - Session Manager client Management
+ * 
+ * ReactOS Operating System
+ * 
+ * --------------------------------------------------------------------
+ *
+ * This software is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING.LIB. If not, write
+ * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ * MA 02139, USA.  
+ *
+ * --------------------------------------------------------------------
+ */
+#include "smss.h"
+#include <sm/helper.h>
+
+#define NDEBUG
+#include <debug.h>
+
+/* Private ADT */
+
+#define SM_MAX_CLIENT_COUNT 16
+#define SM_INVALID_CLIENT_INDEX -1
+
+struct _SM_CLIENT_DIRECTORY
+{
+       RTL_CRITICAL_SECTION  Lock;
+       ULONG                 Count;
+       PSM_CLIENT_DATA       Client [SM_MAX_CLIENT_COUNT];
+       PSM_CLIENT_DATA       CandidateClient;
+
+} SmpClientDirectory;
+
+
+/**********************************************************************
+ *     SmInitializeClientManagement/0
+ */
+NTSTATUS
+SmInitializeClientManagement (VOID)
+{
+       DPRINT("SM: %s called\n", __FUNCTION__);
+       RtlInitializeCriticalSection(& SmpClientDirectory.Lock);
+       SmpClientDirectory.Count = 0;
+       RtlZeroMemory (SmpClientDirectory.Client, sizeof SmpClientDirectory.Client);
+       SmpClientDirectory.CandidateClient = NULL;
+       return STATUS_SUCCESS;
+
+}
+/**********************************************************************
+ * SmpSetClientInitialized/1
+ */
+VOID FASTCALL
+SmpSetClientInitialized (PSM_CLIENT_DATA Client)
+{
+       DPRINT("SM: %s(%08lx) called\n", __FUNCTION__, Client);
+       Client->Flags |= SM_CLIENT_FLAG_INITIALIZED;
+}
+/**********************************************************************
+ * SmpGetFirstFreeClientEntry/0                                        PRIVATE
+ *
+ * NOTE: call it holding SmpClientDirectory.Lock only
+ */
+static INT STDCALL SmpGetFirstFreeClientEntry (VOID)
+{
+       INT ClientIndex = 0;
+
+       DPRINT("SM: %s called\n", __FUNCTION__);
+
+       if (SmpClientDirectory.Count < SM_MAX_CLIENT_COUNT)
+       {
+               for (ClientIndex = 0;
+                       (ClientIndex < SM_MAX_CLIENT_COUNT);
+                       ClientIndex ++)
+               {
+                       if (NULL == SmpClientDirectory.Client[ClientIndex])
+                       {
+                               DPRINT("SM: %s => %d\n", __FUNCTION__, ClientIndex);
+                               return ClientIndex; // found
+                       }
+               }
+       }
+       return SM_INVALID_CLIENT_INDEX; // full!                
+}
+/**********************************************************************
+ *     SmpLookupClient/1                                       PRIVATE
+ *
+ * DESCRIPTION
+ *     Lookup the subsystem server descriptor (client data) given its
+ *     base image ID.
+ *
+ * ARGUMENTS
+ *     SubsystemId: IMAGE_SUBSYSTEM_xxx
+ *
+ * RETURN VALUES
+ *     SM_INVALID_CLIENT_INDEX on error;
+ *     otherwise an index in the range (0..SM_MAX_CLIENT_COUNT).
+ *
+ * WARNING
+ *     SmpClientDirectory.Lock must be held by the caller.
+ */
+static INT FASTCALL
+SmpLookupClient (USHORT SubsystemId)
+{
+       INT  ClientIndex = 0;
+
+       DPRINT("SM: %s(%d) called\n", __FUNCTION__, SubsystemId);
+
+       if (0 != SmpClientDirectory.Count)
+       {
+               for (ClientIndex = 0; (ClientIndex < SM_MAX_CLIENT_COUNT); ClientIndex ++)
+               {
+                       if (NULL != SmpClientDirectory.Client[ClientIndex])
+                       {
+                               if (SubsystemId == SmpClientDirectory.Client[ClientIndex]->SubsystemId)
+                               {
+                                       return ClientIndex;
+                               }
+                       }
+               }
+       }       
+       return SM_INVALID_CLIENT_INDEX;
+}
+/**********************************************************************
+ *     SmpDestroyClientObject/2                                PRIVATE
+ *
+ * WARNING
+ *     SmpClientDirectory.Lock must be held by the caller.
+ */
+static NTSTATUS STDCALL
+SmpDestroyClientObject (PSM_CLIENT_DATA Client, NTSTATUS DestroyReason)
+{
+       DPRINT("SM:%s(%08lx,%08lx) called\n", __FUNCTION__, DestroyReason);
+       /* TODO: send shutdown to the SB port */
+       NtTerminateProcess (Client->ServerProcess, DestroyReason);
+       RtlFreeHeap (SmpHeap, 0, Client);
+       -- SmpClientDirectory.Count;
+       return STATUS_SUCCESS;
+}
+/**********************************************************************
+ *     SmBeginClientInitialization/1
+ *
+ * DESCRIPTION
+ *     Check if the candidate client matches the begin session
+ *     message from the subsystem process.
+ *
+ * ARGUMENTS
+ *     Request: message received by \SmApiPort
+ *     ClientData:
+ *             
+ * RETURN VALUES
+ *     NTSTATUS
+ */
+NTSTATUS STDCALL
+SmBeginClientInitialization (IN  PSM_PORT_MESSAGE Request,
+                            OUT PSM_CLIENT_DATA  * ClientData)
+{
+       NTSTATUS          Status = STATUS_SUCCESS;
+       PSM_CONNECT_DATA  ConnectData = SmpGetConnectData (Request);
+       ULONG             SbApiPortNameSize = SM_CONNECT_DATA_SIZE(*Request);
+       INT               ClientIndex = SM_INVALID_CLIENT_INDEX;
+
+
+       DPRINT("SM: %s(%08lx,%08lx) called\n", __FUNCTION__,
+                       Request, ClientData);
+       
+       RtlEnterCriticalSection (& SmpClientDirectory.Lock);
+       /*
+        * Is there a subsystem bootstrap in progress?
+        */
+       if (NULL != SmpClientDirectory.CandidateClient)
+       {
+               PROCESS_BASIC_INFORMATION pbi;
+               
+               RtlZeroMemory (& pbi, sizeof pbi);
+               Status = NtQueryInformationProcess (Request->Header.ClientId.UniqueProcess,
+                                                   ProcessBasicInformation,
+                                                   & pbi,
+                                                   sizeof pbi,
+                                                   NULL);
+               if (NT_SUCCESS(Status))
+               {
+                       SmpClientDirectory.CandidateClient->ServerProcessId =
+                               (ULONG) pbi.UniqueProcessId;
+               }
+       }
+       else
+       {
+               DPRINT1("SM: %s: subsys booting with no descriptor!\n", __FUNCTION__);
+               Status = STATUS_NOT_FOUND;
+               RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
+               return Status;          
+       }
+       /*
+        * Check if a client for the ID already exist.
+        */
+       if (SM_INVALID_CLIENT_INDEX != SmpLookupClient(ConnectData->SubSystemId))
+       {
+               DPRINT("SM: %s: attempt to register again subsystem %d.\n",
+                       __FUNCTION__,
+                       ConnectData->SubSystemId);
+               // TODO something else to do here?
+               RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
+               return STATUS_UNSUCCESSFUL;
+       }
+       /*
+        * Check if a free entry exists in SmpClientDirectory.Client[].
+        */
+       ClientIndex = SmpGetFirstFreeClientEntry();
+       if (SM_INVALID_CLIENT_INDEX == ClientIndex)
+       {
+               DPRINT("SM: %s: SM_INVALID_CLIENT_INDEX == ClientIndex ", __FUNCTION__);
+               SmpDestroyClientObject (SmpClientDirectory.CandidateClient, STATUS_NO_MEMORY);
+               SmpClientDirectory.CandidateClient = NULL;
+               return STATUS_NO_MEMORY;
+       }
+
+       /* OK! */
+       DPRINT("SM: %s: registering subsystem ID=%d \n",
+               __FUNCTION__, ConnectData->SubSystemId);
+
+       /*
+        * Initialize the client data
+        */
+       SmpClientDirectory.CandidateClient->SubsystemId = ConnectData->SubSystemId;
+       /* SM && DBG auto-initializes; other subsystems are required to call
+        * SM_API_COMPLETE_SESSION via SMDLL. */
+       if ((IMAGE_SUBSYSTEM_NATIVE == SmpClientDirectory.CandidateClient->SubsystemId) ||
+           ((USHORT)-1 == SmpClientDirectory.CandidateClient->SubsystemId))
+       {
+               SmpSetClientInitialized (SmpClientDirectory.CandidateClient);
+       }
+       if (SbApiPortNameSize > 0)
+       {
+               /* Only external servers have an SB port */
+               RtlCopyMemory (SmpClientDirectory.CandidateClient->SbApiPortName,
+                              ConnectData->SbName,
+                              SbApiPortNameSize);
+       }
+       /*
+        * Insert the new descriptor in the
+        * client directory.
+        */
+       SmpClientDirectory.Client [ClientIndex] = SmpClientDirectory.CandidateClient;
+       /*
+        * Increment the number of active subsystems.
+        */
+       ++ SmpClientDirectory.Count;
+       /*
+        * Notify to the caller the reference to the client data.
+        */
+       if (ClientData) 
+       {
+               *ClientData = SmpClientDirectory.CandidateClient;
+       }
+       /*
+        * Free the slot for the candidate subsystem.
+        */
+       SmpClientDirectory.CandidateClient = NULL;
+
+       /* Done */
+       RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
+       
+       return STATUS_SUCCESS;
+}
+/**********************************************************************
+ *     SmCompleteClientInitialization/1
+ *
+ * DESCRIPTION
+ *     Lookup the subsystem server descriptor given the process ID
+ *     of the subsystem server process.
+ */
+NTSTATUS STDCALL
+SmCompleteClientInitialization (ULONG ProcessId)
+{
+       NTSTATUS  Status = STATUS_NOT_FOUND;
+       INT       ClientIndex = SM_INVALID_CLIENT_INDEX;
+
+       DPRINT("SM: %s(%lu) called\n", __FUNCTION__, ProcessId);
+
+       RtlEnterCriticalSection (& SmpClientDirectory.Lock);
+       if (SmpClientDirectory.Count > 0)
+       {
+               for (ClientIndex = 0; ClientIndex < SM_MAX_CLIENT_COUNT; ClientIndex ++)
+               {               
+                       if ((NULL != SmpClientDirectory.Client [ClientIndex]) &&
+                               (ProcessId == SmpClientDirectory.Client [ClientIndex]->ServerProcessId))
+                       {
+                               SmpSetClientInitialized (SmpClientDirectory.Client [ClientIndex]);
+                               Status = STATUS_SUCCESS;
+                               break;
+                       }
+               }
+       }
+       RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
+       return Status;
+}
+/**********************************************************************
+ *     SmpDestroyClientByClientIndex/1                         PRIVATE
+ */
+static NTSTATUS STDCALL
+SmpDestroyClientByClientIndex (INT ClientIndex)
+{
+       NTSTATUS         Status = STATUS_SUCCESS;
+       PSM_CLIENT_DATA  Client = NULL;
+
+       DPRINT("SM: %s(%d) called\n", __FUNCTION__, ClientIndex);
+
+       if (SM_INVALID_CLIENT_INDEX == ClientIndex)
+       {
+               DPRINT1("SM: %s: SM_INVALID_CLIENT_INDEX == ClientIndex!\n",
+                       __FUNCTION__);
+               Status = STATUS_NOT_FOUND;
+       }
+       else
+       {
+               Client = SmpClientDirectory.Client [ClientIndex];
+               SmpClientDirectory.Client [ClientIndex] = NULL;
+               if (NULL != Client)
+               {
+                       Status = SmpDestroyClientObject (Client, STATUS_SUCCESS);
+               } else {
+                       DPRINT("SM:%s: NULL == Client[%d]!\n", __FUNCTION__,
+                               ClientIndex);
+                       Status = STATUS_UNSUCCESSFUL;
+               }
+       }
+       return Status;
+}
+/**********************************************************************
+ *     SmpTimeoutCandidateClient/1
+ *
+ * DESCRIPTION
+ *     Give the candidate client time to bootstrap and complete
+ *     session initialization. If the client fails in any way,
+ *     drop the pending client and kill the process.
+ *
+ * ARGUMENTS
+ *     x: HANDLE for the candidate process.
+ *
+ * RETURN VALUE
+ *     NONE.
+ */
+static VOID STDCALL SmpTimeoutCandidateClient (PVOID x)
+{
+       NTSTATUS       Status = STATUS_SUCCESS;
+       HANDLE         CandidateClientProcessHandle = (HANDLE) x;
+       LARGE_INTEGER  TimeOut;
+
+       DPRINT("SM: %s(%lx) called\n", __FUNCTION__, x);
+
+       TimeOut.QuadPart = (LONGLONG) -300000000L; // 30s
+       Status = NtWaitForSingleObject (CandidateClientProcessHandle,
+                                       FALSE,
+                                       & TimeOut);
+       if (STATUS_TIMEOUT == Status)
+       {
+               RtlEnterCriticalSection (& SmpClientDirectory.Lock);
+               if (NULL != SmpClientDirectory.CandidateClient)
+               {
+                       DPRINT("SM:%s: destroy candidate %08lx\n", __FUNCTION__,
+                                       SmpClientDirectory.CandidateClient);
+                       Status = SmpDestroyClientObject (SmpClientDirectory.CandidateClient,
+                                                        STATUS_TIMEOUT);
+                       SmpClientDirectory.CandidateClient = NULL;
+               }
+               RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
+       }
+       NtTerminateThread (NtCurrentThread(), Status);
+}
+/**********************************************************************
+ *     SmpCreateClient/1
+ *
+ * DESCRIPTION
+ *     Create a "candidate" client. Client descriptor will enter the
+ *     client directory only at the end of the registration
+ *     procedure. Otherwise, we will kill the associated process.
+ *
+ * ARGUMENTS
+ *     ProcessHandle: handle of the subsystem server process.
+ *
+ * RETURN VALUE
+ *     NTSTATUS:
+ *             STATUS_SUCCESS if all OK;
+ *             STATUS_DEVICE_BUSY if another SS is still booting;
+ *             STATUS_NO_MEMORY if client descriptor allocation failed;
+ *             
+ *             
+ */
+NTSTATUS STDCALL
+SmCreateClient (PRTL_USER_PROCESS_INFORMATION ProcessInfo, PWSTR ProgramName)
+{
+       NTSTATUS Status = STATUS_SUCCESS;
+       
+       DPRINT("SM: %s(%lx, %S) called\n", __FUNCTION__, ProcessInfo->ProcessHandle, ProgramName);
+
+       RtlEnterCriticalSection (& SmpClientDirectory.Lock);
+       /*
+        * Check if the candidate client slot is empty.
+        */
+       if (NULL == SmpClientDirectory.CandidateClient)
+       {
+               /*
+                * Check if there exist a free entry in the
+                * SmpClientDirectory.Client array.
+                */
+               if (SM_INVALID_CLIENT_INDEX == SmpGetFirstFreeClientEntry())
+               {
+                       DPRINT("SM: %s(%lx): out of memory!\n",
+                               __FUNCTION__, ProcessInfo->ProcessHandle);
+                       Status = STATUS_NO_MEMORY;
+               }
+               /*
+                * Allocate the storage for client data
+                */
+               SmpClientDirectory.CandidateClient =
+                       RtlAllocateHeap (SmpHeap,
+                                        HEAP_ZERO_MEMORY,
+                                        sizeof (SM_CLIENT_DATA));
+               if (NULL == SmpClientDirectory.CandidateClient)
+               {
+                       DPRINT("SM: %s(%lx): out of memory!\n",
+                               __FUNCTION__, ProcessInfo->ProcessHandle);
+                       Status = STATUS_NO_MEMORY;
+               }
+               else
+               {
+                       DPRINT("SM:%s(%08lx,%S): candidate is %08lx\n", __FUNCTION__,
+                                       ProcessInfo, ProgramName, SmpClientDirectory.CandidateClient);
+                       /* Initialize the candidate client. */
+                       RtlInitializeCriticalSection(& SmpClientDirectory.CandidateClient->Lock);
+                       SmpClientDirectory.CandidateClient->ServerProcess =
+                               (HANDLE) ProcessInfo->ProcessHandle;
+                       SmpClientDirectory.CandidateClient->ServerProcessId = 
+                               (ULONG) ProcessInfo->ClientId.UniqueProcess;
+                       /*
+                        * Copy the program name
+                        */
+                       RtlCopyMemory (SmpClientDirectory.CandidateClient->ProgramName,
+                                      ProgramName,
+                                      SM_SB_NAME_MAX_LENGTH);
+               }
+       } else {
+               DPRINT1("SM: %s: CandidateClient %08lx pending!\n", __FUNCTION__,
+                               SmpClientDirectory.CandidateClient);
+               Status = STATUS_DEVICE_BUSY;
+       }
+
+       RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
+
+       /* Create the timeout thread for external subsystems */
+       if (_wcsicmp (ProgramName, L"Session Manager") && _wcsicmp (ProgramName, L"Debug"))
+       {
+               Status = RtlCreateUserThread (NtCurrentProcess(),
+                                               NULL,
+                                               FALSE,
+                                               0,
+                                               0,
+                                               0,
+                                               (PTHREAD_START_ROUTINE) SmpTimeoutCandidateClient,
+                                               SmpClientDirectory.CandidateClient->ServerProcess,
+                                               NULL,
+                                               NULL);
+               if (!NT_SUCCESS(Status))
+               {
+                       DPRINT1("SM:%s: RtlCreateUserThread() failed (Status=%08lx)\n",
+                               __FUNCTION__, Status);
+               }
+       }
+
+       return Status;
+}
+/**********************************************************************
+ *     SmpDestroyClient/1
+ *
+ *     1. close any handle
+ *     2. kill client process
+ *     3. release resources
+ */
+NTSTATUS STDCALL
+SmDestroyClient (ULONG SubsystemId)
+{
+       NTSTATUS  Status = STATUS_SUCCESS;
+       INT       ClientIndex = SM_INVALID_CLIENT_INDEX;
+
+       DPRINT("SM: %s(%lu) called\n", __FUNCTION__, SubsystemId);
+
+       RtlEnterCriticalSection (& SmpClientDirectory.Lock);
+       ClientIndex = SmpLookupClient (SubsystemId);
+       if (SM_INVALID_CLIENT_INDEX == ClientIndex)
+       {
+               DPRINT1("SM: %s: del req for non existent subsystem (id=%d)\n",
+                       __FUNCTION__, SubsystemId);
+               return STATUS_NOT_FOUND;
+       }
+       Status = SmpDestroyClientByClientIndex (ClientIndex);
+       RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
+       return Status;
+}
+
+/* === Utilities for SmQryInfo === */
+
+/**********************************************************************
+ * SmGetClientBasicInformation/1
+ */
+NTSTATUS FASTCALL
+SmGetClientBasicInformation (PSM_BASIC_INFORMATION i)
+{
+       INT  ClientIndex = 0;
+       INT  Index = 0;
+
+       DPRINT("SM: %s(%08lx) called\n", __FUNCTION__, i);
+
+       RtlEnterCriticalSection (& SmpClientDirectory.Lock);
+
+       i->SubSystemCount = SmpClientDirectory.Count;
+       i->Unused = 0;
+       
+       if (SmpClientDirectory.Count > 0)
+       {
+               for (ClientIndex = 0; (ClientIndex < SM_MAX_CLIENT_COUNT); ClientIndex ++)
+               {
+                       if ((NULL != SmpClientDirectory.Client [ClientIndex]) &&
+                               (Index < SM_QRYINFO_MAX_SS_COUNT))
+                       {
+                               i->SubSystem[Index].Id        = SmpClientDirectory.Client [ClientIndex]->SubsystemId;
+                               i->SubSystem[Index].Flags     = SmpClientDirectory.Client [ClientIndex]->Flags;
+                               i->SubSystem[Index].ProcessId = SmpClientDirectory.Client [ClientIndex]->ServerProcessId;
+                               ++ Index;
+                       }
+               }
+       }
+
+       RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
+       return STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ * SmGetSubSystemInformation/1
+ */
+NTSTATUS FASTCALL
+SmGetSubSystemInformation (PSM_SUBSYSTEM_INFORMATION i)
+{
+       NTSTATUS  Status = STATUS_SUCCESS;
+       INT       ClientIndex = SM_INVALID_CLIENT_INDEX;
+       
+       DPRINT("SM: %s(%08lx) called\n", __FUNCTION__, i);
+
+       RtlEnterCriticalSection (& SmpClientDirectory.Lock);
+       ClientIndex = SmpLookupClient (i->SubSystemId);
+       if (SM_INVALID_CLIENT_INDEX == ClientIndex)
+       {
+               Status = STATUS_NOT_FOUND;
+       }
+       else
+       {
+               i->Flags     = SmpClientDirectory.Client [ClientIndex]->Flags;
+               i->ProcessId = SmpClientDirectory.Client [ClientIndex]->ServerProcessId;
+               RtlCopyMemory (i->NameSpaceRootNode,
+                               SmpClientDirectory.Client [ClientIndex]->SbApiPortName,
+                               (SM_QRYINFO_MAX_ROOT_NODE * sizeof(i->NameSpaceRootNode[0])));
+       }
+       RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
+       return Status;
+}
+
+/* EOF */