Logo Search packages:      
Sourcecode: tcsh version File versions  Download package

fork.c

/*$Header: /src/pub/tcsh/win32/fork.c,v 1.6 2004/05/19 18:22:27 christos Exp $*/
/*-
 * Copyright (c) 1980, 1991 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
 
/* 
 * The fork() here is based on the ideas used by cygwin
 * -amol
 *
 */

/*
 * _M_ALPHA changes by Mark Tucker
 */

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#include <stdlib.h>
#include <setjmp.h>
#include <ntport.h>
#include "forkdata.h"
#include "sh.h"

#pragma intrinsic("memcpy", "memset","memcmp")

typedef unsigned long u_long;
typedef void *ptr_t;
typedef unsigned char U_char; 
typedef unsigned int U_int;
typedef unsigned short U_short;
typedef unsigned long U_long;


static void stack_probe(void *ptr) ;
/*static void heap_init(void);*/
BOOL CreateWow64Events(DWORD , HANDLE *, HANDLE *, BOOL);

//
// This is exported from the user program.
// It must return 0 for no error !!!!
extern int fork_copy_user_mem(HANDLE );

/* 
 * Apparently , visual c++ on the alpha does not place the
 * fork data contiguously. To work around that, Mark created
 * this structure (see forkdata.h)
 * -amol
 */
ForkData gForkData = {0,0,0,0,0,{0},0,0,0};


#ifdef _M_IX86

u_long _old_exr = 0; // Saved exception registration for longjmp

#endif // _M_IX86
/*
 * This hack is an attempt at getting to the exception registration
 * in an architecture-independent way. It's critical for longjmp in a
 * code using __try/__except blocks. Microsoft Visual C++ does a global
 * unwind during a longjmp, and that can cause havoc if the exception 
 * registration stored in longjmp is lower(address wise, indicating a jump
 * from below of the stack upward.) in the stack than the current
 * registration (returned by NtCurrentTeb).
 *
 * This works with VC++, because that's all I have. With other compilers, 
 * there might be minimal changes required, depending on where the 
 * exception registration record is stored in the longjmp structure.
 *
 * -amol 2/6/97
 */

NT_TIB * (* myNtCurrentTeb)(void);

#define GETEXCEPTIONREGIST() (((NT_TIB*)get_teb())->ExceptionList)
#define GETSTACKBASE()         (((NT_TIB*)get_teb())->StackBase)



static NT_TIB *the_tib;

#ifndef _M_IA64
void *get_teb(void) {


      if (the_tib)
            return the_tib;

      myNtCurrentTeb = (void*)GetProcAddress(LoadLibrary("ntdll.dll"),
                              "NtCurrentTeb");
      if (!myNtCurrentTeb)
            return NULL;
      the_tib = myNtCurrentTeb();

      if (the_tib == NULL)
            abort();
      return the_tib;
}
#else
#define get_teb NtCurrentTeb
#endif _M_IA64

void set_stackbase(void*ptr){
      GETSTACKBASE() = ptr;
}
/* 
 * This must be called by the application as the first thing it does.
 * -amol 2/6/97
 *
 * Well, maybe not the FIRST..
 * -amol 11/10/97
 */

extern BOOL bIsWow64Process;

