SM: simplify and fix client (subsystem servers) management.
[reactos.git] / reactos / subsys / smss / client.c
1 /* $Id$
2 *
3 * client.c - Session Manager client Management
4 *
5 * ReactOS Operating System
6 *
7 * --------------------------------------------------------------------
8 *
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.
13 *
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.
18 *
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,
22 * MA 02139, USA.
23 *
24 * --------------------------------------------------------------------
25 */
26 #include "smss.h"
27 #include <sm/helper.h>
28
29 #define NDEBUG
30 #include <debug.h>
31
32 /* Private ADT */
33
34 #define SM_MAX_CLIENT_COUNT 16
35 #define SM_INVALID_CLIENT_INDEX -1
36
37 struct _SM_CLIENT_DIRECTORY
38 {
39 RTL_CRITICAL_SECTION Lock;
40 ULONG Count;
41 PSM_CLIENT_DATA Client [SM_MAX_CLIENT_COUNT];
42 PSM_CLIENT_DATA CandidateClient;
43
44 } SmpClientDirectory;
45
46
47 /**********************************************************************
48 * SmInitializeClientManagement/0
49 */
50 NTSTATUS
51 SmInitializeClientManagement (VOID)
52 {
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;
59
60 }
61 /**********************************************************************
62 * SmpSetClientInitialized/1
63 */
64 VOID FASTCALL
65 SmpSetClientInitialized (PSM_CLIENT_DATA Client)
66 {
67 DPRINT("SM: %s(%08lx) called\n", __FUNCTION__, Client);
68 Client->Flags |= SM_CLIENT_FLAG_INITIALIZED;
69 }
70 /**********************************************************************
71 * SmpGetFirstFreeClientEntry/0 PRIVATE
72 *
73 * NOTE: call it holding SmpClientDirectory.Lock only
74 */
75 static INT STDCALL SmpGetFirstFreeClientEntry (VOID)
76 {
77 INT ClientIndex = 0;
78
79 DPRINT("SM: %s called\n", __FUNCTION__);
80
81 if (SmpClientDirectory.Count < SM_MAX_CLIENT_COUNT)
82 {
83 for (ClientIndex = 0;
84 (ClientIndex < SM_MAX_CLIENT_COUNT);
85 ClientIndex ++)
86 {
87 if (NULL == SmpClientDirectory.Client[ClientIndex])
88 {
89 DPRINT("SM: %s => %d\n", __FUNCTION__, ClientIndex);
90 return ClientIndex; // found
91 }
92 }
93 }
94 return SM_INVALID_CLIENT_INDEX; // full!
95 }
96 /**********************************************************************
97 * SmpLookupClient/1 PRIVATE
98 *
99 * DESCRIPTION
100 * Lookup the subsystem server descriptor (client data) given its
101 * base image ID.
102 *
103 * ARGUMENTS
104 * SubsystemId: IMAGE_SUBSYSTEM_xxx
105 *
106 * RETURN VALUES
107 * SM_INVALID_CLIENT_INDEX on error;
108 * otherwise an index in the range (0..SM_MAX_CLIENT_COUNT).
109 *
110 * WARNING
111 * SmpClientDirectory.Lock must be held by the caller.
112 */
113 static INT FASTCALL
114 SmpLookupClient (USHORT SubsystemId)
115 {
116 INT ClientIndex = 0;
117
118 DPRINT("SM: %s(%d) called\n", __FUNCTION__, SubsystemId);
119
120 if (0 != SmpClientDirectory.Count)
121 {
122 for (ClientIndex = 0; (ClientIndex < SM_MAX_CLIENT_COUNT); ClientIndex ++)
123 {
124 if (NULL != SmpClientDirectory.Client[ClientIndex])
125 {
126 if (SubsystemId == SmpClientDirectory.Client[ClientIndex]->SubsystemId)
127 {
128 return ClientIndex;
129 }
130 }
131 }
132 }
133 return SM_INVALID_CLIENT_INDEX;
134 }
135 /**********************************************************************
136 * SmpDestroyClientObject/2 PRIVATE
137 *
138 * WARNING
139 * SmpClientDirectory.Lock must be held by the caller.
140 */
141 static NTSTATUS STDCALL
142 SmpDestroyClientObject (PSM_CLIENT_DATA Client, NTSTATUS DestroyReason)
143 {
144 DPRINT("SM:%s(%08lx,%08lx) called\n", __FUNCTION__, 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;
150 }
151 /**********************************************************************
152 * SmBeginClientInitialization/1
153 *
154 * DESCRIPTION
155 * Check if the candidate client matches the begin session
156 * message from the subsystem process.
157 *
158 * ARGUMENTS
159 * Request: message received by \SmApiPort
160 * ClientData:
161 *
162 * RETURN VALUES
163 * NTSTATUS
164 */
165 NTSTATUS STDCALL
166 SmBeginClientInitialization (IN PSM_PORT_MESSAGE Request,
167 OUT PSM_CLIENT_DATA * ClientData)
168 {
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;
173
174
175 DPRINT("SM: %s(%08lx,%08lx) called\n", __FUNCTION__,
176 Request, ClientData);
177
178 RtlEnterCriticalSection (& SmpClientDirectory.Lock);
179 /*
180 * Is there a subsystem bootstrap in progress?
181 */
182 if (NULL != SmpClientDirectory.CandidateClient)
183 {
184 PROCESS_BASIC_INFORMATION pbi;
185
186 RtlZeroMemory (& pbi, sizeof pbi);
187 Status = NtQueryInformationProcess (Request->Header.ClientId.UniqueProcess,
188 ProcessBasicInformation,
189 & pbi,
190 sizeof pbi,
191 NULL);
192 if (NT_SUCCESS(Status))
193 {
194 SmpClientDirectory.CandidateClient->ServerProcessId =
195 (ULONG) pbi.UniqueProcessId;
196 }
197 }
198 else
199 {
200 DPRINT1("SM: %s: subsys booting with no descriptor!\n", __FUNCTION__);
201 Status = STATUS_NOT_FOUND;
202 RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
203 return Status;
204 }
205 /*
206 * Check if a client for the ID already exist.
207 */
208 if (SM_INVALID_CLIENT_INDEX != SmpLookupClient(ConnectData->SubSystemId))
209 {
210 DPRINT("SM: %s: attempt to register again subsystem %d.\n",
211 __FUNCTION__,
212 ConnectData->SubSystemId);
213 // TODO something else to do here?
214 RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
215 return STATUS_UNSUCCESSFUL;
216 }
217 /*
218 * Check if a free entry exists in SmpClientDirectory.Client[].
219 */
220 ClientIndex = SmpGetFirstFreeClientEntry();
221 if (SM_INVALID_CLIENT_INDEX == ClientIndex)
222 {
223 DPRINT("SM: %s: SM_INVALID_CLIENT_INDEX == ClientIndex ", __FUNCTION__);
224 SmpDestroyClientObject (SmpClientDirectory.CandidateClient, STATUS_NO_MEMORY);
225 SmpClientDirectory.CandidateClient = NULL;
226 return STATUS_NO_MEMORY;
227 }
228
229 /* OK! */
230 DPRINT("SM: %s: registering subsystem ID=%d \n",
231 __FUNCTION__, ConnectData->SubSystemId);
232
233 /*
234 * Initialize the client data
235 */
236 SmpClientDirectory.CandidateClient->SubsystemId = ConnectData->SubSystemId;
237 /* SM && DBG auto-initializes; other subsystems are required to call
238 * SM_API_COMPLETE_SESSION via SMDLL. */
239 if ((IMAGE_SUBSYSTEM_NATIVE == SmpClientDirectory.CandidateClient->SubsystemId) ||
240 ((USHORT)-1 == SmpClientDirectory.CandidateClient->SubsystemId))
241 {
242 SmpSetClientInitialized (SmpClientDirectory.CandidateClient);
243 }
244 if (SbApiPortNameSize > 0)
245 {
246 /* Only external servers have an SB port */
247 RtlCopyMemory (SmpClientDirectory.CandidateClient->SbApiPortName,
248 ConnectData->SbName,
249 SbApiPortNameSize);
250 }
251 /*
252 * Insert the new descriptor in the
253 * client directory.
254 */
255 SmpClientDirectory.Client [ClientIndex] = SmpClientDirectory.CandidateClient;
256 /*
257 * Increment the number of active subsystems.
258 */
259 ++ SmpClientDirectory.Count;
260 /*
261 * Notify to the caller the reference to the client data.
262 */
263 if (ClientData)
264 {
265 *ClientData = SmpClientDirectory.CandidateClient;
266 }
267 /*
268 * Free the slot for the candidate subsystem.
269 */
270 SmpClientDirectory.CandidateClient = NULL;
271
272 /* Done */
273 RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
274
275 return STATUS_SUCCESS;
276 }
277 /**********************************************************************
278 * SmCompleteClientInitialization/1
279 *
280 * DESCRIPTION
281 * Lookup the subsystem server descriptor given the process ID
282 * of the subsystem server process.
283 */
284 NTSTATUS STDCALL
285 SmCompleteClientInitialization (ULONG ProcessId)
286 {
287 NTSTATUS Status = STATUS_NOT_FOUND;
288 INT ClientIndex = SM_INVALID_CLIENT_INDEX;
289
290 DPRINT("SM: %s(%lu) called\n", __FUNCTION__, ProcessId);
291
292 RtlEnterCriticalSection (& SmpClientDirectory.Lock);
293 if (SmpClientDirectory.Count > 0)
294 {
295 for (ClientIndex = 0; ClientIndex < SM_MAX_CLIENT_COUNT; ClientIndex ++)
296 {
297 if ((NULL != SmpClientDirectory.Client [ClientIndex]) &&
298 (ProcessId == SmpClientDirectory.Client [ClientIndex]->ServerProcessId))
299 {
300 SmpSetClientInitialized (SmpClientDirectory.Client [ClientIndex]);
301 Status = STATUS_SUCCESS;
302 break;
303 }
304 }
305 }
306 RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
307 return Status;
308 }
309 /**********************************************************************
310 * SmpDestroyClientByClientIndex/1 PRIVATE
311 */
312 static NTSTATUS STDCALL
313 SmpDestroyClientByClientIndex (INT ClientIndex)
314 {
315 NTSTATUS Status = STATUS_SUCCESS;
316 PSM_CLIENT_DATA Client = NULL;
317
318 DPRINT("SM: %s(%d) called\n", __FUNCTION__, ClientIndex);
319
320 if (SM_INVALID_CLIENT_INDEX == ClientIndex)
321 {
322 DPRINT1("SM: %s: SM_INVALID_CLIENT_INDEX == ClientIndex!\n",
323 __FUNCTION__);
324 Status = STATUS_NOT_FOUND;
325 }
326 else
327 {
328 Client = SmpClientDirectory.Client [ClientIndex];
329 SmpClientDirectory.Client [ClientIndex] = NULL;
330 if (NULL != Client)
331 {
332 Status = SmpDestroyClientObject (Client, STATUS_SUCCESS);
333 } else {
334 DPRINT("SM:%s: NULL == Client[%d]!\n", __FUNCTION__,
335 ClientIndex);
336 Status = STATUS_UNSUCCESSFUL;
337 }
338 }
339 return Status;
340 }
341 /**********************************************************************
342 * SmpTimeoutCandidateClient/1
343 *
344 * DESCRIPTION
345 * Give the candidate client time to bootstrap and complete
346 * session initialization. If the client fails in any way,
347 * drop the pending client and kill the process.
348 *
349 * ARGUMENTS
350 * x: HANDLE for the candidate process.
351 *
352 * RETURN VALUE
353 * NONE.
354 */
355 static VOID STDCALL SmpTimeoutCandidateClient (PVOID x)
356 {
357 NTSTATUS Status = STATUS_SUCCESS;
358 HANDLE CandidateClientProcessHandle = (HANDLE) x;
359 LARGE_INTEGER TimeOut;
360
361 DPRINT("SM: %s(%lx) called\n", __FUNCTION__, x);
362
363 TimeOut.QuadPart = (LONGLONG) -300000000L; // 30s
364 Status = NtWaitForSingleObject (CandidateClientProcessHandle,
365 FALSE,
366 & TimeOut);
367 if (STATUS_TIMEOUT == Status)
368 {
369 RtlEnterCriticalSection (& SmpClientDirectory.Lock);
370 if (NULL != SmpClientDirectory.CandidateClient)
371 {
372 DPRINT("SM:%s: destroy candidate %08lx\n", __FUNCTION__,
373 SmpClientDirectory.CandidateClient);
374 Status = SmpDestroyClientObject (SmpClientDirectory.CandidateClient,
375 STATUS_TIMEOUT);
376 SmpClientDirectory.CandidateClient = NULL;
377 }
378 RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
379 }
380 NtTerminateThread (NtCurrentThread(), Status);
381 }
382 /**********************************************************************
383 * SmpCreateClient/1
384 *
385 * DESCRIPTION
386 * Create a "candidate" client. Client descriptor will enter the
387 * client directory only at the end of the registration
388 * procedure. Otherwise, we will kill the associated process.
389 *
390 * ARGUMENTS
391 * ProcessHandle: handle of the subsystem server process.
392 *
393 * RETURN VALUE
394 * NTSTATUS:
395 * STATUS_SUCCESS if all OK;
396 * STATUS_DEVICE_BUSY if another SS is still booting;
397 * STATUS_NO_MEMORY if client descriptor allocation failed;
398 *
399 *
400 */
401 NTSTATUS STDCALL
402 SmCreateClient (PRTL_USER_PROCESS_INFORMATION ProcessInfo, PWSTR ProgramName)
403 {
404 NTSTATUS Status = STATUS_SUCCESS;
405
406 DPRINT("SM: %s(%lx, %S) called\n", __FUNCTION__, ProcessInfo->ProcessHandle, ProgramName);
407
408 RtlEnterCriticalSection (& SmpClientDirectory.Lock);
409 /*
410 * Check if the candidate client slot is empty.
411 */
412 if (NULL == SmpClientDirectory.CandidateClient)
413 {
414 /*
415 * Check if there exist a free entry in the
416 * SmpClientDirectory.Client array.
417 */
418 if (SM_INVALID_CLIENT_INDEX == SmpGetFirstFreeClientEntry())
419 {
420 DPRINT("SM: %s(%lx): out of memory!\n",
421 __FUNCTION__, ProcessInfo->ProcessHandle);
422 Status = STATUS_NO_MEMORY;
423 }
424 /*
425 * Allocate the storage for client data
426 */
427 SmpClientDirectory.CandidateClient =
428 RtlAllocateHeap (SmpHeap,
429 HEAP_ZERO_MEMORY,
430 sizeof (SM_CLIENT_DATA));
431 if (NULL == SmpClientDirectory.CandidateClient)
432 {
433 DPRINT("SM: %s(%lx): out of memory!\n",
434 __FUNCTION__, ProcessInfo->ProcessHandle);
435 Status = STATUS_NO_MEMORY;
436 }
437 else
438 {
439 DPRINT("SM:%s(%08lx,%S): candidate is %08lx\n", __FUNCTION__,
440 ProcessInfo, ProgramName, SmpClientDirectory.CandidateClient);
441 /* Initialize the candidate client. */
442 RtlInitializeCriticalSection(& SmpClientDirectory.CandidateClient->Lock);
443 SmpClientDirectory.CandidateClient->ServerProcess =
444 (HANDLE) ProcessInfo->ProcessHandle;
445 SmpClientDirectory.CandidateClient->ServerProcessId =
446 (ULONG) ProcessInfo->ClientId.UniqueProcess;
447 /*
448 * Copy the program name
449 */
450 RtlCopyMemory (SmpClientDirectory.CandidateClient->ProgramName,
451 ProgramName,
452 SM_SB_NAME_MAX_LENGTH);
453 }
454 } else {
455 DPRINT1("SM: %s: CandidateClient %08lx pending!\n", __FUNCTION__,
456 SmpClientDirectory.CandidateClient);
457 Status = STATUS_DEVICE_BUSY;
458 }
459
460 RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
461
462 /* Create the timeout thread for external subsystems */
463 if (_wcsicmp (ProgramName, L"Session Manager") && _wcsicmp (ProgramName, L"Debug"))
464 {
465 Status = RtlCreateUserThread (NtCurrentProcess(),
466 NULL,
467 FALSE,
468 0,
469 0,
470 0,
471 (PTHREAD_START_ROUTINE) SmpTimeoutCandidateClient,
472 SmpClientDirectory.CandidateClient->ServerProcess,
473 NULL,
474 NULL);
475 if (!NT_SUCCESS(Status))
476 {
477 DPRINT1("SM:%s: RtlCreateUserThread() failed (Status=%08lx)\n",
478 __FUNCTION__, Status);
479 }
480 }
481
482 return Status;
483 }
484 /**********************************************************************
485 * SmpDestroyClient/1
486 *
487 * 1. close any handle
488 * 2. kill client process
489 * 3. release resources
490 */
491 NTSTATUS STDCALL
492 SmDestroyClient (ULONG SubsystemId)
493 {
494 NTSTATUS Status = STATUS_SUCCESS;
495 INT ClientIndex = SM_INVALID_CLIENT_INDEX;
496
497 DPRINT("SM: %s(%lu) called\n", __FUNCTION__, SubsystemId);
498
499 RtlEnterCriticalSection (& SmpClientDirectory.Lock);
500 ClientIndex = SmpLookupClient (SubsystemId);
501 if (SM_INVALID_CLIENT_INDEX == ClientIndex)
502 {
503 DPRINT1("SM: %s: del req for non existent subsystem (id=%d)\n",
504 __FUNCTION__, SubsystemId);
505 return STATUS_NOT_FOUND;
506 }
507 Status = SmpDestroyClientByClientIndex (ClientIndex);
508 RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
509 return Status;
510 }
511
512 /* === Utilities for SmQryInfo === */
513
514 /**********************************************************************
515 * SmGetClientBasicInformation/1
516 */
517 NTSTATUS FASTCALL
518 SmGetClientBasicInformation (PSM_BASIC_INFORMATION i)
519 {
520 INT ClientIndex = 0;
521 INT Index = 0;
522
523 DPRINT("SM: %s(%08lx) called\n", __FUNCTION__, i);
524
525 RtlEnterCriticalSection (& SmpClientDirectory.Lock);
526
527 i->SubSystemCount = SmpClientDirectory.Count;
528 i->Unused = 0;
529
530 if (SmpClientDirectory.Count > 0)
531 {
532 for (ClientIndex = 0; (ClientIndex < SM_MAX_CLIENT_COUNT); ClientIndex ++)
533 {
534 if ((NULL != SmpClientDirectory.Client [ClientIndex]) &&
535 (Index < SM_QRYINFO_MAX_SS_COUNT))
536 {
537 i->SubSystem[Index].Id = SmpClientDirectory.Client [ClientIndex]->SubsystemId;
538 i->SubSystem[Index].Flags = SmpClientDirectory.Client [ClientIndex]->Flags;
539 i->SubSystem[Index].ProcessId = SmpClientDirectory.Client [ClientIndex]->ServerProcessId;
540 ++ Index;
541 }
542 }
543 }
544
545 RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
546 return STATUS_SUCCESS;
547 }
548
549 /**********************************************************************
550 * SmGetSubSystemInformation/1
551 */
552 NTSTATUS FASTCALL
553 SmGetSubSystemInformation (PSM_SUBSYSTEM_INFORMATION i)
554 {
555 NTSTATUS Status = STATUS_SUCCESS;
556 INT ClientIndex = SM_INVALID_CLIENT_INDEX;
557
558 DPRINT("SM: %s(%08lx) called\n", __FUNCTION__, i);
559
560 RtlEnterCriticalSection (& SmpClientDirectory.Lock);
561 ClientIndex = SmpLookupClient (i->SubSystemId);
562 if (SM_INVALID_CLIENT_INDEX == ClientIndex)
563 {
564 Status = STATUS_NOT_FOUND;
565 }
566 else
567 {
568 i->Flags = SmpClientDirectory.Client [ClientIndex]->Flags;
569 i->ProcessId = SmpClientDirectory.Client [ClientIndex]->ServerProcessId;
570 RtlCopyMemory (i->NameSpaceRootNode,
571 SmpClientDirectory.Client [ClientIndex]->SbApiPortName,
572 (SM_QRYINFO_MAX_ROOT_NODE * sizeof(i->NameSpaceRootNode[0])));
573 }
574 RtlLeaveCriticalSection (& SmpClientDirectory.Lock);
575 return Status;
576 }
577
578 /* EOF */