SM: simplify and fix client (subsystem servers) management.
[reactos.git] / reactos / subsys / smss / smapi.c
1 /* $Id$
2 *
3 * smapi.c - \SmApiPort LPC port message management
4 *
5 * Reactos Session Manager
6 *
7 */
8 #include "smss.h"
9
10 #define NDEBUG
11 #include <debug.h>
12
13 /* GLOBAL VARIABLES *********************************************************/
14
15 static HANDLE SmApiPort = INVALID_HANDLE_VALUE;
16
17 /* SM API *******************************************************************/
18
19 SMAPI(SmInvalid)
20 {
21 DPRINT("SM: %s called\n",__FUNCTION__);
22 Request->SmHeader.Status = STATUS_NOT_IMPLEMENTED;
23 return STATUS_SUCCESS;
24 }
25
26 /* SM API Table */
27 typedef NTSTATUS (FASTCALL * SM_PORT_API)(PSM_PORT_MESSAGE);
28
29 SM_PORT_API SmApi [] =
30 {
31 SmInvalid, /* unused */
32 SmCompSes, /* smapicomp.c */
33 SmInvalid, /* obsolete */
34 SmInvalid, /* unknown */
35 SmExecPgm, /* smapiexec.c */
36 SmQryInfo /* smapyqry.c */
37 };
38
39 /* TODO: optimize this address computation (it should be done
40 * with a macro) */
41 PSM_CONNECT_DATA FASTCALL SmpGetConnectData (PSM_PORT_MESSAGE Request)
42 {
43 PPORT_MESSAGE PortMessage = (PPORT_MESSAGE) Request;
44 return (PSM_CONNECT_DATA)(PortMessage + 1);
45 }
46
47 #if !defined(__USE_NT_LPC__)
48 NTSTATUS STDCALL
49 SmpHandleConnectionRequest (PSM_PORT_MESSAGE Request);
50 #endif
51
52 /**********************************************************************
53 * SmpCallbackServer/2
54 *
55 * DESCRIPTION
56 * The SM calls back a previously connected subsystem process to
57 * authorize it to bootstrap (initialize). The SM connects to a
58 * named LPC port which name was sent in the connection data by
59 * the candidate subsystem server process.
60 */
61 static NTSTATUS
62 SmpCallbackServer (PSM_PORT_MESSAGE Request,
63 PSM_CLIENT_DATA ClientData)
64 {
65 NTSTATUS Status = STATUS_SUCCESS;
66 PSM_CONNECT_DATA ConnectData = SmpGetConnectData (Request);
67 UNICODE_STRING CallbackPortName;
68 ULONG CallbackPortNameLength = SM_SB_NAME_MAX_LENGTH; /* TODO: compute length */
69 SB_CONNECT_DATA SbConnectData;
70 ULONG SbConnectDataLength = sizeof SbConnectData;
71
72 DPRINT("SM: %s called\n", __FUNCTION__);
73
74 if ( ((USHORT)-1 == ConnectData->SubSystemId) ||
75 (IMAGE_SUBSYSTEM_NATIVE == ConnectData->SubSystemId))
76 {
77 DPRINT("SM: %s: we do not need calling back SM!\n",
78 __FUNCTION__);
79 return STATUS_SUCCESS;
80 }
81 RtlCopyMemory (ClientData->SbApiPortName,
82 ConnectData->SbName,
83 CallbackPortNameLength);
84 RtlInitUnicodeString (& CallbackPortName,
85 ClientData->SbApiPortName);
86
87 SbConnectData.SmApiMax = (sizeof SmApi / sizeof SmApi[0]);
88 Status = NtConnectPort (& ClientData->SbApiPort,
89 & CallbackPortName,
90 NULL,
91 NULL,
92 NULL,
93 NULL,
94 & SbConnectData,
95 & SbConnectDataLength);
96 return Status;
97 }
98
99 /**********************************************************************
100 * NAME
101 * SmpApiConnectedThread/1
102 *
103 * DESCRIPTION
104 * Entry point for the listener thread of LPC port "\SmApiPort".
105 */
106 VOID STDCALL
107 SmpApiConnectedThread(PVOID pConnectedPort)
108 {
109 NTSTATUS Status = STATUS_SUCCESS;
110 PVOID Unknown = NULL;
111 PPORT_MESSAGE Reply = NULL;
112 SM_PORT_MESSAGE Request;
113 HANDLE ConnectedPort = * (PHANDLE) pConnectedPort;
114
115 DPRINT("SM: %s called\n", __FUNCTION__);
116 RtlZeroMemory(&Request, sizeof(SM_PORT_MESSAGE));
117
118 while (TRUE)
119 {
120 DPRINT("SM: %s: waiting for message\n",__FUNCTION__);
121
122 Status = NtReplyWaitReceivePort(ConnectedPort,
123 (PULONG) & Unknown,
124 Reply,
125 (PPORT_MESSAGE) & Request);
126 if (NT_SUCCESS(Status))
127 {
128 DPRINT("SM: %s: message received (type=%d)\n",
129 __FUNCTION__,
130 Request.Header.u2.s2.Type);
131
132 switch (Request.Header.u2.s2.Type)
133 {
134 case LPC_CONNECTION_REQUEST:
135 SmpHandleConnectionRequest (&Request);
136 Reply = NULL;
137 break;
138 case LPC_DEBUG_EVENT:
139 // DbgSsHandleKmApiMsg (&Request, 0);
140 Reply = NULL;
141 break;
142 case LPC_PORT_CLOSED:
143 Reply = NULL;
144 break;
145 default:
146 if ((Request.SmHeader.ApiIndex) &&
147 (Request.SmHeader.ApiIndex < (sizeof SmApi / sizeof SmApi[0])))
148 {
149 Status = SmApi[Request.SmHeader.ApiIndex](&Request);
150 Reply = (PPORT_MESSAGE) & Request;
151 } else {
152 Request.SmHeader.Status = STATUS_NOT_IMPLEMENTED;
153 Reply = (PPORT_MESSAGE) & Request;
154 }
155 }
156 } else {
157 /* LPC failed */
158 break;
159 }
160 }
161 NtClose (ConnectedPort);
162 NtTerminateThread (NtCurrentThread(), Status);
163 }
164
165 /**********************************************************************
166 * NAME
167 * SmpHandleConnectionRequest/1
168 *
169 * ARGUMENTS
170 * Request: LPC connection request message
171 *
172 * REMARKS
173 * Quoted in http://support.microsoft.com/kb/258060/EN-US/
174 */
175 NTSTATUS STDCALL
176 SmpHandleConnectionRequest (PSM_PORT_MESSAGE Request)
177 {
178 PSM_CONNECT_DATA ConnectData = SmpGetConnectData (Request);
179 NTSTATUS Status = STATUS_SUCCESS;
180 BOOL Accept = FALSE;
181 PSM_CLIENT_DATA ClientData = NULL;
182 HANDLE hClientDataApiPort = (HANDLE) 0;
183 PHANDLE ClientDataApiPort = & hClientDataApiPort;
184 HANDLE hClientDataApiPortThread = (HANDLE) 0;
185 PHANDLE ClientDataApiPortThread = & hClientDataApiPortThread;
186 PVOID Context = NULL;
187
188 DPRINT("SM: %s called:\n SubSystemID=%d\n SbName=\"%S\"\n",
189 __FUNCTION__, ConnectData->SubSystemId, ConnectData->SbName);
190
191 if(sizeof (SM_CONNECT_DATA) == Request->Header.u1.s1.DataLength)
192 {
193 if(IMAGE_SUBSYSTEM_UNKNOWN == ConnectData->SubSystemId)
194 {
195 /*
196 * This is not a call to register an image set,
197 * but a simple connection request from a process
198 * that will use the SM API.
199 */
200 DPRINT("SM: %s: simple request\n", __FUNCTION__);
201 ClientDataApiPort = & hClientDataApiPort;
202 ClientDataApiPortThread = & hClientDataApiPortThread;
203 Accept = TRUE;
204 } else {
205 DPRINT("SM: %s: request to register an image set\n", __FUNCTION__);
206 /*
207 * Reject GUIs classes: only odd subsystem IDs are
208 * allowed to register here (tty mode images).
209 */
210 if(1 == (ConnectData->SubSystemId % 2))
211 {
212 DPRINT("SM: %s: id = %d\n", __FUNCTION__, ConnectData->SubSystemId);
213 /*
214 * SmBeginClientInitialization/2 will succeed only if there
215 * is a candidate client ready.
216 */
217 Status = SmBeginClientInitialization (Request, & ClientData);
218 if(STATUS_SUCCESS == Status)
219 {
220 DPRINT("SM: %s: ClientData = 0x%08lx\n",
221 __FUNCTION__, ClientData);
222 /*
223 * OK: the client is an environment subsystem
224 * willing to manage a free image type.
225 */
226 ClientDataApiPort = & ClientData->ApiPort;
227 ClientDataApiPortThread = & ClientData->ApiPortThread;
228 /*
229 * Call back the candidate environment subsystem
230 * server (use the port name sent in in the
231 * connection request message).
232 */
233 Status = SmpCallbackServer (Request, ClientData);
234 if(NT_SUCCESS(Status))
235 {
236 DPRINT("SM: %s: SmpCallbackServer OK\n",
237 __FUNCTION__);
238 Accept = TRUE;
239 } else {
240 DPRINT("SM: %s: SmpCallbackServer failed (Status=%08lx)\n",
241 __FUNCTION__, Status);
242 Status = SmDestroyClient (ConnectData->SubSystemId);
243 }
244 }
245 }
246 }
247 }
248 DPRINT("SM: %s: before NtAcceptConnectPort\n", __FUNCTION__);
249 #if defined(__USE_NT_LPC__)
250 Status = NtAcceptConnectPort (ClientDataApiPort,
251 Context,
252 (PPORT_MESSAGE) Request,
253 Accept,
254 NULL,
255 NULL);
256 #else /* ReactOS LPC */
257 Status = NtAcceptConnectPort (ClientDataApiPort,
258 SmApiPort, // ROS LPC requires the listen port here
259 Context,
260 Accept,
261 NULL,
262 NULL);
263 #endif
264 if(Accept)
265 {
266 if(!NT_SUCCESS(Status))
267 {
268 DPRINT1("SM: %s: NtAcceptConnectPort() failed (Status=0x%08lx)\n",
269 __FUNCTION__, Status);
270 return Status;
271 } else {
272 DPRINT("SM: %s: completing conn req\n", __FUNCTION__);
273 Status = NtCompleteConnectPort (*ClientDataApiPort);
274 if (!NT_SUCCESS(Status))
275 {
276 DPRINT1("SM: %s: NtCompleteConnectPort() failed (Status=0x%08lx)\n",
277 __FUNCTION__, Status);
278 return Status;
279 }
280 #if !defined(__USE_NT_LPC__) /* ReactOS LPC */
281 DPRINT("SM: %s: server side comm port thread (ROS LPC)\n", __FUNCTION__);
282 Status = RtlCreateUserThread(NtCurrentProcess(),
283 NULL,
284 FALSE,
285 0,
286 0,
287 0,
288 (PTHREAD_START_ROUTINE) SmpApiConnectedThread,
289 ClientDataApiPort,
290 ClientDataApiPortThread,
291 NULL);
292 if (!NT_SUCCESS(Status))
293 {
294 DPRINT1("SM: %s: Unable to create server thread (Status=0x%08lx)\n",
295 __FUNCTION__, Status);
296 return Status;
297 }
298 #endif
299 }
300 Status = STATUS_SUCCESS;
301 }
302 DPRINT("SM: %s done\n", __FUNCTION__);
303 return Status;
304 }
305
306 /**********************************************************************
307 * NAME
308 * SmpApiThread/1
309 *
310 * DECRIPTION
311 * Due to differences in LPC implementation between NT and ROS,
312 * we need a thread to listen to for connection request that
313 * creates a new thread for each connected port. This is not
314 * necessary in NT LPC, because server side connected ports are
315 * never used to receive requests.
316 */
317 VOID STDCALL
318 SmpApiThread (HANDLE ListeningPort)
319 {
320 NTSTATUS Status = STATUS_SUCCESS;
321 SM_PORT_MESSAGE Request;
322
323 DPRINT("SM: %s called\n", __FUNCTION__);
324 RtlZeroMemory(&Request, sizeof(PORT_MESSAGE));
325
326 while (TRUE)
327 {
328 Status = NtListenPort (ListeningPort, & Request.Header);
329 if (!NT_SUCCESS(Status))
330 {
331 DPRINT1("SM: %s: NtListenPort() failed! (Status==x%08lx)\n", __FUNCTION__, Status);
332 break;
333 }
334 Status = SmpHandleConnectionRequest (& Request);
335 if(!NT_SUCCESS(Status))
336 {
337 DPRINT1("SM: %s: SmpHandleConnectionRequest failed (Status=0x%08lx)\n",
338 __FUNCTION__, Status);
339 break;
340 }
341 }
342 /* Cleanup */
343 NtClose(ListeningPort);
344 /* DIE */
345 NtTerminateThread(NtCurrentThread(), Status);
346 }
347
348
349 /* LPC PORT INITIALIZATION **************************************************/
350
351
352 /**********************************************************************
353 * NAME
354 * SmCreateApiPort/0
355 *
356 * DECRIPTION
357 */
358 NTSTATUS
359 SmCreateApiPort(VOID)
360 {
361 OBJECT_ATTRIBUTES ObjectAttributes = {0};
362 UNICODE_STRING UnicodeString = RTL_CONSTANT_STRING(L"\\SmApiPort");
363 NTSTATUS Status = STATUS_SUCCESS;
364
365 InitializeObjectAttributes(&ObjectAttributes,
366 &UnicodeString,
367 0,
368 NULL,
369 NULL);
370
371 Status = NtCreatePort(&SmApiPort,
372 &ObjectAttributes,
373 0,
374 0,
375 0);
376 if (!NT_SUCCESS(Status))
377 {
378 return(Status);
379 }
380 /*
381 * Create one thread for the named LPC
382 * port \SmApiPort
383 */
384 RtlCreateUserThread(NtCurrentProcess(),
385 NULL,
386 FALSE,
387 0,
388 0,
389 0,
390 (PTHREAD_START_ROUTINE)SmpApiThread,
391 (PVOID)SmApiPort,
392 NULL,
393 NULL);
394
395 return(Status);
396 }
397
398 /* EOF */