int fork_init(void) {


      //heap_init(); Now called as the very first thing in silly_entry().

      if (__forked) {


            // stack_probe probes out a decent-sized stack for the child,
            // since initially it has a very small stack (1 page).
            //

            /* not needed since default commit is set to 0.5MB in 
             * makefile.win32
             *
             * stack_probe((char *)__fork_stack_end - 64);
             */

            //
            // Save the old Exception registration record and jump
            // off the cliff.
            //
#ifdef  _M_IX86
            _old_exr = __fork_context[6];
            __fork_context[6] =(int)GETEXCEPTIONREGIST();//tmp;
#endif  _M_IX86
            //
            // Whee !
            longjmp(__fork_context,1);
      }

      return 0;
}
int fork(void) {

      int rc;
      size_t stacksize;
      char modname[512];
      HANDLE  hProc,hThread, hArray[2];
      STARTUPINFO si;
      PROCESS_INFORMATION pi;
      SECURITY_ATTRIBUTES sa;
      DWORD dwCreationflags;
      unsigned int priority;
      HANDLE h64Parent,h64Child;

#ifndef _M_ALPHA
      unsigned long fork_stack_end;
#endif _M_ALPHA

      __fork_stack_begin =GETSTACKBASE();

#ifndef _M_ALPHA
      __fork_stack_end = &fork_stack_end;
#else
      __fork_stack_end = (unsigned long *)__asm("mov $sp, $0");
#endif /*_M_ALPHA*/

      h64Parent = h64Child = NULL;
      //
      // Create two inheritable events
      //
      sa.nLength = sizeof(sa);
      sa.lpSecurityDescriptor =0;
      sa.bInheritHandle = TRUE;
      if (!__hforkchild)
            __hforkchild = CreateEvent(&sa,TRUE,FALSE,NULL);
      if (!__hforkparent)
            __hforkparent = CreateEvent(&sa,TRUE,FALSE,NULL);

      rc = setjmp(__fork_context);

      if (rc) { // child
#ifdef  _M_IX86
            //
            // Restore old registration
            // -amol 2/2/97
            GETEXCEPTIONREGIST() = (struct _EXCEPTION_REGISTRATION_RECORD*)_old_exr;
#endif // _M_IX86
            SetEvent(__hforkchild);

            dprintf("Child ready to rumble\n");
            if(WaitForSingleObject(__hforkparent,FORK_TIMEOUT) != WAIT_OBJECT_0)
                  ExitProcess(0xFFFF);

            CloseHandle(__hforkchild);
            CloseHandle(__hforkparent);
            __hforkchild = __hforkparent=0;

            //__asm { int 3};
            restore_fds();

            STR_environ = blk2short(environ);
            environ = short2blk(STR_environ);   /* So that we can free it */

            return 0;
      }
      copy_fds();
      memset(&si,0,sizeof(si));
      si.cb= sizeof(si);

      /*
       * This f!@#!@% function returns the old value even if the std handles
       * have been closed.
       * Skip this step, since we know tcsh will do the right thing later.
       * 
      si.hStdInput= GetStdHandle(STD_INPUT_HANDLE);
      si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
      si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
      */

      if (!GetModuleFileName(GetModuleHandle(NULL),modname,512) ) {
            rc = GetLastError();
            return -1;
      }
      dwCreationflags = GetPriorityClass(GetCurrentProcess());
      priority = GetThreadPriority(GetCurrentThread());
      rc = CreateProcess(NULL,
                                    modname,
                                    NULL,
                                    NULL,
                                    TRUE,
                                    CREATE_SUSPENDED | dwCreationflags,
                                    NULL,
                                    NULL,
                                    &si,
                                    &pi);
      if (!rc)  {
            rc = GetLastError();
            return -1;
      }
      
      ResetEvent(__hforkchild);
      ResetEvent(__hforkparent);

      hProc = pi.hProcess;
      hThread = pi.hThread;


      __forked=1;
      /*
       * Usage of events in the wow64 case:
       *
       * h64Parent : initially non-signalled
       * h64Child  : initially non-signalled
       *
       *    1. Create the events, resume the child thread.
       *    2. Child opens h64Parent to see if it is a child process in wow64
       *    3. Child opens and sets h64Child to tell parent it's running. (This
       *       step is needed because we can't copy to a process created in the
       *       suspended state on wow64.)
       *    4. Copy gForkData and then set h64Parent. This tells the child
       *       that the parameters in the structure are trustworthy.
       *    5. Wait for h64Child so that we know the child has created the stack
       *       in dynamic memory.
       *
       *   The rest of the fork hack should now proceed as in x86
       *
       */
      if (bIsWow64Process) {

            // allocate the heap for the child. this can be done even when
            // the child is suspended. 
            // avoids inexplicable allocation failures in the child.
            if (VirtualAllocEx(hProc,
                                          __heap_base,
                                          __heap_size,
                                          MEM_RESERVE,
                                          PAGE_READWRITE) == NULL) {
                  dprintf("virtual allocex failed %d\n",GetLastError());
                  goto error;
            }
            if (VirtualAllocEx(hProc,
                                          __heap_base,
                                          __heap_size,
                                          MEM_COMMIT,
                                          PAGE_READWRITE) == NULL) {
                  dprintf("virtual allocex2 failed %d\n",GetLastError());
                  goto error;
            }

            // Do NOT expect existing events
            if (!CreateWow64Events(pi.dwProcessId,&h64Parent,&h64Child,FALSE)) {
                  goto error;
            }
            ResumeThread(hThread);

            // wait for the child to tell us it is running
            //if (WaitForSingleObject(h64Child,FORK_TIMEOUT) != WAIT_OBJECT_0) {
            //    rc = GetLastError();
            //    goto error;
            //}
            hArray[0] = h64Child;
            hArray[1] = hProc;

            if (WaitForMultipleObjects(2,hArray,FALSE,FORK_TIMEOUT) != 
                                    WAIT_OBJECT_0){

                  rc = GetLastError();
                  goto error;
            }

      }
      //
      // Copy all the shared data
      //
      if (!WriteProcessMemory(hProc,&gForkData,&gForkData,
                  sizeof(ForkData),&rc)) {
            goto error;
      }
      if (rc != sizeof(ForkData)) 
            goto error;

      if (!bIsWow64Process) {
            rc = ResumeThread(hThread);
      }
      // in the wow64 case, the child will be waiting  on h64parent again.
      // set it, and then wait for h64child. This will mean the child has
      // a stack set up at the right location.
      else {
            SetEvent(h64Parent);
            hArray[0] = h64Child;
            hArray[1] = hProc;

            if (WaitForMultipleObjects(2,hArray,FALSE,FORK_TIMEOUT) != 
                                    WAIT_OBJECT_0){

                  rc = GetLastError();
                  goto error;
            }
            CloseHandle(h64Parent);
            CloseHandle(h64Child);
            h64Parent = h64Child = NULL;
      }
      
      //
      // Wait for the child to start and init itself.
      // The timeout is so that we don't wait too long
      //
      hArray[0] = __hforkchild;
      hArray[1] = hProc;

      if (WaitForMultipleObjects(2,hArray,FALSE,FORK_TIMEOUT) != WAIT_OBJECT_0){

            int err = GetLastError(); // For debugging purposes
            dprintf("wait failed err %d\n",err);
            goto error;
      }

      // Stop the child again and copy the stack and heap
      //
      SuspendThread(hThread);

      if (!SetThreadPriority(hThread,priority) ) {
            priority =GetLastError();
      }

      // stack
      stacksize = (char*)__fork_stack_begin - (char*)__fork_stack_end;
      if (!WriteProcessMemory(hProc,(char *)__fork_stack_end,
                                                  (char *)__fork_stack_end,
                                                  (u_long)stacksize,
                                                  &rc)){
            goto error;
      }
      //
      // copy heap itself
      if (!WriteProcessMemory(hProc, (void*)__heap_base,(void*)__heap_base, 
                                    (DWORD)((char*)__heap_top-(char*)__heap_base),
                                    &rc)){
            goto error;
      }

      rc = fork_copy_user_mem(hProc);

      if(rc) {
            goto error;
      }

      // Release the child.
      SetEvent(__hforkparent);
      rc = ResumeThread(hThread);

      __forked=0;
    dprintf("forked process %d\n",pi.dwProcessId);
      start_sigchild_thread(hProc,pi.dwProcessId);
      close_copied_fds();

      CloseHandle(hThread);
      //
      // return process id to parent.
      return pi.dwProcessId;

error:
      __forked=0;
      SetEvent(__hforkparent);
      ResumeThread(hThread);
      CloseHandle(hProc);
      CloseHandle(hThread);
      if (h64Parent) {
            SetEvent(h64Parent); // don't let child block forever
            CloseHandle(h64Parent);
      }
      if (h64Child)
            CloseHandle(h64Child);
      return -1;
}
#pragma optimize("",off)
// The damn optimizer will remove the recursion, resulting in an infinite
// loop. -amol 4/17/97
void stack_probe (void *ptr) {
      char buf[1000];
      int x;

      if (&x > (int *)ptr)
            stack_probe(ptr);
      (void)buf;
}
#pragma optimize("",on)
//
// This function basically reserves some heap space.
// In the child it also commits the size committed in the parent.
void heap_init(void) {

      char * temp;
      int err;
      if (__forked) {
            temp = (char *)VirtualAlloc((void*)__heap_base,__heap_size, MEM_RESERVE,
                                                      PAGE_READWRITE);
            if (temp != (char*)__heap_base) {
                  if (!temp){
                        err = GetLastError();
                        if (bIsWow64Process)
                              ExitProcess(0);
                        abort();
                  }
                  else 
                        __heap_base = temp;
            }
            if (!VirtualAlloc(__heap_base,(char*)__heap_top -(char*)__heap_base, 
                                                MEM_COMMIT,PAGE_READWRITE)){
                  err = GetLastError();
                  if (bIsWow64Process)
                        ExitProcess(0);
                  abort();
            }
            temp = (char*)__heap_base;
      }
      else {
            SYSTEM_INFO sysinfo;
            GetSystemInfo(&sysinfo);
            __heap_size = sysinfo.dwPageSize * 1024;
            __heap_base = VirtualAlloc(0 , __heap_size,MEM_RESERVE|MEM_TOP_DOWN,
                                                                        PAGE_READWRITE);

            if (__heap_base == 0) {
                  abort();
            }

            __heap_top = __heap_base;
      }
      
}
//
// Implementation of sbrk() for the fmalloc family
//
void * sbrk(int delta) {

       void *retval;
       void *old_top=__heap_top;

       if (delta == 0)
            return  __heap_top;
       if (delta > 0) {
            retval = VirtualAlloc((void*)__heap_top, delta,MEM_COMMIT,
                                                      PAGE_READWRITE);
            if (retval == 0 )
                  abort();
            (char*)__heap_top += delta;
       }
       else {
            retval = VirtualAlloc((void*)((char*)__heap_top-(char*)delta), 
                                                delta,MEM_DECOMMIT, PAGE_READWRITE);
            if (retval = 0)
                  abort();
            (char*)__heap_top -= delta;
      }

      return (void*) old_top;
}
/*
 * Semantics of CreateWow64Events
 *
 * Try to open the events even if bOpenExisting is FALSE. This will help
 * us detect name duplication.
 *
 *       1. If OpenEvent succeeds,and bOpenExisting is FALSE,  fail.
 *
 *       2. If OpenEvent failed,and bOpenExisting is TRUE fail
 *
 *       3. else create the events anew
 *
 */
