Fixed CreateProcess() to use the command line parameter like NT does
[reactos.git] / reactos / lib / kernel32 / process / create.c
1 /* $Id: create.c,v 1.39 2001/06/18 03:02:43 phreak Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/proc/proc.c
6 * PURPOSE: Process functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * UPDATE HISTORY:
9 * Created 01/11/98
10 */
11
12 /* INCLUDES ****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <windows.h>
16 #include <kernel32/proc.h>
17 #include <kernel32/thread.h>
18 #include <wchar.h>
19 #include <string.h>
20 #include <napi/i386/segment.h>
21 #include <ntdll/ldr.h>
22 #include <napi/teb.h>
23 #include <ntdll/base.h>
24 #include <ntdll/rtl.h>
25 #include <csrss/csrss.h>
26 #include <ntdll/csr.h>
27
28 #define NDEBUG
29 #include <kernel32/kernel32.h>
30 #include <kernel32/error.h>
31
32 /* FUNCTIONS ****************************************************************/
33
34 WINBOOL STDCALL
35 CreateProcessA (LPCSTR lpApplicationName,
36 LPSTR lpCommandLine,
37 LPSECURITY_ATTRIBUTES lpProcessAttributes,
38 LPSECURITY_ATTRIBUTES lpThreadAttributes,
39 WINBOOL bInheritHandles,
40 DWORD dwCreationFlags,
41 LPVOID lpEnvironment,
42 LPCSTR lpCurrentDirectory,
43 LPSTARTUPINFOA lpStartupInfo,
44 LPPROCESS_INFORMATION lpProcessInformation)
45 /*
46 * FUNCTION: The CreateProcess function creates a new process and its
47 * primary thread. The new process executes the specified executable file
48 * ARGUMENTS:
49 *
50 * lpApplicationName = Pointer to name of executable module
51 * lpCommandLine = Pointer to command line string
52 * lpProcessAttributes = Process security attributes
53 * lpThreadAttributes = Thread security attributes
54 * bInheritHandles = Handle inheritance flag
55 * dwCreationFlags = Creation flags
56 * lpEnvironment = Pointer to new environment block
57 * lpCurrentDirectory = Pointer to current directory name
58 * lpStartupInfo = Pointer to startup info
59 * lpProcessInformation = Pointer to process information
60 */
61 {
62 UNICODE_STRING ApplicationNameU;
63 UNICODE_STRING CurrentDirectoryU;
64 UNICODE_STRING CommandLineU;
65 ANSI_STRING ApplicationName;
66 ANSI_STRING CurrentDirectory;
67 ANSI_STRING CommandLine;
68 WINBOOL Result;
69
70 DPRINT("CreateProcessA\n");
71
72 RtlInitAnsiString (&CommandLine,
73 lpCommandLine);
74 RtlInitAnsiString (&ApplicationName,
75 (LPSTR)lpApplicationName);
76 RtlInitAnsiString (&CurrentDirectory,
77 (LPSTR)lpCurrentDirectory);
78
79 /* convert ansi (or oem) strings to unicode */
80 if (bIsFileApiAnsi)
81 {
82 RtlAnsiStringToUnicodeString (&CommandLineU,
83 &CommandLine,
84 TRUE);
85 RtlAnsiStringToUnicodeString (&ApplicationNameU,
86 &ApplicationName,
87 TRUE);
88 RtlAnsiStringToUnicodeString (&CurrentDirectoryU,
89 &CurrentDirectory,
90 TRUE);
91 }
92 else
93 {
94 RtlOemStringToUnicodeString (&CommandLineU,
95 &CommandLine,
96 TRUE);
97 RtlOemStringToUnicodeString (&ApplicationNameU,
98 &ApplicationName,
99 TRUE);
100 RtlOemStringToUnicodeString (&CurrentDirectoryU,
101 &CurrentDirectory,
102 TRUE);
103 }
104
105 Result = CreateProcessW (ApplicationNameU.Buffer,
106 CommandLineU.Buffer,
107 lpProcessAttributes,
108 lpThreadAttributes,
109 bInheritHandles,
110 dwCreationFlags,
111 lpEnvironment,
112 (lpCurrentDirectory == NULL) ? NULL : CurrentDirectoryU.Buffer,
113 (LPSTARTUPINFOW)lpStartupInfo,
114 lpProcessInformation);
115
116 RtlFreeUnicodeString (&ApplicationNameU);
117 RtlFreeUnicodeString (&CommandLineU);
118 RtlFreeUnicodeString (&CurrentDirectoryU);
119
120 return Result;
121 }
122
123
124 HANDLE STDCALL
125 KlCreateFirstThread(HANDLE ProcessHandle,
126 LPSECURITY_ATTRIBUTES lpThreadAttributes,
127 DWORD dwStackSize,
128 LPTHREAD_START_ROUTINE lpStartAddress,
129 DWORD dwCreationFlags,
130 LPDWORD lpThreadId)
131 {
132 NTSTATUS Status;
133 HANDLE ThreadHandle;
134 OBJECT_ATTRIBUTES ObjectAttributes;
135 CLIENT_ID ClientId;
136 CONTEXT ThreadContext;
137 INITIAL_TEB InitialTeb;
138 BOOLEAN CreateSuspended = FALSE;
139 PVOID BaseAddress;
140
141 ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
142 ObjectAttributes.RootDirectory = NULL;
143 ObjectAttributes.ObjectName = NULL;
144 ObjectAttributes.Attributes = 0;
145 if (lpThreadAttributes != NULL)
146 {
147 if (lpThreadAttributes->bInheritHandle)
148 ObjectAttributes.Attributes = OBJ_INHERIT;
149 ObjectAttributes.SecurityDescriptor =
150 lpThreadAttributes->lpSecurityDescriptor;
151 }
152 ObjectAttributes.SecurityQualityOfService = NULL;
153
154 if ((dwCreationFlags & CREATE_SUSPENDED) == CREATE_SUSPENDED)
155 CreateSuspended = TRUE;
156 else
157 CreateSuspended = FALSE;
158
159 /* Allocate thread stack */
160 BaseAddress = NULL;
161 Status = NtAllocateVirtualMemory(ProcessHandle,
162 &BaseAddress,
163 0,
164 (PULONG)&dwStackSize,
165 MEM_COMMIT,
166 PAGE_READWRITE);
167 if (!NT_SUCCESS(Status))
168 {
169 return(NULL);
170 }
171
172 memset(&ThreadContext,0,sizeof(CONTEXT));
173 ThreadContext.Eip = (ULONG)lpStartAddress;
174 ThreadContext.SegGs = USER_DS;
175 ThreadContext.SegFs = USER_DS;
176 ThreadContext.SegEs = USER_DS;
177 ThreadContext.SegDs = USER_DS;
178 ThreadContext.SegCs = USER_CS;
179 ThreadContext.SegSs = USER_DS;
180 ThreadContext.Esp = (ULONG)(BaseAddress + dwStackSize - 20);
181 ThreadContext.EFlags = (1<<1) + (1<<9);
182
183 DPRINT("ThreadContext.Eip %x\n",ThreadContext.Eip);
184
185 Status = NtCreateThread(&ThreadHandle,
186 THREAD_ALL_ACCESS,
187 &ObjectAttributes,
188 ProcessHandle,
189 &ClientId,
190 &ThreadContext,
191 &InitialTeb,
192 CreateSuspended);
193 if (lpThreadId != NULL)
194 {
195 memcpy(lpThreadId, &ClientId.UniqueThread,sizeof(ULONG));
196 }
197
198 return(ThreadHandle);
199 }
200
201 HANDLE
202 KlMapFile(LPCWSTR lpApplicationName,
203 LPCWSTR lpCommandLine)
204 {
205 HANDLE hFile;
206 IO_STATUS_BLOCK IoStatusBlock;
207 UNICODE_STRING ApplicationNameString;
208 OBJECT_ATTRIBUTES ObjectAttributes;
209 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
210 NTSTATUS Status;
211 HANDLE hSection;
212
213 hFile = NULL;
214
215 /*
216 * Find the application name
217 */
218
219 if (!RtlDosPathNameToNtPathName_U ((LPWSTR)lpApplicationName,
220 &ApplicationNameString,
221 NULL,
222 NULL))
223 return NULL;
224
225 DPRINT("ApplicationName %S\n",ApplicationNameString.Buffer);
226
227 InitializeObjectAttributes(&ObjectAttributes,
228 &ApplicationNameString,
229 OBJ_CASE_INSENSITIVE,
230 NULL,
231 SecurityDescriptor);
232
233 /*
234 * Try to open the executable
235 */
236
237 Status = NtOpenFile(&hFile,
238 SYNCHRONIZE|FILE_EXECUTE|FILE_READ_DATA,
239 &ObjectAttributes,
240 &IoStatusBlock,
241 FILE_SHARE_DELETE|FILE_SHARE_READ,
242 FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE);
243
244 RtlFreeUnicodeString (&ApplicationNameString);
245
246 if (!NT_SUCCESS(Status))
247 {
248 DPRINT("Failed to open file\n");
249 SetLastErrorByStatus (Status);
250 return(NULL);
251 }
252
253 Status = NtCreateSection(&hSection,
254 SECTION_ALL_ACCESS,
255 NULL,
256 NULL,
257 PAGE_EXECUTE,
258 SEC_IMAGE,
259 hFile);
260 NtClose(hFile);
261
262 if (!NT_SUCCESS(Status))
263 {
264 DPRINT("Failed to create section\n");
265 SetLastErrorByStatus (Status);
266 return(NULL);
267 }
268
269 return(hSection);
270 }
271
272 static NTSTATUS
273 KlInitPeb (HANDLE ProcessHandle,
274 PRTL_USER_PROCESS_PARAMETERS Ppb)
275 {
276 NTSTATUS Status;
277 PVOID PpbBase;
278 ULONG PpbSize;
279 ULONG BytesWritten;
280 ULONG Offset;
281 PVOID ParentEnv = NULL;
282 PVOID EnvPtr = NULL;
283 ULONG EnvSize = 0;
284
285 /* create the Environment */
286 if (Ppb->Environment != NULL)
287 ParentEnv = Ppb->Environment;
288 else if (NtCurrentPeb()->ProcessParameters->Environment != NULL)
289 ParentEnv = NtCurrentPeb()->ProcessParameters->Environment;
290
291 if (ParentEnv != NULL)
292 {
293 MEMORY_BASIC_INFORMATION MemInfo;
294
295 Status = NtQueryVirtualMemory (NtCurrentProcess (),
296 ParentEnv,
297 MemoryBasicInformation,
298 &MemInfo,
299 sizeof(MEMORY_BASIC_INFORMATION),
300 NULL);
301 if (!NT_SUCCESS(Status))
302 {
303 return Status;
304 }
305 EnvSize = MemInfo.RegionSize;
306 }
307 DPRINT("EnvironmentSize %ld\n", EnvSize);
308
309 /* allocate and initialize new environment block */
310 if (EnvSize != 0)
311 {
312 Status = NtAllocateVirtualMemory(ProcessHandle,
313 &EnvPtr,
314 0,
315 &EnvSize,
316 MEM_COMMIT,
317 PAGE_READWRITE);
318 if (!NT_SUCCESS(Status))
319 {
320 return(Status);
321 }
322
323 NtWriteVirtualMemory(ProcessHandle,
324 EnvPtr,
325 ParentEnv,
326 EnvSize,
327 &BytesWritten);
328 }
329
330 /* create the PPB */
331 PpbBase = (PVOID)PEB_STARTUPINFO;
332 PpbSize = Ppb->MaximumLength;
333 Status = NtAllocateVirtualMemory(ProcessHandle,
334 &PpbBase,
335 0,
336 &PpbSize,
337 MEM_COMMIT,
338 PAGE_READWRITE);
339 if (!NT_SUCCESS(Status))
340 {
341 return(Status);
342 }
343
344 DPRINT("Ppb->MaximumLength %x\n", Ppb->MaximumLength);
345 NtWriteVirtualMemory(ProcessHandle,
346 PpbBase,
347 Ppb,
348 Ppb->MaximumLength,
349 &BytesWritten);
350
351 /* write pointer to environment */
352 Offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Environment);
353 NtWriteVirtualMemory(ProcessHandle,
354 (PVOID)(PpbBase + Offset),
355 &EnvPtr,
356 sizeof(EnvPtr),
357 &BytesWritten);
358
359 /* write pointer to process parameter block */
360 Offset = FIELD_OFFSET(PEB, ProcessParameters);
361 NtWriteVirtualMemory(ProcessHandle,
362 (PVOID)(PEB_BASE + Offset),
363 &PpbBase,
364 sizeof(PpbBase),
365 &BytesWritten);
366
367 return(STATUS_SUCCESS);
368 }
369
370
371 WINBOOL STDCALL
372 CreateProcessW(LPCWSTR lpApplicationName,
373 LPWSTR lpCommandLine,
374 LPSECURITY_ATTRIBUTES lpProcessAttributes,
375 LPSECURITY_ATTRIBUTES lpThreadAttributes,
376 WINBOOL bInheritHandles,
377 DWORD dwCreationFlags,
378 LPVOID lpEnvironment,
379 LPCWSTR lpCurrentDirectory,
380 LPSTARTUPINFOW lpStartupInfo,
381 LPPROCESS_INFORMATION lpProcessInformation)
382 {
383 HANDLE hSection, hProcess, hThread;
384 NTSTATUS Status;
385 LPTHREAD_START_ROUTINE lpStartAddress = NULL;
386 WCHAR TempCommandLine[256];
387 WCHAR ImagePathName[256];
388 UNICODE_STRING ImagePathName_U;
389 PROCESS_BASIC_INFORMATION ProcessBasicInfo;
390 ULONG retlen;
391 PRTL_USER_PROCESS_PARAMETERS Ppb;
392 UNICODE_STRING CommandLine_U;
393 CSRSS_API_REQUEST CsrRequest;
394 CSRSS_API_REPLY CsrReply;
395 CHAR ImageFileName[8];
396 PWCHAR s;
397 PWCHAR e;
398 ULONG i;
399 ANSI_STRING ProcedureName;
400 UNICODE_STRING CurrentDirectoryW;
401 SECTION_IMAGE_INFORMATION Sii;
402
403 DPRINT("CreateProcessW(lpApplicationName '%S', lpCommandLine '%S')\n",
404 lpApplicationName,lpCommandLine);
405
406 /*
407 * Store the image file name for the process
408 */
409 s = wcsrchr(lpApplicationName, '\\');
410 if (s == NULL)
411 {
412 s = (PWCHAR)lpApplicationName;
413 }
414 else
415 {
416 s++;
417 }
418 e = wcschr(s, '.');
419 if (e != NULL)
420 {
421 *e = 0;
422 }
423 for (i = 0; i < 8; i++)
424 {
425 ImageFileName[i] = (CHAR)(s[i]);
426 }
427 if (e != NULL)
428 {
429 *e = '.';
430 }
431
432 /*
433 * Process the application name and command line
434 */
435
436 RtlGetFullPathName_U ((LPWSTR)lpApplicationName,
437 256 * sizeof(WCHAR),
438 TempCommandLine,
439 NULL);
440 wcscpy(ImagePathName, TempCommandLine);
441 RtlInitUnicodeString(&ImagePathName_U, ImagePathName);
442
443 if (lpCommandLine != NULL)
444 {
445 wcscat(TempCommandLine, L" ");
446 wcscat(TempCommandLine, lpCommandLine);
447 }
448
449 /* Initialize the current directory string */
450 RtlInitUnicodeString(&CurrentDirectoryW,
451 lpCurrentDirectory);
452
453 /*
454 * Create the PPB
455 */
456
457 RtlInitUnicodeString(&CommandLine_U, lpCommandLine);
458
459 DPRINT("CommandLine_U %S\n", CommandLine_U.Buffer);
460
461 RtlCreateProcessParameters(&Ppb,
462 &ImagePathName_U,
463 NULL,
464 (lpCurrentDirectory == NULL) ? NULL : &CurrentDirectoryW,
465 &CommandLine_U,
466 lpEnvironment,
467 NULL,
468 NULL,
469 NULL,
470 NULL);
471
472 /*
473 * Create a section for the executable
474 */
475
476 hSection = KlMapFile (lpApplicationName, lpCommandLine);
477 if (hSection == NULL)
478 {
479 RtlDestroyProcessParameters (Ppb);
480 return FALSE;
481 }
482
483 /*
484 * Create a new process
485 */
486 Status = NtCreateProcess(&hProcess,
487 PROCESS_ALL_ACCESS,
488 NULL,
489 NtCurrentProcess(),
490 bInheritHandles,
491 hSection,
492 NULL,
493 NULL);
494
495 /*
496 * Get some information about the executable
497 */
498 Status = ZwQuerySection(hSection,
499 SectionImageInformation,
500 &Sii,
501 sizeof(Sii),
502 &i);
503
504 /*
505 * Close the section
506 */
507 NtClose(hSection);
508
509 /*
510 * Get some information about the process
511 */
512 ZwQueryInformationProcess(hProcess,
513 ProcessBasicInformation,
514 &ProcessBasicInfo,
515 sizeof(ProcessBasicInfo),
516 &retlen);
517 DPRINT("ProcessBasicInfo.UniqueProcessId %d\n",
518 ProcessBasicInfo.UniqueProcessId);
519 lpProcessInformation->dwProcessId = ProcessBasicInfo.UniqueProcessId;
520
521 /*
522 * Tell the csrss server we are creating a new process
523 */
524 CsrRequest.Type = CSRSS_CREATE_PROCESS;
525 CsrRequest.Data.CreateProcessRequest.NewProcessId =
526 ProcessBasicInfo.UniqueProcessId;
527 CsrRequest.Data.CreateProcessRequest.Flags = dwCreationFlags;
528 Status = CsrClientCallServer(&CsrRequest,
529 &CsrReply,
530 sizeof(CSRSS_API_REQUEST),
531 sizeof(CSRSS_API_REPLY));
532 if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrReply.Status))
533 {
534 DbgPrint("Failed to tell csrss about new process. Expect trouble.\n");
535 }
536
537 /*
538 * Create Process Environment Block
539 */
540 DPRINT("Creating peb\n");
541
542 Ppb->InputHandle = CsrReply.Data.CreateProcessReply.InputHandle;
543 Ppb->OutputHandle = CsrReply.Data.CreateProcessReply.OutputHandle;;
544 Ppb->ErrorHandle = Ppb->OutputHandle;
545 KlInitPeb(hProcess, Ppb);
546
547 RtlDestroyProcessParameters (Ppb);
548
549 Status = NtSetInformationProcess(hProcess,
550 ProcessImageFileName,
551 ImageFileName,
552 8);
553 /*
554 * Retrieve the start address
555 */
556 DPRINT("Retrieving entry point address\n");
557 RtlInitAnsiString (&ProcedureName, "LdrInitializeThunk");
558 Status = LdrGetProcedureAddress ((PVOID)NTDLL_BASE,
559 &ProcedureName,
560 0,
561 (PVOID*)&lpStartAddress);
562 if (!NT_SUCCESS(Status))
563 {
564 DbgPrint ("LdrGetProcedureAddress failed (Status %x)\n", Status);
565 return (Status);
566 }
567 DPRINT("lpStartAddress 0x%08lx\n", (ULONG)lpStartAddress);
568
569 /*
570 * Create the thread for the kernel
571 */
572 DPRINT("Creating thread for process\n");
573 hThread = KlCreateFirstThread(hProcess,
574 lpThreadAttributes,
575 //Sii.StackReserve,
576 0x200000,
577 lpStartAddress,
578 dwCreationFlags,
579 &lpProcessInformation->dwThreadId);
580
581 if (hThread == NULL)
582 {
583 return FALSE;
584 }
585
586 lpProcessInformation->hProcess = hProcess;
587 lpProcessInformation->hThread = hThread;
588
589 return TRUE;
590 }
591
592 /* EOF */