3 * client.c - Session Manager client Management
5 * ReactOS Operating System
7 * --------------------------------------------------------------------
9 * This software is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
14 * This software is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this software; see the file COPYING.LIB. If not, write
21 * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
24 * --------------------------------------------------------------------
27 #include <sm/helper.h>
34 #define SM_MAX_CLIENT_COUNT 16
35 #define SM_INVALID_CLIENT_INDEX -1
37 struct _SM_CLIENT_DIRECTORY
39 RTL_CRITICAL_SECTION Lock
;
41 PSM_CLIENT_DATA Client
[SM_MAX_CLIENT_COUNT
];
42 PSM_CLIENT_DATA CandidateClient
;
47 /**********************************************************************
48 * SmInitializeClientManagement/0
51 SmInitializeClientManagement (VOID
)
53 DPRINT("SM: %s called\n", __FUNCTION__
);
54 RtlInitializeCriticalSection(& SmpClientDirectory
.Lock
);
55 SmpClientDirectory
.Count
= 0;
56 RtlZeroMemory (SmpClientDirectory
.Client
, sizeof SmpClientDirectory
.Client
);
57 SmpClientDirectory
.CandidateClient
= NULL
;
58 return STATUS_SUCCESS
;
61 /**********************************************************************
62 * SmpSetClientInitialized/1
65 SmpSetClientInitialized (PSM_CLIENT_DATA Client
)
67 DPRINT("SM: %s(%p) called\n", __FUNCTION__
, Client
);
68 Client
->Flags
|= SM_CLIENT_FLAG_INITIALIZED
;
70 /**********************************************************************
71 * SmpGetFirstFreeClientEntry/0 PRIVATE
73 * NOTE: call it holding SmpClientDirectory.Lock only
75 static INT STDCALL
SmpGetFirstFreeClientEntry (VOID
)
79 DPRINT("SM: %s called\n", __FUNCTION__
);
81 if (SmpClientDirectory
.Count
< SM_MAX_CLIENT_COUNT
)
84 (ClientIndex
< SM_MAX_CLIENT_COUNT
);
87 if (NULL
== SmpClientDirectory
.Client
[ClientIndex
])
89 DPRINT("SM: %s => %d\n", __FUNCTION__
, ClientIndex
);
90 return ClientIndex
; // found
94 return SM_INVALID_CLIENT_INDEX
; // full!
96 /**********************************************************************
97 * SmpLookupClient/1 PRIVATE
100 * Lookup the subsystem server descriptor (client data) given its
104 * SubsystemId: IMAGE_SUBSYSTEM_xxx
107 * SM_INVALID_CLIENT_INDEX on error;
108 * otherwise an index in the range (0..SM_MAX_CLIENT_COUNT).
111 * SmpClientDirectory.Lock must be held by the caller.
114 SmpLookupClient (USHORT SubsystemId
)
118 DPRINT("SM: %s(%d) called\n", __FUNCTION__
, SubsystemId
);
120 if (0 != SmpClientDirectory
.Count
)
122 for (ClientIndex
= 0; (ClientIndex
< SM_MAX_CLIENT_COUNT
); ClientIndex
++)
124 if (NULL
!= SmpClientDirectory
.Client
[ClientIndex
])
126 if (SubsystemId
== SmpClientDirectory
.Client
[ClientIndex
]->SubsystemId
)
133 return SM_INVALID_CLIENT_INDEX
;
135 /**********************************************************************
136 * SmpDestroyClientObject/2 PRIVATE
139 * SmpClientDirectory.Lock must be held by the caller.
141 static NTSTATUS STDCALL
142 SmpDestroyClientObject (PSM_CLIENT_DATA Client
, NTSTATUS DestroyReason
)
144 DPRINT("SM:%s(%p,%08lx) called\n", __FUNCTION__
, Client
, DestroyReason
);
145 /* TODO: send shutdown to the SB port */
146 NtTerminateProcess (Client
->ServerProcess
, DestroyReason
);
147 RtlFreeHeap (SmpHeap
, 0, Client
);
148 -- SmpClientDirectory
.Count
;
149 return STATUS_SUCCESS
;
151 /**********************************************************************
152 * SmBeginClientInitialization/1
155 * Check if the candidate client matches the begin session
156 * message from the subsystem process.
159 * Request: message received by \SmApiPort
166 SmBeginClientInitialization (IN PSM_PORT_MESSAGE Request
,
167 OUT PSM_CLIENT_DATA
* ClientData
)
169 NTSTATUS Status
= STATUS_SUCCESS
;
170 PSM_CONNECT_DATA ConnectData
= SmpGetConnectData (Request
);
171 ULONG SbApiPortNameSize
= SM_CONNECT_DATA_SIZE(*Request
);
172 INT ClientIndex
= SM_INVALID_CLIENT_INDEX
;
176 DPRINT("SM: %s(%p,%p) called\n", __FUNCTION__
,
177 Request
, ClientData
);
179 RtlEnterCriticalSection (& SmpClientDirectory
.Lock
);
181 * Is there a subsystem bootstrap in progress?
183 if (NULL
!= SmpClientDirectory
.CandidateClient
)
185 PROCESS_BASIC_INFORMATION pbi
;
186 OBJECT_ATTRIBUTES ObjectAttributes
;
188 RtlZeroMemory (& pbi
, sizeof pbi
);
189 InitializeObjectAttributes(&ObjectAttributes
, NULL
, 0, NULL
, NULL
);
190 Status
= NtOpenProcess(&Process
,
193 &Request
->Header
.ClientId
);
194 ASSERT(NT_SUCCESS(Status
));
195 Status
= NtQueryInformationProcess (Process
,
196 ProcessBasicInformation
,
200 ASSERT(NT_SUCCESS(Status
));
202 SmpClientDirectory
.CandidateClient
->ServerProcessId
=
203 (ULONG
) pbi
.UniqueProcessId
;
208 DPRINT1("SM: %s: subsys booting with no descriptor!\n", __FUNCTION__
);
209 Status
= STATUS_NOT_FOUND
;
210 RtlLeaveCriticalSection (& SmpClientDirectory
.Lock
);
214 * Check if a client for the ID already exist.
216 if (SM_INVALID_CLIENT_INDEX
!= SmpLookupClient(ConnectData
->SubSystemId
))
218 DPRINT("SM: %s: attempt to register again subsystem %d.\n",
220 ConnectData
->SubSystemId
);
221 // TODO something else to do here?
222 RtlLeaveCriticalSection (& SmpClientDirectory
.Lock
);
223 return STATUS_UNSUCCESSFUL
;
226 * Check if a free entry exists in SmpClientDirectory.Client[].
228 ClientIndex
= SmpGetFirstFreeClientEntry();
229 if (SM_INVALID_CLIENT_INDEX
== ClientIndex
)
231 DPRINT("SM: %s: SM_INVALID_CLIENT_INDEX == ClientIndex ", __FUNCTION__
);
232 SmpDestroyClientObject (SmpClientDirectory
.CandidateClient
, STATUS_NO_MEMORY
);
233 SmpClientDirectory
.CandidateClient
= NULL
;
234 return STATUS_NO_MEMORY
;
238 DPRINT("SM: %s: registering subsystem ID=%d \n",
239 __FUNCTION__
, ConnectData
->SubSystemId
);
242 * Initialize the client data
244 SmpClientDirectory
.CandidateClient
->SubsystemId
= ConnectData
->SubSystemId
;
245 /* SM && DBG auto-initializes; other subsystems are required to call
246 * SM_API_COMPLETE_SESSION via SMDLL. */
247 if ((IMAGE_SUBSYSTEM_NATIVE
== SmpClientDirectory
.CandidateClient
->SubsystemId
) ||
248 ((USHORT
)-1 == SmpClientDirectory
.CandidateClient
->SubsystemId
))
250 SmpSetClientInitialized (SmpClientDirectory
.CandidateClient
);
252 if (SbApiPortNameSize
> 0)
254 /* Only external servers have an SB port */
255 RtlCopyMemory (SmpClientDirectory
.CandidateClient
->SbApiPortName
,
260 * Insert the new descriptor in the
263 SmpClientDirectory
.Client
[ClientIndex
] = SmpClientDirectory
.CandidateClient
;
265 * Increment the number of active subsystems.
267 ++ SmpClientDirectory
.Count
;
269 * Notify to the caller the reference to the client data.
273 *ClientData
= SmpClientDirectory
.CandidateClient
;
276 * Free the slot for the candidate subsystem.
278 SmpClientDirectory
.CandidateClient
= NULL
;
281 RtlLeaveCriticalSection (& SmpClientDirectory
.Lock
);
283 return STATUS_SUCCESS
;
285 /**********************************************************************
286 * SmCompleteClientInitialization/1
289 * Lookup the subsystem server descriptor given the process ID
290 * of the subsystem server process.
293 SmCompleteClientInitialization (ULONG ProcessId
)
295 NTSTATUS Status
= STATUS_NOT_FOUND
;
296 INT ClientIndex
= SM_INVALID_CLIENT_INDEX
;
298 DPRINT("SM: %s(%lu) called\n", __FUNCTION__
, ProcessId
);
300 RtlEnterCriticalSection (& SmpClientDirectory
.Lock
);
301 if (SmpClientDirectory
.Count
> 0)
303 for (ClientIndex
= 0; ClientIndex
< SM_MAX_CLIENT_COUNT
; ClientIndex
++)
305 if ((NULL
!= SmpClientDirectory
.Client
[ClientIndex
]) &&
306 (ProcessId
== SmpClientDirectory
.Client
[ClientIndex
]->ServerProcessId
))
308 SmpSetClientInitialized (SmpClientDirectory
.Client
[ClientIndex
]);
309 Status
= STATUS_SUCCESS
;
314 RtlLeaveCriticalSection (& SmpClientDirectory
.Lock
);
317 /**********************************************************************
318 * SmpDestroyClientByClientIndex/1 PRIVATE
320 static NTSTATUS STDCALL
321 SmpDestroyClientByClientIndex (INT ClientIndex
)
323 NTSTATUS Status
= STATUS_SUCCESS
;
324 PSM_CLIENT_DATA Client
= NULL
;
326 DPRINT("SM: %s(%d) called\n", __FUNCTION__
, ClientIndex
);
328 if (SM_INVALID_CLIENT_INDEX
== ClientIndex
)
330 DPRINT1("SM: %s: SM_INVALID_CLIENT_INDEX == ClientIndex!\n",
332 Status
= STATUS_NOT_FOUND
;
336 Client
= SmpClientDirectory
.Client
[ClientIndex
];
337 SmpClientDirectory
.Client
[ClientIndex
] = NULL
;
340 Status
= SmpDestroyClientObject (Client
, STATUS_SUCCESS
);
342 DPRINT("SM:%s: NULL == Client[%d]!\n", __FUNCTION__
,
344 Status
= STATUS_UNSUCCESSFUL
;
349 /**********************************************************************
350 * SmpTimeoutCandidateClient/1
353 * Give the candidate client time to bootstrap and complete
354 * session initialization. If the client fails in any way,
355 * drop the pending client and kill the process.
358 * x: HANDLE for the candidate process.
363 static VOID STDCALL
SmpTimeoutCandidateClient (PVOID x
)
365 NTSTATUS Status
= STATUS_SUCCESS
;
366 HANDLE CandidateClientProcessHandle
= (HANDLE
) x
;
367 LARGE_INTEGER TimeOut
;
369 DPRINT("SM: %s(%p) called\n", __FUNCTION__
, x
);
371 TimeOut
.QuadPart
= (LONGLONG
) -300000000L; // 30s
372 Status
= NtWaitForSingleObject (CandidateClientProcessHandle
,
375 if (STATUS_TIMEOUT
== Status
)
377 RtlEnterCriticalSection (& SmpClientDirectory
.Lock
);
378 if (NULL
!= SmpClientDirectory
.CandidateClient
)
380 DPRINT("SM:%s: destroy candidate %p\n", __FUNCTION__
,
381 SmpClientDirectory
.CandidateClient
);
382 Status
= SmpDestroyClientObject (SmpClientDirectory
.CandidateClient
,
384 SmpClientDirectory
.CandidateClient
= NULL
;
386 RtlLeaveCriticalSection (& SmpClientDirectory
.Lock
);
388 NtTerminateThread (NtCurrentThread(), Status
);
390 /**********************************************************************
394 * Create a "candidate" client. Client descriptor will enter the
395 * client directory only at the end of the registration
396 * procedure. Otherwise, we will kill the associated process.
399 * ProcessHandle: handle of the subsystem server process.
403 * STATUS_SUCCESS if all OK;
404 * STATUS_DEVICE_BUSY if another SS is still booting;
405 * STATUS_NO_MEMORY if client descriptor allocation failed;
410 SmCreateClient (PRTL_USER_PROCESS_INFORMATION ProcessInfo
, PWSTR ProgramName
)
412 NTSTATUS Status
= STATUS_SUCCESS
;
414 DPRINT("SM: %s(%p, %S) called\n", __FUNCTION__
, ProcessInfo
->ProcessHandle
, ProgramName
);
416 RtlEnterCriticalSection (& SmpClientDirectory
.Lock
);
418 * Check if the candidate client slot is empty.
420 if (NULL
== SmpClientDirectory
.CandidateClient
)
423 * Check if there exist a free entry in the
424 * SmpClientDirectory.Client array.
426 if (SM_INVALID_CLIENT_INDEX
== SmpGetFirstFreeClientEntry())
428 DPRINT("SM: %s(%p): out of memory!\n",
429 __FUNCTION__
, ProcessInfo
->ProcessHandle
);
430 Status
= STATUS_NO_MEMORY
;
433 * Allocate the storage for client data
435 SmpClientDirectory
.CandidateClient
=
436 RtlAllocateHeap (SmpHeap
,
438 sizeof (SM_CLIENT_DATA
));
439 if (NULL
== SmpClientDirectory
.CandidateClient
)
441 DPRINT("SM: %s(%p): out of memory!\n",
442 __FUNCTION__
, ProcessInfo
->ProcessHandle
);
443 Status
= STATUS_NO_MEMORY
;
447 DPRINT("SM:%s(%p,%S): candidate is %p\n", __FUNCTION__
,
448 ProcessInfo
, ProgramName
, SmpClientDirectory
.CandidateClient
);
449 /* Initialize the candidate client. */
450 RtlInitializeCriticalSection(& SmpClientDirectory
.CandidateClient
->Lock
);
451 SmpClientDirectory
.CandidateClient
->ServerProcess
=
452 (HANDLE
) ProcessInfo
->ProcessHandle
;
453 SmpClientDirectory
.CandidateClient
->ServerProcessId
=
454 (ULONG
) ProcessInfo
->ClientId
.UniqueProcess
;
456 * Copy the program name
458 RtlCopyMemory (SmpClientDirectory
.CandidateClient
->ProgramName
,
460 SM_SB_NAME_MAX_LENGTH
);
463 DPRINT1("SM: %s: CandidateClient %p pending!\n", __FUNCTION__
,
464 SmpClientDirectory
.CandidateClient
);
465 Status
= STATUS_DEVICE_BUSY
;
468 RtlLeaveCriticalSection (& SmpClientDirectory
.Lock
);
470 /* Create the timeout thread for external subsystems */
471 if (_wcsicmp (ProgramName
, L
"Session Manager") && _wcsicmp (ProgramName
, L
"Debug"))
473 Status
= RtlCreateUserThread (NtCurrentProcess(),
479 (PTHREAD_START_ROUTINE
) SmpTimeoutCandidateClient
,
480 SmpClientDirectory
.CandidateClient
->ServerProcess
,
483 if (!NT_SUCCESS(Status
))
485 DPRINT1("SM:%s: RtlCreateUserThread() failed (Status=%08lx)\n",
486 __FUNCTION__
, Status
);
492 /**********************************************************************
495 * 1. close any handle
496 * 2. kill client process
497 * 3. release resources
500 SmDestroyClient (ULONG SubsystemId
)
502 NTSTATUS Status
= STATUS_SUCCESS
;
503 INT ClientIndex
= SM_INVALID_CLIENT_INDEX
;
505 DPRINT("SM: %s(%lu) called\n", __FUNCTION__
, SubsystemId
);
507 RtlEnterCriticalSection (& SmpClientDirectory
.Lock
);
508 ClientIndex
= SmpLookupClient (SubsystemId
);
509 if (SM_INVALID_CLIENT_INDEX
== ClientIndex
)
511 DPRINT1("SM: %s: del req for non existent subsystem (id=%d)\n",
512 __FUNCTION__
, SubsystemId
);
513 return STATUS_NOT_FOUND
;
515 Status
= SmpDestroyClientByClientIndex (ClientIndex
);
516 RtlLeaveCriticalSection (& SmpClientDirectory
.Lock
);
520 /* === Utilities for SmQryInfo === */
522 /**********************************************************************
523 * SmGetClientBasicInformation/1
526 SmGetClientBasicInformation (PSM_BASIC_INFORMATION i
)
531 DPRINT("SM: %s(%p) called\n", __FUNCTION__
, i
);
533 RtlEnterCriticalSection (& SmpClientDirectory
.Lock
);
535 i
->SubSystemCount
= SmpClientDirectory
.Count
;
538 if (SmpClientDirectory
.Count
> 0)
540 for (ClientIndex
= 0; (ClientIndex
< SM_MAX_CLIENT_COUNT
); ClientIndex
++)
542 if ((NULL
!= SmpClientDirectory
.Client
[ClientIndex
]) &&
543 (Index
< SM_QRYINFO_MAX_SS_COUNT
))
545 i
->SubSystem
[Index
].Id
= SmpClientDirectory
.Client
[ClientIndex
]->SubsystemId
;
546 i
->SubSystem
[Index
].Flags
= SmpClientDirectory
.Client
[ClientIndex
]->Flags
;
547 i
->SubSystem
[Index
].ProcessId
= SmpClientDirectory
.Client
[ClientIndex
]->ServerProcessId
;
553 RtlLeaveCriticalSection (& SmpClientDirectory
.Lock
);
554 return STATUS_SUCCESS
;
557 /**********************************************************************
558 * SmGetSubSystemInformation/1
561 SmGetSubSystemInformation (PSM_SUBSYSTEM_INFORMATION i
)
563 NTSTATUS Status
= STATUS_SUCCESS
;
564 INT ClientIndex
= SM_INVALID_CLIENT_INDEX
;
566 DPRINT("SM: %s(%p) called\n", __FUNCTION__
, i
);
568 RtlEnterCriticalSection (& SmpClientDirectory
.Lock
);
569 ClientIndex
= SmpLookupClient (i
->SubSystemId
);
570 if (SM_INVALID_CLIENT_INDEX
== ClientIndex
)
572 Status
= STATUS_NOT_FOUND
;
576 i
->Flags
= SmpClientDirectory
.Client
[ClientIndex
]->Flags
;
577 i
->ProcessId
= SmpClientDirectory
.Client
[ClientIndex
]->ServerProcessId
;
578 RtlCopyMemory (i
->NameSpaceRootNode
,
579 SmpClientDirectory
.Client
[ClientIndex
]->SbApiPortName
,
580 (SM_QRYINFO_MAX_ROOT_NODE
* sizeof(i
->NameSpaceRootNode
[0])));
582 RtlLeaveCriticalSection (& SmpClientDirectory
.Lock
);