#define TCSH_WOW64_PARENT_EVENT_NAME "tcsh-wow64-parent-event"
#define TCSH_WOW64_CHILD_EVENT_NAME  "tcsh-wow64-child-event"
BOOL CreateWow64Events(DWORD pid, HANDLE *hParent, HANDLE *hChild, 
                                    BOOL bOpenExisting) {

      SECURITY_ATTRIBUTES sa;
      char parentname[256],childname[256];

      *hParent = *hChild = NULL;

      // make darn sure they're not inherited
      sa.nLength = sizeof(sa);
      sa.lpSecurityDescriptor =0;
      sa.bInheritHandle = FALSE;
      //

      // This event tells the child to hold for gForkData to be copied
      wsprintf(parentname,"Local\\%d-%s",pid, TCSH_WOW64_PARENT_EVENT_NAME);

      wsprintf(childname,"Local\\%d-%s",pid, TCSH_WOW64_CHILD_EVENT_NAME );

      *hParent = OpenEvent(EVENT_ALL_ACCESS,FALSE, parentname);

      if(*hParent) {
            if (bOpenExisting == FALSE) { // didn't expect to be a child process
                  CloseHandle(*hParent);
                  *hParent = NULL;
                  return FALSE;
            }

            *hChild = OpenEvent(EVENT_ALL_ACCESS,FALSE, childname);
            if (!*hChild) {
                  CloseHandle(*hParent);
                  *hParent = NULL;
                  return FALSE;
            }

            return TRUE;
      }
      else { //event does not exist
            if (bOpenExisting == TRUE)
                  return FALSE;
      }

      *hParent = CreateEvent(&sa,FALSE,FALSE,parentname);   
      if (!*hParent)
            return FALSE;


      *hChild = CreateEvent(&sa,FALSE,FALSE,childname);     
      if (!*hChild){
            CloseHandle(*hParent);
            *hParent = NULL;
            return FALSE;
      }
      return TRUE;
}

Generated by  Doxygen 1.6.0   Back to index