1 /*
2 * Win32 processes
3 *
4 * Copyright 1996, 1998 Alexandre Julliard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <time.h>
30 #ifdef HAVE_SYS_TIME_H
31 # include <sys/time.h>
32 #endif
33 #ifdef HAVE_SYS_IOCTL_H
34 #include <sys/ioctl.h>
35 #endif
36 #ifdef HAVE_SYS_SOCKET_H
37 #include <sys/socket.h>
38 #endif
39 #ifdef HAVE_SYS_PRCTL_H
40 # include <sys/prctl.h>
41 #endif
42 #include <sys/types.h>
43
44 #include "ntstatus.h"
45 #define WIN32_NO_STATUS
46 #include "wine/winbase16.h"
47 #include "wine/winuser16.h"
48 #include "winternl.h"
49 #include "kernel_private.h"
50 #include "wine/exception.h"
51 #include "wine/server.h"
52 #include "wine/unicode.h"
53 #include "wine/debug.h"
54
55 WINE_DEFAULT_DEBUG_CHANNEL(process);
56 WINE_DECLARE_DEBUG_CHANNEL(file);
57 WINE_DECLARE_DEBUG_CHANNEL(relay);
58
59 typedef struct
60 {
61 LPSTR lpEnvAddress;
62 LPSTR lpCmdLine;
63 LPSTR lpCmdShow;
64 DWORD dwReserved;
65 } LOADPARMS32;
66
67 static UINT process_error_mode;
68
69 static DWORD shutdown_flags = 0;
70 static DWORD shutdown_priority = 0x280;
71 static DWORD process_dword;
72
73 HMODULE kernel32_handle = 0;
74
75 const WCHAR *DIR_Windows = NULL;
76 const WCHAR *DIR_System = NULL;
77
78 /* Process flags */
79 #define PDB32_DEBUGGED 0x0001 /* Process is being debugged */
80 #define PDB32_WIN16_PROC 0x0008 /* Win16 process */
81 #define PDB32_DOS_PROC 0x0010 /* Dos process */
82 #define PDB32_CONSOLE_PROC 0x0020 /* Console process */
83 #define PDB32_FILE_APIS_OEM 0x0040 /* File APIs are OEM */
84 #define PDB32_WIN32S_PROC 0x8000 /* Win32s process */
85
86 static const WCHAR comW[] = {'.','c','o','m',0};
87 static const WCHAR batW[] = {'.','b','a','t',0};
88 static const WCHAR cmdW[] = {'.','c','m','d',0};
89 static const WCHAR pifW[] = {'.','p','i','f',0};
90 static const WCHAR winevdmW[] = {'w','i','n','e','v','d','m','.','e','x','e',0};
91
92 static void exec_process( LPCWSTR name );
93
94 extern void SHELL_LoadRegistry(void);
95
96
97 /***********************************************************************
98 * contains_path
99 */
100 static inline int contains_path( LPCWSTR name )
101 {
102 return ((*name && (name[1] == ':')) || strchrW(name, '/') || strchrW(name, '\\'));
103 }
104
105
106 /***********************************************************************
107 * is_special_env_var
108 *
109 * Check if an environment variable needs to be handled specially when
110 * passed through the Unix environment (i.e. prefixed with "WINE").
111 */
112 static inline int is_special_env_var( const char *var )
113 {
114 return (!strncmp( var, "PATH=", sizeof("PATH=")-1 ) ||
115 !strncmp( var, "HOME=", sizeof("HOME=")-1 ) ||
116 !strncmp( var, "TEMP=", sizeof("TEMP=")-1 ) ||
117 !strncmp( var, "TMP=", sizeof("TMP=")-1 ));
118 }
119
120
121 /***************************************************************************
122 * get_builtin_path
123 *
124 * Get the path of a builtin module when the native file does not exist.
125 */
126 static BOOL get_builtin_path( const WCHAR *libname, const WCHAR *ext, WCHAR *filename, UINT size )
127 {
128 WCHAR *file_part;
129 UINT len = strlenW( DIR_System );
130
131 if (contains_path( libname ))
132 {
133 if (RtlGetFullPathName_U( libname, size * sizeof(WCHAR),
134 filename, &file_part ) > size * sizeof(WCHAR))
135 return FALSE; /* too long */
136
137 if (strncmpiW( filename, DIR_System, len ) || filename[len] != '\\')
138 return FALSE;
139 while (filename[len] == '\\') len++;
140 if (filename + len != file_part) return FALSE;
141 }
142 else
143 {
144 if (strlenW(libname) + len + 2 >= size) return FALSE; /* too long */
145 memcpy( filename, DIR_System, len * sizeof(WCHAR) );
146 file_part = filename + len;
147 if (file_part > filename && file_part[-1] != '\\') *file_part++ = '\\';
148 strcpyW( file_part, libname );
149 }
150 if (ext && !strchrW( file_part, '.' ))
151 {
152 if (file_part + strlenW(file_part) + strlenW(ext) + 1 > filename + size)
153 return FALSE; /* too long */
154 strcatW( file_part, ext );
155 }
156 return TRUE;
157 }
158
159
160 /***********************************************************************
161 * open_builtin_exe_file
162 *
163 * Open an exe file for a builtin exe.
164 */
165 static void *open_builtin_exe_file( const WCHAR *name, char *error, int error_size,
166 int test_only, int *file_exists )
167 {
168 char exename[MAX_PATH];
169 WCHAR *p;
170 UINT i, len;
171
172 *file_exists = 0;
173 if ((p = strrchrW( name, '/' ))) name = p + 1;
174 if ((p = strrchrW( name, '\\' ))) name = p + 1;
175
176 /* we don't want to depend on the current codepage here */
177 len = strlenW( name ) + 1;
178 if (len >= sizeof(exename)) return NULL;
179 for (i = 0; i < len; i++)
180 {
181 if (name[i] > 127) return NULL;
182 exename[i] = (char)name[i];
183 if (exename[i] >= 'A' && exename[i] <= 'Z') exename[i] += 'a' - 'A';
184 }
185 return wine_dll_load_main_exe( exename, error, error_size, test_only, file_exists );
186 }
187
188
189 /***********************************************************************
190 * open_exe_file
191 *
192 * Open a specific exe file, taking load order into account.
193 * Returns the file handle or 0 for a builtin exe.
194 */
195 static HANDLE open_exe_file( const WCHAR *name )
196 {
197 HANDLE handle;
198
199 TRACE("looking for %s\n", debugstr_w(name) );
200
201 if ((handle = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE,
202 NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE)
203 {
204 WCHAR buffer[MAX_PATH];
205 /* file doesn't exist, check for builtin */
206 if (!contains_path( name )) goto error;
207 if (!get_builtin_path( name, NULL, buffer, sizeof(buffer) )) goto error;
208 handle = 0;
209 }
210 return handle;
211
212 error:
213 SetLastError( ERROR_FILE_NOT_FOUND );
214 return INVALID_HANDLE_VALUE;
215 }
216
217
218 /***********************************************************************
219 * find_exe_file
220 *
221 * Open an exe file, and return the full name and file handle.
222 * Returns FALSE if file could not be found.
223 * If file exists but cannot be opened, returns TRUE and set handle to INVALID_HANDLE_VALUE.
224 * If file is a builtin exe, returns TRUE and sets handle to 0.
225 */
226 static BOOL find_exe_file( const WCHAR *name, WCHAR *buffer, int buflen, HANDLE *handle )
227 {
228 static const WCHAR exeW[] = {'.','e','x','e',0};
229 int file_exists;
230
231 TRACE("looking for %s\n", debugstr_w(name) );
232
233 if (!SearchPathW( NULL, name, exeW, buflen, buffer, NULL ) &&
234 !get_builtin_path( name, exeW, buffer, buflen ))
235 {
236 /* no builtin found, try native without extension in case it is a Unix app */
237
238 if (SearchPathW( NULL, name, NULL, buflen, buffer, NULL ))
239 {
240 TRACE( "Trying native/Unix binary %s\n", debugstr_w(buffer) );
241 if ((*handle = CreateFileW( buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE,
242 NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
243 return TRUE;
244 }
245 return FALSE;
246 }
247
248 TRACE( "Trying native exe %s\n", debugstr_w(buffer) );
249 if ((*handle = CreateFileW( buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE,
250 NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
251 return TRUE;
252
253 TRACE( "Trying built-in exe %s\n", debugstr_w(buffer) );
254 open_builtin_exe_file( buffer, NULL, 0, 1, &file_exists );
255 if (file_exists)
256 {
257 *handle = 0;
258 return TRUE;
259 }
260
261 return FALSE;
262 }
263
264
265 /***********************************************************************
266 * build_initial_environment
267 *
268 * Build the Win32 environment from the Unix environment
269 */
270 static BOOL build_initial_environment( char **environ )
271 {
272 SIZE_T size = 1;
273 char **e;
274 WCHAR *p, *endptr;
275 void *ptr;
276
277 /* Compute the total size of the Unix environment */
278 for (e = environ; *e; e++)
279 {
280 if (is_special_env_var( *e )) continue;
281 size += MultiByteToWideChar( CP_UNIXCP, 0, *e, -1, NULL, 0 );
282 }
283 size *= sizeof(WCHAR);
284
285 /* Now allocate the environment */
286 ptr = NULL;
287 if (NtAllocateVirtualMemory(NtCurrentProcess(), &ptr, 0, &size,
288 MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) != STATUS_SUCCESS)
289 return FALSE;
290
291 NtCurrentTeb()->Peb->ProcessParameters->Environment = p = ptr;
292 endptr = p + size / sizeof(WCHAR);
293
294 /* And fill it with the Unix environment */
295 for (e = environ; *e; e++)
296 {
297 char *str = *e;
298
299 /* skip Unix special variables and use the Wine variants instead */
300 if (!strncmp( str, "WINE", 4 ))
301 {
302 if (is_special_env_var( str + 4 )) str += 4;
303 else if (!strncmp( str, "WINEPRELOADRESERVE=", 19 )) continue; /* skip it */
304 }
305 else if (is_special_env_var( str )) continue; /* skip it */
306
307 MultiByteToWideChar( CP_UNIXCP, 0, str, -1, p, endptr - p );
308 p += strlenW(p) + 1;
309 }
310 *p = 0;
311 return TRUE;
312 }
313
314
315 /***********************************************************************
316 * set_registry_variables
317 *
318 * Set environment variables by enumerating the values of a key;
319 * helper for set_registry_environment().
320 * Note that Windows happily truncates the value if it's too big.
321 */
322 static void set_registry_variables( HANDLE hkey, ULONG type )
323 {
324 UNICODE_STRING env_name, env_value;
325 NTSTATUS status;
326 DWORD size;
327 int index;
328 char buffer[1024*sizeof(WCHAR) + sizeof(KEY_VALUE_FULL_INFORMATION)];
329 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
330
331 for (index = 0; ; index++)
332 {
333 status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
334 buffer, sizeof(buffer), &size );
335 if (status != STATUS_SUCCESS && status != STATUS_BUFFER_OVERFLOW)
336 break;
337 if (info->Type != type)
338 continue;
339 env_name.Buffer = info->Name;
340 env_name.Length = env_name.MaximumLength = info->NameLength;
341 env_value.Buffer = (WCHAR *)(buffer + info->DataOffset);
342 env_value.Length = env_value.MaximumLength = info->DataLength;
343 if (env_value.Length && !env_value.Buffer[env_value.Length/sizeof(WCHAR)-1])
344 env_value.Length -= sizeof(WCHAR); /* don't count terminating null if any */
345 if (info->Type == REG_EXPAND_SZ)
346 {
347 WCHAR buf_expanded[1024];
348 UNICODE_STRING env_expanded;
349 env_expanded.Length = env_expanded.MaximumLength = sizeof(buf_expanded);
350 env_expanded.Buffer=buf_expanded;
351 status = RtlExpandEnvironmentStrings_U(NULL, &env_value, &env_expanded, NULL);
352 if (status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW)
353 RtlSetEnvironmentVariable( NULL, &env_name, &env_expanded );
354 }
355 else
356 {
357 RtlSetEnvironmentVariable( NULL, &env_name, &env_value );
358 }
359 }
360 }
361
362
363 /***********************************************************************
364 * set_registry_environment
365 *
366 * Set the environment variables specified in the registry.
367 *
368 * Note: Windows handles REG_SZ and REG_EXPAND_SZ in one pass with the
369 * consequence that REG_EXPAND_SZ cannot be used reliably as it depends
370 * on the order in which the variables are processed. But on Windows it
371 * does not really matter since they only use %SystemDrive% and
372 * %SystemRoot% which are predefined. But Wine defines these in the
373 * registry, so we need two passes.
374 */
375 static BOOL set_registry_environment(void)
376 {
377 static const WCHAR env_keyW[] = {'M','a','c','h','i','n','e','\\',
378 'S','y','s','t','e','m','\\',
379 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
380 'C','o','n','t','r','o','l','\\',
381 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
382 'E','n','v','i','r','o','n','m','e','n','t',0};
383 static const WCHAR envW[] = {'E','n','v','i','r','o','n','m','e','n','t',0};
384
385 OBJECT_ATTRIBUTES attr;
386 UNICODE_STRING nameW;
387 HANDLE hkey;
388 BOOL ret = FALSE;
389
390 attr.Length = sizeof(attr);
391 attr.RootDirectory = 0;
392 attr.ObjectName = &nameW;
393 attr.Attributes = 0;
394 attr.SecurityDescriptor = NULL;
395 attr.SecurityQualityOfService = NULL;
396
397 /* first the system environment variables */
398 RtlInitUnicodeString( &nameW, env_keyW );
399 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) == STATUS_SUCCESS)
400 {
401 set_registry_variables( hkey, REG_SZ );
402 set_registry_variables( hkey, REG_EXPAND_SZ );
403 NtClose( hkey );
404 ret = TRUE;
405 }
406
407 /* then the ones for the current user */
408 if (RtlOpenCurrentUser( KEY_ALL_ACCESS, &attr.RootDirectory ) != STATUS_SUCCESS) return ret;
409 RtlInitUnicodeString( &nameW, envW );
410 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) == STATUS_SUCCESS)
411 {
412 set_registry_variables( hkey, REG_SZ );
413 set_registry_variables( hkey, REG_EXPAND_SZ );
414 NtClose( hkey );
415 }
416 NtClose( attr.RootDirectory );
417 return ret;
418 }
419
420
421 /***********************************************************************
422 * get_reg_value
423 */
424 static WCHAR *get_reg_value( HKEY hkey, const WCHAR *name )
425 {
426 char buffer[1024 * sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
427 KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
428 DWORD len, size = sizeof(buffer);
429 WCHAR *ret = NULL;
430 UNICODE_STRING nameW;
431
432 RtlInitUnicodeString( &nameW, name );
433 if (NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, buffer, size, &size ))
434 return NULL;
435
436 if (size <= FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data )) return NULL;
437 len = (size - FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data )) / sizeof(WCHAR);
438
439 if (info->Type == REG_EXPAND_SZ)
440 {
441 UNICODE_STRING value, expanded;
442
443 value.MaximumLength = len * sizeof(WCHAR);
444 value.Buffer = (WCHAR *)info->Data;
445 if (!value.Buffer[len - 1]) len--; /* don't count terminating null if any */
446 value.Length = len * sizeof(WCHAR);
447 expanded.Length = expanded.MaximumLength = 1024 * sizeof(WCHAR);
448 if (!(expanded.Buffer = HeapAlloc( GetProcessHeap(), 0, expanded.MaximumLength ))) return NULL;
449 if (!RtlExpandEnvironmentStrings_U( NULL, &value, &expanded, NULL )) ret = expanded.Buffer;
450 else RtlFreeUnicodeString( &expanded );
451 }
452 else if (info->Type == REG_SZ)
453 {
454 if ((ret = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
455 {
456 memcpy( ret, info->Data, len * sizeof(WCHAR) );
457 ret[len] = 0;
458 }
459 }
460 return ret;
461 }
462
463
464 /***********************************************************************
465 * set_additional_environment
466 *
467 * Set some additional environment variables not specified in the registry.
468 */
469 static void set_additional_environment(void)
470 {
471 static const WCHAR profile_keyW[] = {'M','a','c','h','i','n','e','\\',
472 'S','o','f','t','w','a','r','e','\\',
473 'M','i','c','r','o','s','o','f','t','\\',
474 'W','i','n','d','o','w','s',' ','N','T','\\',
475 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
476 'P','r','o','f','i','l','e','L','i','s','t',0};
477 static const WCHAR profiles_valueW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
478 static const WCHAR all_users_valueW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e','\0'};
479 static const WCHAR usernameW[] = {'U','S','E','R','N','A','M','E',0};
480 static const WCHAR userprofileW[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
481 static const WCHAR allusersW[] = {'A','L','L','U','S','E','R','S','P','R','O','F','I','L','E',0};
482 OBJECT_ATTRIBUTES attr;
483 UNICODE_STRING nameW;
484 WCHAR *user_name = NULL, *profile_dir = NULL, *all_users_dir = NULL;
485 HANDLE hkey;
486 const char *name = wine_get_user_name();
487 DWORD len;
488
489 /* set the USERNAME variable */
490
491 len = MultiByteToWideChar( CP_UNIXCP, 0, name, -1, NULL, 0 );
492 if (len)
493 {
494 user_name = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
495 MultiByteToWideChar( CP_UNIXCP, 0, name, -1, user_name, len );
496 SetEnvironmentVariableW( usernameW, user_name );
497 }
498
499 /* set the USERPROFILE and ALLUSERSPROFILE variables */
500
501 attr.Length = sizeof(attr);
502 attr.RootDirectory = 0;
503 attr.ObjectName = &nameW;
504 attr.Attributes = 0;
505 attr.SecurityDescriptor = NULL;
506 attr.SecurityQualityOfService = NULL;
507 RtlInitUnicodeString( &nameW, profile_keyW );
508 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
509 {
510 profile_dir = get_reg_value( hkey, profiles_valueW );
511 all_users_dir = get_reg_value( hkey, all_users_valueW );
512 NtClose( hkey );
513 }
514
515 if (profile_dir)
516 {
517 WCHAR *value, *p;
518
519 if (all_users_dir) len = max( len, strlenW(all_users_dir) + 1 );
520 len += strlenW(profile_dir) + 1;
521 value = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
522 strcpyW( value, profile_dir );
523 p = value + strlenW(value);
524 if (p > value && p[-1] != '\\') *p++ = '\\';
525 strcpyW( p, user_name );
526 SetEnvironmentVariableW( userprofileW, value );
527 if (all_users_dir)
528 {
529 strcpyW( p, all_users_dir );
530 SetEnvironmentVariableW( allusersW, value );
531 }
532 HeapFree( GetProcessHeap(), 0, value );
533 }
534
535 HeapFree( GetProcessHeap(), 0, all_users_dir );
536 HeapFree( GetProcessHeap(), 0, profile_dir );
537 HeapFree( GetProcessHeap(), 0, user_name );
538 }
539
540 /***********************************************************************
541 * set_library_wargv
542 *
543 * Set the Wine library Unicode argv global variables.
544 */
545 static void set_library_wargv( char **argv )
546 {
547 int argc;
548 char *q;
549 WCHAR *p;
550 WCHAR **wargv;
551 DWORD total = 0;
552
553 for (argc = 0; argv[argc]; argc++)
554 total += MultiByteToWideChar( CP_UNIXCP, 0, argv[argc], -1, NULL, 0 );
555
556 wargv = RtlAllocateHeap( GetProcessHeap(), 0,
557 total * sizeof(WCHAR) + (argc + 1) * sizeof(*wargv) );
558 p = (WCHAR *)(wargv + argc + 1);
559 for (argc = 0; argv[argc]; argc++)
560 {
561 DWORD reslen = MultiByteToWideChar( CP_UNIXCP, 0, argv[argc], -1, p, total );
562 wargv[argc] = p;
563 p += reslen;
564 total -= reslen;
565 }
566 wargv[argc] = NULL;
567
568 /* convert argv back from Unicode since it has to be in the Ansi codepage not the Unix one */
569
570 for (argc = 0; wargv[argc]; argc++)
571 total += WideCharToMultiByte( CP_ACP, 0, wargv[argc], -1, NULL, 0, NULL, NULL );
572
573 argv = RtlAllocateHeap( GetProcessHeap(), 0, total + (argc + 1) * sizeof(*argv) );
574 q = (char *)(argv + argc + 1);
575 for (argc = 0; wargv[argc]; argc++)
576 {
577 DWORD reslen = WideCharToMultiByte( CP_ACP, 0, wargv[argc], -1, q, total, NULL, NULL );
578 argv[argc] = q;
579 q += reslen;
580 total -= reslen;
581 }
582 argv[argc] = NULL;
583
584 __wine_main_argc = argc;
585 __wine_main_argv = argv;
586 __wine_main_wargv = wargv;
587 }
588
589
590 /***********************************************************************
591 * build_command_line
592 *
593 * Build the command line of a process from the argv array.
594 *
595 * Note that it does NOT necessarily include the file name.
596 * Sometimes we don't even have any command line options at all.
597 *
598 * We must quote and escape characters so that the argv array can be rebuilt
599 * from the command line:
600 * - spaces and tabs must be quoted
601 * 'a b' -> '"a b"'
602 * - quotes must be escaped
603 * '"' -> '\"'
604 * - if '\'s are followed by a '"', they must be doubled and followed by '\"',
605 * resulting in an odd number of '\' followed by a '"'
606 * '\"' -> '\\\"'
607 * '\\"' -> '\\\\\"'
608 * - '\'s that are not followed by a '"' can be left as is
609 * 'a\b' == 'a\b'
610 * 'a\\b' == 'a\\b'
611 */
612 static BOOL build_command_line( WCHAR **argv )
613 {
614 int len;
615 WCHAR **arg;
616 LPWSTR p;
617 RTL_USER_PROCESS_PARAMETERS* rupp = NtCurrentTeb()->Peb->ProcessParameters;
618
619 if (rupp->CommandLine.Buffer) return TRUE; /* already got it from the server */
620
621 len = 0;
622 for (arg = argv; *arg; arg++)
623 {
624 int has_space,bcount;
625 WCHAR* a;
626
627 has_space=0;
628 bcount=0;
629 a=*arg;
630 if( !*a ) has_space=1;
631 while (*a!='\0') {
632 if (*a=='\\') {
633 bcount++;
634 } else {
635 if (*a==' ' || *a=='\t') {
636 has_space=1;
637 } else if (*a=='"') {
638 /* doubling of '\' preceding a '"',
639 * plus escaping of said '"'
640 */
641 len+=2*bcount+1;
642 }
643 bcount=0;
644 }
645 a++;
646 }
647 len+=(a-*arg)+1 /* for the separating space */;
648 if (has_space)
649 len+=2; /* for the quotes */
650 }
651
652 if (!(rupp->CommandLine.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR))))
653 return FALSE;
654
655 p = rupp->CommandLine.Buffer;
656 rupp->CommandLine.Length = (len - 1) * sizeof(WCHAR);
657 rupp->CommandLine.MaximumLength = len * sizeof(WCHAR);
658 for (arg = argv; *arg; arg++)
659 {
660 int has_space,has_quote;
661 WCHAR* a;
662
663 /* Check for quotes and spaces in this argument */
664 has_space=has_quote=0;
665 a=*arg;
666 if( !*a ) has_space=1;
667 while (*a!='\0') {
668 if (*a==' ' || *a=='\t') {
669 has_space=1;
670 if (has_quote)
671 break;
672 } else if (*a=='"') {
673 has_quote=1;
674 if (has_space)
675 break;
676 }
677 a++;
678 }
679
680 /* Now transfer it to the command line */
681 if (has_space)
682 *p++='"';
683 if (has_quote) {
684 int bcount;
685 WCHAR* a;
686
687 bcount=0;
688 a=*arg;
689 while (*a!='\0') {
690 if (*a=='\\') {
691 *p++=*a;
692 bcount++;
693 } else {
694 if (*a=='"') {
695 int i;
696
697 /* Double all the '\\' preceding this '"', plus one */
698 for (i=0;i<=bcount;i++)
699 *p++='\\';
700 *p++='"';
701 } else {
702 *p++=*a;
703 }
704 bcount=0;
705 }
706 a++;
707 }
708 } else {
709 WCHAR* x = *arg;
710 while ((*p=*x++)) p++;
711 }
712 if (has_space)
713 *p++='"';
714 *p++=' ';
715 }
716 if (p > rupp->CommandLine.Buffer)
717 p--; /* remove last space */
718 *p = '\0';
719
720 return TRUE;
721 }
722
723
724 /***********************************************************************
725 * init_current_directory
726 *
727 * Initialize the current directory from the Unix cwd or the parent info.
728 */
729 static void init_current_directory( CURDIR *cur_dir )
730 {
731 UNICODE_STRING dir_str;
732 char *cwd;
733 int size;
734
735 /* if we received a cur dir from the parent, try this first */
736
737 if (cur_dir->DosPath.Length)
738 {
739 if (RtlSetCurrentDirectory_U( &cur_dir->DosPath ) == STATUS_SUCCESS) goto done;
740 }
741
742 /* now try to get it from the Unix cwd */
743
744 for (size = 256; ; size *= 2)
745 {
746 if (!(cwd = HeapAlloc( GetProcessHeap(), 0, size ))) break;
747 if (getcwd( cwd, size )) break;
748 HeapFree( GetProcessHeap(), 0, cwd );
749 if (errno == ERANGE) continue;
750 cwd = NULL;
751 break;
752 }
753
754 if (cwd)
755 {
756 WCHAR *dirW;
757 int lenW = MultiByteToWideChar( CP_UNIXCP, 0, cwd, -1, NULL, 0 );
758 if ((dirW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
759 {
760 MultiByteToWideChar( CP_UNIXCP, 0, cwd, -1, dirW, lenW );
761 RtlInitUnicodeString( &dir_str, dirW );
762 RtlSetCurrentDirectory_U( &dir_str );
763 RtlFreeUnicodeString( &dir_str );
764 }
765 }
766
767 if (!cur_dir->DosPath.Length) /* still not initialized */
768 {
769 MESSAGE("Warning: could not find DOS drive for current working directory '%s', "
770 "starting in the Windows directory.\n", cwd ? cwd : "" );
771 RtlInitUnicodeString( &dir_str, DIR_Windows );
772 RtlSetCurrentDirectory_U( &dir_str );
773 }
774 HeapFree( GetProcessHeap(), 0, cwd );
775
776 done:
777 if (!cur_dir->Handle) chdir("/"); /* change to root directory so as not to lock cdroms */
778 TRACE( "starting in %s %p\n", debugstr_w( cur_dir->DosPath.Buffer ), cur_dir->Handle );
779 }
780
781
782 /***********************************************************************
783 * init_windows_dirs
784 *
785 * Initialize the windows and system directories from the environment.
786 */
787 static void init_windows_dirs(void)
788 {
789 extern void __wine_init_windows_dir( const WCHAR *windir, const WCHAR *sysdir );
790
791 static const WCHAR windirW[] = {'w','i','n','d','i','r',0};
792 static const WCHAR winsysdirW[] = {'w','i','n','s','y','s','d','i','r',0};
793 static const WCHAR default_windirW[] = {'C',':','\\','w','i','n','d','o','w','s',0};
794 static const WCHAR default_sysdirW[] = {'\\','s','y','s','t','e','m','3','2',0};
795
796 DWORD len;
797 WCHAR *buffer;
798
799 if ((len = GetEnvironmentVariableW( windirW, NULL, 0 )))
800 {
801 buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
802 GetEnvironmentVariableW( windirW, buffer, len );
803 DIR_Windows = buffer;
804 }
805 else DIR_Windows = default_windirW;
806
807 if ((len = GetEnvironmentVariableW( winsysdirW, NULL, 0 )))
808 {
809 buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
810 GetEnvironmentVariableW( winsysdirW, buffer, len );
811 DIR_System = buffer;
812 }
813 else
814 {
815 len = strlenW( DIR_Windows );
816 buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) + sizeof(default_sysdirW) );
817 memcpy( buffer, DIR_Windows, len * sizeof(WCHAR) );
818 memcpy( buffer + len, default_sysdirW, sizeof(default_sysdirW) );
819 DIR_System = buffer;
820 }
821
822 if (!CreateDirectoryW( DIR_Windows, NULL ) && GetLastError() != ERROR_ALREADY_EXISTS)
823 ERR( "directory %s could not be created, error %u\n",
824 debugstr_w(DIR_Windows), GetLastError() );
825 if (!CreateDirectoryW( DIR_System, NULL ) && GetLastError() != ERROR_ALREADY_EXISTS)
826 ERR( "directory %s could not be created, error %u\n",
827 debugstr_w(DIR_System), GetLastError() );
828
829 TRACE_(file)( "WindowsDir = %s\n", debugstr_w(DIR_Windows) );
830 TRACE_(file)( "SystemDir = %s\n", debugstr_w(DIR_System) );
831
832 /* set the directories in ntdll too */
833 __wine_init_windows_dir( DIR_Windows, DIR_System );
834 }
835
836
837 /***********************************************************************
838 * start_wineboot
839 *
840 * Start the wineboot process if necessary. Return the event to wait on.
841 */
842 static HANDLE start_wineboot(void)
843 {
844 static const WCHAR wineboot_eventW[] = {'_','_','w','i','n','e','b','o','o','t','_','e','v','e','n','t',0};
845 HANDLE event;
846
847 if (!(event = CreateEventW( NULL, TRUE, FALSE, wineboot_eventW )))
848 {
849 ERR( "failed to create wineboot event, expect trouble\n" );
850 return 0;
851 }
852 if (GetLastError() != ERROR_ALREADY_EXISTS) /* we created it */
853 {
854 static const WCHAR command_line[] = {'\\','w','i','n','e','b','o','o','t','.','e','x','e',' ','-','-','i','n','i','t',0};
855 STARTUPINFOW si;
856 PROCESS_INFORMATION pi;
857 WCHAR cmdline[MAX_PATH + sizeof(command_line)/sizeof(WCHAR)];
858
859 memset( &si, 0, sizeof(si) );
860 si.cb = sizeof(si);
861 si.dwFlags = STARTF_USESTDHANDLES;
862 si.hStdInput = 0;
863 si.hStdOutput = 0;
864 si.hStdError = GetStdHandle( STD_ERROR_HANDLE );
865
866 GetSystemDirectoryW( cmdline, MAX_PATH );
867 lstrcatW( cmdline, command_line );
868 if (CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi ))
869 {
870 TRACE( "started wineboot pid %04x tid %04x\n", pi.dwProcessId, pi.dwThreadId );
871 CloseHandle( pi.hThread );
872 CloseHandle( pi.hProcess );
873
874 }
875 else ERR( "failed to start wineboot, err %u\n", GetLastError() );
876 }
877 return event;
878 }
879
880
881 /***********************************************************************
882 * start_process
883 *
884 * Startup routine of a new process. Runs on the new process stack.
885 */
886 static void start_process( void *arg )
887 {
888 __TRY
889 {
890 PEB *peb = NtCurrentTeb()->Peb;
891 IMAGE_NT_HEADERS *nt;
892 LPTHREAD_START_ROUTINE entry;
893