SM: change the way sessions are managed.
[reactos.git] / reactos / subsys / smss / smapiexec.c
1 /* $Id$
2 *
3 * smapiexec.c - SM_API_EXECUTE_PROGRAM
4 *
5 * Reactos Session Manager
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
28 #define NDEBUG
29 #include <debug.h>
30
31 /**********************************************************************
32 * SmCreateUserProcess/5
33 *
34 * DESCRIPTION
35 *
36 * ARGUMENTS
37 * ImagePath: bsolute path of the image to run;
38 * CommandLine: arguments and options for ImagePath;
39 * WaitForIt: TRUE for boot time processes and FALSE for
40 * subsystems bootstrapping;
41 * Timeout: optional: used if WaitForIt==TRUE;
42 * ProcessHandle: optional: a duplicated handle for
43 the child process (storage provided by the caller).
44 *
45 * RETURN VALUE
46 * NTSTATUS:
47 *
48 */
49 NTSTATUS STDCALL
50 SmCreateUserProcess (LPWSTR ImagePath,
51 LPWSTR CommandLine,
52 BOOLEAN WaitForIt,
53 PLARGE_INTEGER Timeout OPTIONAL,
54 PRTL_PROCESS_INFO UserProcessInfo OPTIONAL)
55 {
56 UNICODE_STRING ImagePathString = {0};
57 UNICODE_STRING CommandLineString = {0};
58 PRTL_USER_PROCESS_PARAMETERS ProcessParameters = NULL;
59 RTL_PROCESS_INFO ProcessInfo = {0};
60 PRTL_PROCESS_INFO pProcessInfo = & ProcessInfo;
61 NTSTATUS Status = STATUS_SUCCESS;
62
63 DPRINT("SM: %s called\n", __FUNCTION__);
64
65 if (NULL != UserProcessInfo)
66 {
67 pProcessInfo = UserProcessInfo;
68 }
69
70 RtlInitUnicodeString (& ImagePathString, ImagePath);
71 RtlInitUnicodeString (& CommandLineString, CommandLine);
72
73 RtlCreateProcessParameters(& ProcessParameters,
74 & ImagePathString,
75 NULL,
76 NULL,
77 & CommandLineString,
78 SmSystemEnvironment,
79 NULL,
80 NULL,
81 NULL,
82 NULL);
83
84 Status = RtlCreateUserProcess (& ImagePathString,
85 OBJ_CASE_INSENSITIVE,
86 ProcessParameters,
87 NULL,
88 NULL,
89 NULL,
90 FALSE,
91 NULL,
92 NULL,
93 pProcessInfo);
94
95 RtlDestroyProcessParameters (ProcessParameters);
96
97 if (!NT_SUCCESS(Status))
98 {
99 DPRINT1("SM: %s: Running \"%S\" failed (Status=0x%08lx)\n",
100 __FUNCTION__, ImagePathString.Buffer, Status);
101 return Status;
102 }
103 /*
104 * It the caller is *not* interested in the child info,
105 * resume it immediately.
106 */
107 if (NULL == UserProcessInfo)
108 {
109 Status = NtResumeThread (ProcessInfo.ThreadHandle, NULL);
110 if(!NT_SUCCESS(Status))
111 {
112 DPRINT1("SM: %s: NtResumeThread failed (Status=0x%08lx)\n",
113 __FUNCTION__, Status);
114 }
115 }
116 else
117 {
118 HANDLE DupProcessHandle = (HANDLE) 0;
119
120 Status = NtDuplicateObject (NtCurrentProcess(),
121 pProcessInfo->ProcessHandle,
122 NtCurrentProcess(),
123 & DupProcessHandle,
124 PROCESS_ALL_ACCESS,
125 0, 0);
126 if(!NT_SUCCESS(Status))
127 {
128 DPRINT1("SM: %s: NtDuplicateObject failed (Status=0x%08lx)\n",
129 __FUNCTION__, Status);
130 }
131 pProcessInfo->ProcessHandle = DupProcessHandle;
132
133 }
134
135 /* Wait for process termination */
136 if (WaitForIt)
137 {
138 Status = NtWaitForSingleObject (pProcessInfo->ProcessHandle,
139 FALSE,
140 Timeout);
141 if (!NT_SUCCESS(Status))
142 {
143 DPRINT1("SM: %s: NtWaitForSingleObject failed with Status=0x%08lx\n",
144 __FUNCTION__, Status);
145 }
146
147 }
148 return Status;
149 }
150
151 /**********************************************************************
152 * NAME
153 * SmLookupSubsystem/5
154 *
155 * DESCRIPTION
156 * Read from the registry key
157 * \Registry\SYSTEM\CurrentControlSet\Control\Session Manager\Subsystems
158 * the value which name is Name.
159 *
160 * ARGUMENTS
161 * Name: name of the program to run, that is a value's name in
162 * the SM registry key Subsystems;
163 * Data: what the registry gave back for Name;
164 * DataLength: how much Data the registry returns;
165 * DataType: what is Data?
166 * Expand: set it TRUE if you want this function to use the env
167 * to possibly expand Data before giving it back.
168 */
169 NTSTATUS STDCALL
170 SmLookupSubsystem (IN PWSTR Name,
171 IN OUT PWSTR Data,
172 IN OUT PULONG DataLength,
173 IN OUT PULONG DataType,
174 IN BOOLEAN Expand)
175 {
176 NTSTATUS Status = STATUS_SUCCESS;
177 UNICODE_STRING usKeyName = {0};
178 OBJECT_ATTRIBUTES Oa = {0};
179 HANDLE hKey = (HANDLE) 0;
180
181 DPRINT("SM: %s(Name='%S') called\n", __FUNCTION__, Name);
182 /*
183 * Prepare the key name to scan and
184 * related object attributes.
185 */
186 RtlInitUnicodeString (& usKeyName,
187 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\SubSystems");
188
189 InitializeObjectAttributes (& Oa,
190 & usKeyName,
191 OBJ_CASE_INSENSITIVE,
192 NULL,
193 NULL);
194 /*
195 * Open the key. This MUST NOT fail, if the
196 * request is for a legitimate subsystem.
197 */
198 Status = NtOpenKey (& hKey,
199 MAXIMUM_ALLOWED,
200 & Oa);
201 if(NT_SUCCESS(Status))
202 {
203 UNICODE_STRING usValueName = {0};
204 WCHAR KeyValueInformation [1024] = {L'\0'};
205 ULONG ResultLength = 0L;
206 PKEY_VALUE_PARTIAL_INFORMATION
207 kvpi = (PKEY_VALUE_PARTIAL_INFORMATION) KeyValueInformation;
208
209
210 RtlInitUnicodeString (& usValueName, Name);
211 Status = NtQueryValueKey (hKey,
212 & usValueName,
213 KeyValuePartialInformation,
214 KeyValueInformation,
215 sizeof KeyValueInformation,
216 & ResultLength);
217 if(NT_SUCCESS(Status))
218 {
219 DPRINT("nkvpi.TitleIndex = %ld\n", kvpi->TitleIndex);
220 DPRINT("kvpi.Type = %ld\n", kvpi->Type);
221 DPRINT("kvpi.DataLength = %ld\n", kvpi->DataLength);
222
223 if((NULL != Data) && (NULL != DataLength) && (NULL != DataType))
224 {
225 *DataType = kvpi->Type;
226 if((Expand) && (REG_EXPAND_SZ == *DataType))
227 {
228 UNICODE_STRING Source;
229 WCHAR DestinationBuffer [2048] = {0};
230 UNICODE_STRING Destination;
231 ULONG Length = 0;
232
233 DPRINT("SM: %s: value will be expanded\n", __FUNCTION__);
234
235 Source.Length = kvpi->DataLength;
236 Source.MaximumLength = kvpi->DataLength;
237 Source.Buffer = (PWCHAR) & kvpi->Data;
238
239 Destination.Length = 0;
240 Destination.MaximumLength = sizeof DestinationBuffer;
241 Destination.Buffer = DestinationBuffer;
242
243 Status = RtlExpandEnvironmentStrings_U (SmSystemEnvironment,
244 & Source,
245 & Destination,
246 & Length);
247 if(NT_SUCCESS(Status))
248 {
249 *DataLength = min(*DataLength, Destination.Length);
250 RtlCopyMemory (Data, Destination.Buffer, *DataLength);
251 }
252
253 }else{
254 DPRINT("SM: %s: value won't be expanded\n", __FUNCTION__);
255 *DataLength = min(*DataLength, kvpi->DataLength);
256 RtlCopyMemory (Data, & kvpi->Data, *DataLength);
257 }
258 *DataType = kvpi->Type;
259 }else{
260 DPRINT1("SM: %s: Data or DataLength or DataType is NULL!\n", __FUNCTION__);
261 Status = STATUS_INVALID_PARAMETER;
262 }
263 }else{
264 DPRINT1("%s: NtQueryValueKey failed (Status=0x%08lx)\n", __FUNCTION__, Status);
265 }
266 NtClose (hKey);
267 }else{
268 DPRINT1("%s: NtOpenKey failed (Status=0x%08lx)\n", __FUNCTION__, Status);
269 }
270 return Status;
271 }
272
273
274 /**********************************************************************
275 * SmExecPgm/1 API
276 */
277 SMAPI(SmExecPgm)
278 {
279 PSM_PORT_MESSAGE_EXECPGM ExecPgm = NULL;
280 WCHAR Name [SM_EXEXPGM_MAX_LENGTH + 1];
281 NTSTATUS Status = STATUS_SUCCESS;
282
283 DPRINT("SM: %s called\n",__FUNCTION__);
284
285 if(NULL == Request)
286 {
287 DPRINT1("SM: %s: Request == NULL!\n", __FUNCTION__);
288 return STATUS_INVALID_PARAMETER;
289 }
290 DPRINT("SM: %s called from CID(%lx|%lx)\n",
291 __FUNCTION__, Request->Header.ClientId.UniqueProcess,
292 Request->Header.ClientId.UniqueThread);
293 ExecPgm = & Request->Request.ExecPgm;
294 /* Check if the name lenght is valid */
295 if((ExecPgm->NameLength > 0) &&
296 (ExecPgm->NameLength <= SM_EXEXPGM_MAX_LENGTH) &&
297 TRUE /* TODO: check LPC payload size */)
298 {
299 WCHAR Data [MAX_PATH + 1] = {0};
300 ULONG DataLength = sizeof Data;
301 ULONG DataType = REG_EXPAND_SZ;
302
303
304 RtlZeroMemory (Name, sizeof Name);
305 RtlCopyMemory (Name,
306 ExecPgm->Name,
307 (sizeof ExecPgm->Name[0] * ExecPgm->NameLength));
308 DPRINT("SM: %s: Name='%S'\n", __FUNCTION__, Name);
309 /* Lookup Name in the registry */
310 Status = SmLookupSubsystem (Name,
311 Data,
312 & DataLength,
313 & DataType,
314 TRUE); /* expand */
315 if(NT_SUCCESS(Status))
316 {
317 /* Is the subsystem definition non-empty? */
318 if (DataLength > sizeof Data[0])
319 {
320 WCHAR ImagePath [MAX_PATH + 1] = {0};
321 PWCHAR CommandLine = ImagePath;
322 RTL_PROCESS_INFO ProcessInfo = {0};
323
324 wcscpy (ImagePath, L"\\??\\");
325 wcscat (ImagePath, Data);
326 /*
327 * Look for the beginning of the command line.
328 */
329 for (; (*CommandLine != L'\0') && (*CommandLine != L' ');
330 CommandLine ++);
331 for (; *CommandLine == L' '; CommandLine ++)
332 {
333 *CommandLine = L'\0';
334 }
335 /*
336 * Create a native process (suspended).
337 */
338 ProcessInfo.Size = sizeof ProcessInfo;
339 Request->SmHeader.Status =
340 SmCreateUserProcess(ImagePath,
341 CommandLine,
342 FALSE, /* wait */
343 NULL, /* timeout */
344 & ProcessInfo);
345 if (NT_SUCCESS(Request->SmHeader.Status))
346 {
347 Status = SmCreateClient (& ProcessInfo, Name);
348 if (NT_SUCCESS(Status))
349 {
350 Status = NtResumeThread (ProcessInfo.ThreadHandle, NULL);
351 if (!NT_SUCCESS(Status))
352 {
353 //Status = SmDestroyClient TODO
354 }
355 } else {
356 DPRINT1("SM: %s: SmCreateClient failed (Status=0x%08lx)\n",
357 __FUNCTION__, Status);
358 }
359 }
360 }
361 else
362 {
363 /*
364 * OK, the definition is empty, but check
365 * if it is the name of an embedded subsystem.
366 */
367 if(0 == _wcsicmp(L"DEBUG", Name))
368 {
369 /*
370 * Initialize the embedded DBGSS.
371 */
372 Request->SmHeader.Status = SmInitializeDbgSs();
373 }
374 else
375 {
376 /*
377 * Badly defined subsystem. Check the registry!
378 */
379 Request->SmHeader.Status = STATUS_NOT_FOUND;
380 }
381 }
382 } else {
383 /* It couldn't lookup the Name! */
384 Request->SmHeader.Status = Status;
385 }
386 }
387 return Status;
388 }
389
390 /* EOF */