1/*$Header: /p/tcsh/cvsroot/tcsh/win32/fork.c,v 1.11 2008/08/31 14:09:01 amold Exp $*/
2/*-
3 * Copyright (c) 1980, 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31/*
32 * The fork() here is based on the ideas used by cygwin
33 * -amol
34 *
35 */
36
37/*
38 * _M_ALPHA changes by Mark Tucker
39 */
40
41#define WIN32_LEAN_AND_MEAN
42#include <windows.h>
43#include <fcntl.h>
44#include <io.h>
45#include <stdlib.h>
46#include <setjmp.h>
47#include <ntport.h>
48#include "forkdata.h"
49#include "sh.h"
50
51#pragma intrinsic("memcpy", "memset","memcmp")
52#pragma warning(push,3) // forget about W4 here
53
54typedef unsigned long u_long;
55typedef void *ptr_t;
56typedef unsigned char U_char;
57typedef unsigned int U_int;
58typedef unsigned short U_short;
59typedef unsigned long U_long;
60
61
62static void stack_probe(void *ptr) ;
63/*static void heap_init(void);*/
64BOOL CreateWow64Events(DWORD , HANDLE *, HANDLE *, BOOL);
65
66//
67// This is exported from the user program.
68// It must return 0 for no error !!!!
69extern int fork_copy_user_mem(HANDLE );
70
71/*
72 * Apparently , visual c++ on the alpha does not place the
73 * fork data contiguously. To work around that, Mark created
74 * this structure (see forkdata.h)
75 * -amol
76 */
77ForkData gForkData = {0,0,0,0,0,{0},0,0,0};
78
79
80#ifdef _M_IX86
81
82u_long _old_exr = 0; // Saved exception registration for longjmp
83
84#endif // _M_ALPHA
85/*
86 * This hack is an attempt at getting to the exception registration
87 * in an architecture-independent way. It's critical for longjmp in a
88 * code using __try/__except blocks. Microsoft Visual C++ does a global
89 * unwind during a longjmp, and that can cause havoc if the exception
90 * registration stored in longjmp is lower(address wise, indicating a jump
91 * from below of the stack upward.) in the stack than the current
92 * registration (returned by NtCurrentTeb).
93 *
94 * This works with VC++, because that's all I have. With other compilers,
95 * there might be minimal changes required, depending on where the
96 * exception registration record is stored in the longjmp structure.
97 *
98 * -amol 2/6/97
99 */
100
101NT_TIB * (* myNtCurrentTeb)(void);
102
103#define GETEXCEPTIONREGIST() (((NT_TIB*)get_teb())->ExceptionList)
104#define GETSTACKBASE()		 (((NT_TIB*)get_teb())->StackBase)
105
106
107
108static NT_TIB *the_tib;
109
110#if !defined(_M_IA64) && !defined(_M_AMD64)
111void *get_teb(void) {
112
113
114	if (the_tib)
115		return the_tib;
116
117	myNtCurrentTeb = (void*)GetProcAddress(LoadLibrary("ntdll.dll"),
118							"NtCurrentTeb");
119	if (!myNtCurrentTeb)
120		return NULL;
121	the_tib = myNtCurrentTeb();
122
123	if (the_tib == NULL)
124		abort();
125	return the_tib;
126}
127#else
128#define get_teb NtCurrentTeb
129#endif _M_IA64
130
131void set_stackbase(void*ptr){
132	GETSTACKBASE() = ptr;
133}
134/*
135 * This must be called by the application as the first thing it does.
136 * -amol 2/6/97
137 *
138 * Well, maybe not the FIRST..
139 * -amol 11/10/97
140 */
141
142extern BOOL bIsWow64Process;
143
144int fork_init(void) {
145
146
147	//heap_init(); Now called as the very first thing in silly_entry().
148
149	if (__forked) {
150
151
152		// stack_probe probes out a decent-sized stack for the child,
153		// since initially it has a very small stack (1 page).
154		//
155
156		/* not needed since default commit is set to 0.5MB in
157		 * makefile.win32
158		 *
159		 * stack_probe((char *)__fork_stack_end - 64);
160		 */
161
162		//
163		// Save the old Exception registration record and jump
164		// off the cliff.
165		//
166#ifdef  _M_IX86
167		_old_exr = __fork_context[6];
168		__fork_context[6] =(int)GETEXCEPTIONREGIST();//tmp;
169#endif  _M_ALPHA
170		//
171		// Whee !
172		longjmp(__fork_context,1);
173	}
174
175	return 0;
176}
177int fork(void) {
178
179	size_t rc;
180	size_t stacksize;
181	char modname[512];/*FIXBUF*/
182	HANDLE  hProc,hThread, hArray[2];
183	STARTUPINFO si;
184	PROCESS_INFORMATION pi;
185	SECURITY_ATTRIBUTES sa;
186	DWORD dwCreationflags;
187	unsigned int priority;
188	HANDLE h64Parent,h64Child;
189
190#ifndef _M_ALPHA
191	unsigned long fork_stack_end;
192#endif _M_ALPHA
193
194	__fork_stack_begin =GETSTACKBASE();
195
196#ifndef _M_ALPHA
197	__fork_stack_end = &fork_stack_end;
198#else
199	__fork_stack_end = (unsigned long *)__asm("mov $sp, $0");
200#endif /*_M_ALPHA*/
201
202	h64Parent = h64Child = NULL;
203	//
204	// Create two inheritable events
205	//
206	sa.nLength = sizeof(sa);
207	sa.lpSecurityDescriptor =0;
208	sa.bInheritHandle = TRUE;
209	if (!__hforkchild)
210		__hforkchild = CreateEvent(&sa,TRUE,FALSE,NULL);
211	if (!__hforkparent)
212		__hforkparent = CreateEvent(&sa,TRUE,FALSE,NULL);
213
214	rc = setjmp(__fork_context);
215
216	if (rc) { // child
217#ifdef  _M_IX86
218		//
219		// Restore old registration
220		// -amol 2/2/97
221		GETEXCEPTIONREGIST() = (struct _EXCEPTION_REGISTRATION_RECORD*)_old_exr;
222#endif // _M_ALPHA
223		SetEvent(__hforkchild);
224
225		dprintf("Child ready to rumble\n");
226		if(WaitForSingleObject(__hforkparent,FORK_TIMEOUT) != WAIT_OBJECT_0)
227			ExitProcess(0xFFFF);
228
229		CloseHandle(__hforkchild);
230		CloseHandle(__hforkparent);
231		__hforkchild = __hforkparent=0;
232
233		//__asm { int 3};
234		restore_fds();
235
236		STR_environ = blk2short(environ);
237		environ = short2blk(STR_environ);	/* So that we can free it */
238
239		return 0;
240	}
241	copy_fds();
242	memset(&si,0,sizeof(si));
243	si.cb= sizeof(si);
244
245	/*
246	 * This f!@#!@% function returns the old value even if the std handles
247	 * have been closed.
248	 * Skip this step, since we know tcsh will do the right thing later.
249	 *
250	 si.hStdInput= GetStdHandle(STD_INPUT_HANDLE);
251	 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
252	 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
253	 */
254
255	if (!GetModuleFileName(GetModuleHandle(NULL),modname,512) ) {
256		rc = GetLastError();
257		return -1;
258	}
259	dwCreationflags = GetPriorityClass(GetCurrentProcess());
260	priority = GetThreadPriority(GetCurrentThread());
261	rc = CreateProcess(NULL,
262			modname,
263			NULL,
264			NULL,
265			TRUE,
266			CREATE_SUSPENDED | dwCreationflags,
267			NULL,
268			NULL,
269			&si,
270			&pi);
271	if (!rc)  {
272		rc = GetLastError();
273		return -1;
274	}
275
276	ResetEvent(__hforkchild);
277	ResetEvent(__hforkparent);
278
279	hProc = pi.hProcess;
280	hThread = pi.hThread;
281
282
283	__forked=1;
284	/*
285	 * Usage of events in the wow64 case:
286	 *
287	 * h64Parent : initially non-signalled
288	 * h64Child  : initially non-signalled
289	 *
290	 *    1. Create the events, resume the child thread.
291	 *    2. Child opens h64Parent to see if it is a child process in wow64
292	 *    3. Child opens and sets h64Child to tell parent it's running. (This
293	 *       step is needed because we can't copy to a process created in the
294	 *       suspended state on wow64.)
295	 *    4. Copy gForkData and then set h64Parent. This tells the child
296	 *       that the parameters in the structure are trustworthy.
297	 *    5. Wait for h64Child so that we know the child has created the stack
298	 *       in dynamic memory.
299	 *
300	 *   The rest of the fork hack should now proceed as in x86
301	 *
302	 */
303	if (bIsWow64Process) {
304
305		// allocate the heap for the child. this can be done even when
306		// the child is suspended.
307		// avoids inexplicable allocation failures in the child.
308		if (VirtualAllocEx(hProc,
309					__heap_base,
310					__heap_size,
311					MEM_RESERVE,
312					PAGE_READWRITE) == NULL) {
313			dprintf("virtual allocex failed %d\n",GetLastError());
314			goto error;
315		}
316		if (VirtualAllocEx(hProc,
317					__heap_base,
318					__heap_size,
319					MEM_COMMIT,
320					PAGE_READWRITE) == NULL) {
321			dprintf("virtual allocex2 failed %d\n",GetLastError());
322			goto error;
323		}
324
325		// Do NOT expect existing events
326		if (!CreateWow64Events(pi.dwProcessId,&h64Parent,&h64Child,FALSE)) {
327			goto error;
328		}
329		ResumeThread(hThread);
330
331		// wait for the child to tell us it is running
332		//if (WaitForSingleObject(h64Child,FORK_TIMEOUT) != WAIT_OBJECT_0) {
333		//	rc = GetLastError();
334		//	goto error;
335		//}
336		hArray[0] = h64Child;
337		hArray[1] = hProc;
338
339		if (WaitForMultipleObjects(2,hArray,FALSE,FORK_TIMEOUT) !=
340				WAIT_OBJECT_0){
341
342			rc = GetLastError();
343			goto error;
344		}
345
346	}
347	//
348	// Copy all the shared data
349	//
350	if (!WriteProcessMemory(hProc,&gForkData,&gForkData,
351				sizeof(ForkData),&rc)) {
352		goto error;
353	}
354	if (rc != sizeof(ForkData))
355		goto error;
356
357	if (!bIsWow64Process) {
358		rc = ResumeThread(hThread);
359	}
360	// in the wow64 case, the child will be waiting  on h64parent again.
361	// set it, and then wait for h64child. This will mean the child has
362	// a stack set up at the right location.
363	else {
364		SetEvent(h64Parent);
365		hArray[0] = h64Child;
366		hArray[1] = hProc;
367
368		if (WaitForMultipleObjects(2,hArray,FALSE,FORK_TIMEOUT) !=
369				WAIT_OBJECT_0){
370
371			rc = GetLastError();
372			goto error;
373		}
374		CloseHandle(h64Parent);
375		CloseHandle(h64Child);
376		h64Parent = h64Child = NULL;
377	}
378
379	//
380	// Wait for the child to start and init itself.
381	// The timeout is so that we don't wait too long
382	//
383	hArray[0] = __hforkchild;
384	hArray[1] = hProc;
385
386	if (WaitForMultipleObjects(2,hArray,FALSE,FORK_TIMEOUT) != WAIT_OBJECT_0){
387
388		int err = GetLastError(); // For debugging purposes
389		dprintf("wait failed err %d\n",err);
390		goto error;
391	}
392
393	// Stop the child again and copy the stack and heap
394	//
395	SuspendThread(hThread);
396
397	if (!SetThreadPriority(hThread,priority) ) {
398		priority =GetLastError();
399	}
400
401	// stack
402	stacksize = (char*)__fork_stack_begin - (char*)__fork_stack_end;
403	if (!WriteProcessMemory(hProc,(char *)__fork_stack_end,
404				(char *)__fork_stack_end,
405				(u_long)stacksize,
406				&rc)){
407		goto error;
408	}
409	//
410	// copy heap itself
411	if (!WriteProcessMemory(hProc, (void*)__heap_base,(void*)__heap_base,
412				(DWORD)((char*)__heap_top-(char*)__heap_base),
413				&rc)){
414		goto error;
415	}
416
417	rc = fork_copy_user_mem(hProc);
418
419	if(rc) {
420		goto error;
421	}
422
423	// Release the child.
424	SetEvent(__hforkparent);
425	rc = ResumeThread(hThread);
426
427	__forked=0;
428	dprintf("forked process %d\n",pi.dwProcessId);
429	start_sigchild_thread(hProc,pi.dwProcessId);
430	close_copied_fds();
431
432	CloseHandle(hThread);
433	//
434	// return process id to parent.
435	return pi.dwProcessId;
436
437error:
438	__forked=0;
439	SetEvent(__hforkparent);
440	ResumeThread(hThread);
441	CloseHandle(hProc);
442	CloseHandle(hThread);
443	if (h64Parent) {
444		SetEvent(h64Parent); // don't let child block forever
445		CloseHandle(h64Parent);
446	}
447	if (h64Child)
448		CloseHandle(h64Child);
449	return -1;
450}
451#pragma optimize("",off)
452// The damn optimizer will remove the recursion, resulting in an infinite
453// loop. -amol 4/17/97
454void stack_probe (void *ptr) {
455	char buf[1000];
456	int x;
457
458	if (&x > (int *)ptr)
459		stack_probe(ptr);
460	(void)buf;
461}
462#pragma optimize("",on)
463//
464// This function basically reserves some heap space.
465// In the child it also commits the size committed in the parent.
466void heap_init(void) {
467
468	char * temp;
469	int err;
470	if (__forked) {
471		temp = (char *)VirtualAlloc((void*)__heap_base,__heap_size, MEM_RESERVE,
472				PAGE_READWRITE);
473		if (temp != (char*)__heap_base) {
474			if (!temp){
475				err = GetLastError();
476				if (bIsWow64Process)
477					ExitProcess(0);
478				abort();
479			}
480			else
481				__heap_base = temp;
482		}
483		if (!VirtualAlloc(__heap_base,(char*)__heap_top -(char*)__heap_base,
484					MEM_COMMIT,PAGE_READWRITE)){
485			err = GetLastError();
486			if (bIsWow64Process)
487				ExitProcess(0);
488			abort();
489		}
490		temp = (char*)__heap_base;
491	}
492	else {
493		SYSTEM_INFO sysinfo;
494		GetSystemInfo(&sysinfo);
495		__heap_size = sysinfo.dwPageSize * 1024;
496		__heap_base = VirtualAlloc(0 , __heap_size,MEM_RESERVE|MEM_TOP_DOWN,
497				PAGE_READWRITE);
498
499		if (__heap_base == 0) {
500			abort();
501		}
502
503		__heap_top = __heap_base;
504	}
505
506}
507//
508// Implementation of sbrk() for the fmalloc family
509//
510void * sbrk(int delta) {
511
512	void *retval;
513	void *old_top=__heap_top;
514	char *b = (char*)__heap_top;
515
516	if (delta == 0)
517		return  __heap_top;
518	if (delta > 0) {
519
520		retval =VirtualAlloc((void*)__heap_top,delta,MEM_COMMIT,PAGE_READWRITE);
521
522		if (retval == 0 )
523			abort();
524
525		b += delta;
526		__heap_top = (void*)b;
527	}
528	else {
529		retval = VirtualAlloc((void*)((char*)__heap_top - delta),
530				delta,MEM_DECOMMIT, PAGE_READWRITE);
531
532		if (retval == 0)
533			abort();
534
535		b -= delta;
536		__heap_top = (void*)b;
537	}
538
539	return (void*) old_top;
540}
541/*
542 * Semantics of CreateWow64Events
543 *
544 * Try to open the events even if bOpenExisting is FALSE. This will help
545 * us detect name duplication.
546 *
547 *       1. If OpenEvent succeeds,and bOpenExisting is FALSE,  fail.
548 *
549 *       2. If OpenEvent failed,and bOpenExisting is TRUE fail
550 *
551 *       3. else create the events anew
552 *
553 */
554#define TCSH_WOW64_PARENT_EVENT_NAME "tcsh-wow64-parent-event"
555#define TCSH_WOW64_CHILD_EVENT_NAME  "tcsh-wow64-child-event"
556BOOL CreateWow64Events(DWORD pid, HANDLE *hParent, HANDLE *hChild,
557		BOOL bOpenExisting) {
558
559	SECURITY_ATTRIBUTES sa;
560	char parentname[256],childname[256];
561
562	*hParent = *hChild = NULL;
563
564	// make darn sure they're not inherited
565	sa.nLength = sizeof(sa);
566	sa.lpSecurityDescriptor =0;
567	sa.bInheritHandle = FALSE;
568	//
569
570#pragma warning(disable:4995)
571
572	// This event tells the child to hold for gForkData to be copied
573	wsprintfA(parentname, "Local\\%d-%s",pid, TCSH_WOW64_PARENT_EVENT_NAME);
574
575	wsprintfA(childname, "Local\\%d-%s",pid, TCSH_WOW64_CHILD_EVENT_NAME );
576
577#pragma warning(default:4995)
578
579	*hParent = OpenEvent(EVENT_ALL_ACCESS,FALSE, parentname);
580
581	if(*hParent) {
582		if (bOpenExisting == FALSE) { // didn't expect to be a child process
583			CloseHandle(*hParent);
584			*hParent = NULL;
585			return FALSE;
586		}
587
588		*hChild = OpenEvent(EVENT_ALL_ACCESS,FALSE, childname);
589		if (!*hChild) {
590			CloseHandle(*hParent);
591			*hParent = NULL;
592			return FALSE;
593		}
594
595		return TRUE;
596	}
597	else { //event does not exist
598		if (bOpenExisting == TRUE)
599			return FALSE;
600	}
601
602	*hParent = CreateEvent(&sa,FALSE,FALSE,parentname);
603	if (!*hParent)
604		return FALSE;
605
606
607	*hChild = CreateEvent(&sa,FALSE,FALSE,childname);
608	if (!*hChild){
609		CloseHandle(*hParent);
610		*hParent = NULL;
611		return FALSE;
612	}
613	return TRUE;
614}
615