// win_thread.c

// WIN32 thread creation, and running - a sort of replacement of fork()

// extern int execvp (__const char *__file, char *__const __argv[])
// from - C:\Projects\include\unistd.h (203)

#pragma warning(disable:4995) // suppress name was marked as #pragma deprecated
#include <config.h>
#include "win_thread.h"
#include <aclapi.h>
#include <strsafe.h>

/* with this off, the application seems to get into a stalled state
   when the 'tar' application ends - that ending is not detected,
   and it stays on a read ...
 */
#define USESTDHANDLES            // THIS WORKS FINE

#undef START_SUSPENDED           // another idea

#undef ADD_SECURITY_DESCRIPTOR   // trying to get this working, but FAILED
#define ADD_OUT_FILE             // ADD debug output

#define  CHKMEM(a)   if(!a) { fprintf(stderr, "\nERROR: MEMORY FAILED! ABORTING!!\n" ); exit(2); }

typedef struct tagTHARGS1 {
   char * file;
   char ** argv;
}THARGS1, * PTHARGS1;

int exec_error = 0;
char * pthreaderr = 0;

static char _s_tebuff[1024];
unsigned int threadID1 = 0;
BOOL bThreadExit1 = FALSE;
static THARGS1 thargs1;
int   sleep_max_ms = 50000;
int   sleep_one_ms = 50;
static TCHAR  szErr[1024]; 

LPSTR GetLastErrorText( LPSTR lpszBuf, DWORD dwSize ); 
char * GetWinErrorText( char * lpszBuf, DWORD dwSize, DWORD dwLastError );
int ForkAndExec( int out_sock, char **argvv, int *sockpid );
char * win_genfile_use_system( char * file, char * argv[] );
PSECURITY_DESCRIPTOR create_security_descriptor( void );

static char * w_set_thread_error( int err )
{
   char * perr;
   switch(err)
   {
   case EAGAIN:
      perr = "too many threads!";
      break;
   case EINVAL:
      perr = "argument is invalid or the stack size is incorrect!";
      break;
   case E2BIG:
      perr = "The space required for the arguments and environment settings exceeds 32 KB!";
      break;
   case EACCES:
      perr = "The specified file has a locking or sharing violation!";
      break;
   case EMFILE:
      perr = "Too many files open (the specified file must be opened to determine whether it is executable).";
      break;
   case ENOENT:
      perr = "The file or path not found.";
      break;
    case ENOEXEC:
      perr = "The specified file is not executable or has an invalid executable-file format!";
      break;
   case ENOMEM:
      perr = "Not enough memory; available memory corrupted; invalid block exists!";
      break;
   default:
      perr = _s_tebuff;
      sprintf(perr, "Uncased value %d", exec_error);
      break;
   }
   return perr;
}

unsigned __stdcall genfile_thread( void * pArg )
{
   PTHARGS1 pta = (PTHARGS1)pArg;
   /* execvp() is supposed to use PATH to find the EXE,
      BUT is seems you can NOT pass just say 'tar' ...
      and NOT even with tar.exe, which should be on the PATH
      ONLY with the fully qualified PATH will it work,
      and that PATH HAS TO USE '/' to get it through the
      --run command of genfile !!!
      ************************************************************
      EEEK! It seems when _execpv() completes successfully, it CLOSES
      this application, even when it is on a THREAD of this app!!!
      So the creation of a THREAD does NOT protect this application,
      and allow it to continue running ... it was about to exit anyway,
      but what if I did want to do other things ...
      ************************************************************
   */

   int status = _execvp ( pta->file, pta->argv );

   /* if this returns, it is an ERROR */
   exec_error = errno;
   pthreaderr = w_set_thread_error( exec_error );
   bThreadExit1 = TRUE; // set EXIT flag
   _endthreadex( 0xdeadfeed );
   return 0xdeadfeed;
}

/* an ALTERNATIVE to _execvp(), which never returns,
   AND exits the whole application when done */

unsigned __stdcall genfile_thread2( void * pArg )
{
   PTHARGS1 pta = (PTHARGS1)pArg;
   char * arg, * p;
   int   i, max;
   p = _s_tebuff;
   *p = 0;
   for( i = 0; ; i++ )
   {
      arg = pta->argv[i];
      if( arg && *arg )
      {
         if( i )
            strcat(p, " ");
         strcat(p, arg);
      }
      else
         break;
   }

   /* NOW TO RUN IT ON THIS THREAD */
   max = system(p);
#if (defined(_MSC_VER) && !defined(NDEBUG)) /* DEBUG: show system(...) return */
   printf( "Return is %d ...\n", max );
#endif

   arg = 0;
   if( max == -1 )
   {
      max = errno;
      exec_error = max;
      arg = szErr;
      sprintf(arg, "system(\"%s\"); returned %d!", max );
   }

   pthreaderr = arg;
   bThreadExit1 = TRUE; // reset EXIT flag
   _endthreadex( 0xdeadfeed );
   return 0xdeadfeed;
}

static void w_set_perr( char ** pperr )
{
   int err = errno;
   char * perr = w_set_thread_error(err);
   exec_error = err;
   *pperr = perr;
}

static void w_wait_a_bit( void )
{
   int max = sleep_max_ms;
   while(max)
   {
      if( bThreadExit1 ) // is EXIT flag set
         break;
      if( pthreaderr )
         break;
      Sleep(sleep_one_ms);
      if( max > sleep_one_ms )
         max -= sleep_one_ms;
      else
         max = 0;
   }
}


char * win_genfile_thread( char * file, char * argv[], enum thread_options type )
{
   // CreateThread(
   uintptr_t uiThread;
   PTHARGS1 pta = &thargs1;
   char * perr = 0;

   exec_error = 0;
   pthreaderr = NULL;
   bThreadExit1 = FALSE; // reset EXIT flag
   
   pta->file = file;
   pta->argv = argv;
   // Create the thread.
   switch(type)
   {
   case THREAD_USE_EXECVP:
      uiThread = _beginthreadex( NULL, 0, &genfile_thread, pta, 0, &threadID1 );
      if( uiThread == 0 )
         w_set_perr( &perr );
      else
         w_wait_a_bit();
      break;
   case THREAD_USE_SYSTEM:
      uiThread = _beginthreadex( NULL, 0, &genfile_thread2, pta, 0, &threadID1 );
      if( uiThread == 0 )
         w_set_perr( &perr );
      else
         w_wait_a_bit();
      break;
   case USE_SYSTEM_DIRECT:
      win_genfile_use_system( file, argv );
      break;
   case USE_CREATE_PROCESS:
      ForkAndExec( 0, argv, NULL );
      break;
   default:
      fprintf(stderr, "ERROR: Internal bad parameter! (%d) ... Aborting!!\n", type );
      exit(2);
      break;
   }
   if( pthreaderr )
      return pthreaderr;
   return perr;
}

/* an ALTERNATIVE approach, with NO THREADS */
static size_t get_total_argument_length( char ** argv )
{
   size_t len = 0;
   if( argv )
   {
      int   j;
      for (j = 0; argv[j]!= NULL; j++)
      {
         len += strlen(argv[j]) + 2;   // plus space and nul
         if( j == 0 )
            len *= 2;   // allow for the first runtime argument to be added twice
      }
   }
   return len;
}

// #ifdef _MSC_VER /* replace pipe and fork for WIN32 */
char * win_genfile_use_system( char * file, char * argv[] )
{
   /* all the effort to split the argument at 'spaces' is now undone */
   char * cp;
   char * arg = argv[0];
   int max = 0;
   size_t len;
   int i;

   len = get_total_argument_length(argv);
   arg = malloc( len );
   CHKMEM(arg);
   cp = arg;
   *cp = 0;
   for( i = 0; argv[i] != NULL; i++ )
   {
      if(i) strcat(cp, " ");
      strcat( cp, argv[i] );
   }

   /* NOW TO RUN IT ON THIS THREAD */
   max = system(arg);
   /* all done */

#if (defined(_MSC_VER) && !defined(NDEBUG)) /* DEBUG: show system(...) return */
   printf( "Return is %d ...\n", max );
#endif

   cp = 0;  // clear error
   if( max == -1 )
   {
      max = errno;
      exec_error = max;
      cp = _s_tebuff;
      sprintf(cp, "system(\"%s\"); returned %d!", arg, max );
   }
   free(arg);
   arg = cp;
   return arg;
}

// /* _MSC_VER */

#define CHECKPOINT_TEXT "Write checkpoint"
extern void process_checkpoint (size_t n);

#define  FE_ERROR(a) \
   dw_winLastError = GetLastError(); \
   pthreaderr = GetWinErrorText(szErr,sizeof(szErr),dw_winLastError); \
   if( VFH(win_GetOutHandle()) ) WriteFile(win_GetOutHandle(),pthreaderr,strlen(pthreaderr),&dwWritten,NULL); \
   iret = a; goto cleanup;

int ForkAndExec( int out_sock, char **argvv, int *sockpid )
{
   char * pszArgs = NULL;
   int iret = 0;
   char chReadBuffer[64];
   BOOL bSuccess;
   int j;
   HANDLE hReadPipe = NULL;
   HANDLE hWritePipe = NULL;
   HANDLE hWritePipe2 = NULL;
   char *p;
   DWORD cchReadBuffer; 
   DWORD dwWritten;
   STARTUPINFO si;
   PROCESS_INFORMATION pi; 
   SECURITY_ATTRIBUTES saPipe;
   SECURITY_ATTRIBUTES saProcess;
   SECURITY_ATTRIBUTES * psa = NULL;
   DWORD dwCreationFlags = 0;
   size_t len;
   char * buf = _s_tebuff;
   HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
   FILE * frh = NULL;   // this did NOT work!!!

   /* Set up the command-line buffer */ 
   len = get_total_argument_length ( argvv );
   if ( len == 0 )
   {
      pthreaderr = "ERROR: Invalid aguments!";
      iret = 1;
      return iret;
   }
   pszArgs = malloc( len );
   memset(pszArgs, 0, len);

   /* Set up the STARTUPINFO structure */ 
   memset( &si, 0, sizeof(si) );
   si.cb = sizeof(si);

   /* Clear the PROCESS_INFORMATION structure */ 
   memset( &pi, 0, sizeof(pi) );
   
   // strcpy(szArgs, *argvv); // copy in the runtime exe/bat/etc file name
   p = pszArgs;  // get end of buffer
   for (j = 0; argvv[j]!= NULL; j++)
   {
      if(j)
         strcat(p, " ");      // space em out
      strcat(p, argvv[j]); // add agument
   }

#ifdef ADD_OUT_FILE
   /* create the log file where we will save all output from child */
   win_Create_Log_File();
   /* more or less ignore if this fails, since it is only for DEBUG,
      but issue a WARNING ... */
   if( !VFH(win_GetOutHandle()) )
   {
      printf("WARNING: %s DEBUG LOG FAILED! Error inication\n%s\n",
         DEF_LOG_FILE,
         GetLastErrorText(szErr,sizeof(szErr)));
   }
#endif // ADD_OUT_FILE


   /* set up the security attributes for the anonymous pipe */ 
   saPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
   saPipe.lpSecurityDescriptor = NULL; 
   saPipe.bInheritHandle = TRUE; 

#ifdef ADD_SECURITY_DESCRIPTOR
   saProcess.nLength = sizeof(SECURITY_ATTRIBUTES);
   saProcess.lpSecurityDescriptor = create_security_descriptor();
   saProcess.bInheritHandle = FALSE;
   psa = &saProcess;
#endif // ADD_SECURITY_DESCRIPTOR

   /* create the anonymous pipe */ 
   bSuccess = CreatePipe( &hReadPipe, // read handle
      &hWritePipe,   // write handle
      &saPipe,       // SECURITY_ATTRIBUTES
      0 );           // buffer size indication - 0 = default size
   if ( !bSuccess ) { FE_ERROR(2); }

   /* Now we need to change the inheritable property for the readable 
      end of the pipe. 
      */ 
   bSuccess = DuplicateHandle(
      GetCurrentProcess(),    // HANDLE hSourceProcessHandle
      hReadPipe,              // HANDLE hSourceHandle
      GetCurrentProcess(),    // HANDLE hTargetProcessHandle
      NULL,                   // LPHANDLE lpTargetHandle
      0,                      // DWORD dwDesiredAccess
      FALSE,                  // BOOL bInheritHandle
      DUPLICATE_SAME_ACCESS );// DWORD dwOptions
   if ( !bSuccess ) { FE_ERROR(3); }

   // oops, can NOT do this on a HANDLE!!!
   //frh = _fdopen( (int)hReadPipe, "r" );
   //if( !frh )
   //{
   //   pthreaderr = "ERROR: Failed to get FILE * from handle!";
   //   iret = 3;
   //   goto cleanup;
   //}

   bSuccess = DuplicateHandle(GetCurrentProcess(),
      hWritePipe, 
      GetCurrentProcess(),
      &hWritePipe2,
      0,
      TRUE,                   // BOOL bInheritHandle
      DUPLICATE_SAME_ACCESS );
   if ( !bSuccess ) { FE_ERROR(4); }

#ifdef USESTDHANDLES 
   /* file handles specified in the STARTUPINFO structure will
      be inheritied by the child. */ 
   si.hStdInput = hWritePipe2;
   si.hStdOutput = hWritePipe; 
   si.hStdError = hWritePipe2;
   si.dwFlags = STARTF_USESTDHANDLES;
 #else 
   /* If the standard handles have been redirected,
      you can specify the CONIN$ value in a call to the CreateFile function
      to get a handle to a console's input buffer. Similarly, you can 
      specify the CONOUT$ value to get a handle to the console's 
      active screen buffer.
   */
   bSuccess = SetStdHandle( STD_INPUT_HANDLE,  hWritePipe2 );
  	bSuccess = SetStdHandle( STD_OUTPUT_HANDLE, hWritePipe  );
  	bSuccess = SetStdHandle( STD_ERROR_HANDLE,  hWritePipe2 );

   bSuccess = DuplicateHandle(
      GetCurrentProcess(),
      GetStdHandle(STD_INPUT_HANDLE),
      GetCurrentProcess(),
      NULL, 
      0,
      FALSE,
      DUPLICATE_SAME_ACCESS );

#endif 

   /* create the child process
      BOOL CreateProcess(
        LPCTSTR lpApplicationName,
        LPTSTR lpCommandLine,
        LPSECURITY_ATTRIBUTES lpProcessAttributes,
        LPSECURITY_ATTRIBUTES lpThreadAttributes,
        BOOL bInheritHandles,
        DWORD dwCreationFlags,
        LPVOID lpEnvironment,
        LPCTSTR lpCurrentDirectory,
        LPSTARTUPINFO lpStartupInfo,
        LPPROCESS_INFORMATION lpProcessInformation
      );
    // open the process with the required access for the operation
    HANDLE hProcess = OpenProcess(PROCESS_VM_READ|PROCESS_QUERY_INFORMATION,
                                  FALSE,
                                  Pid);
   */ 

#ifdef START_SUSPENDED
   dwCreationFlags |= CREATE_SUSPENDED;
#endif

   bSuccess = CreateProcess(NULL,   // LPCTSTR lpApplicationName
      pszArgs,                      // LPTSTR lpCommandLine
      psa,                          // LPSECURITY_ATTRIBUTES lpProcessAttributes
      NULL,                         // LPSECURITY_ATTRIBUTES lpThreadAttributes
      TRUE,                         // BOOL bInheritHandles
      dwCreationFlags,              // DWORD dwCreationFlags
      NULL,                         // LPVOID lpEnvironment
      NULL,                         // LPCTSTR lpCurrentDirectory
      &si,                          // LPSTARTUPINFO lpStartupInfo
      &pi );                        // LPPROCESS_INFORMATION lpProcessInformation
   if ( !bSuccess ) { FE_ERROR(5); }

   if( dwCreationFlags & CREATE_SUSPENDED )
   {
      ResumeThread( pi.hThread );
   }

   CloseHandle(pi.hThread);   // clean up object - does nothing to thread
   pi.hThread = NULL;

   /*We can close our instances of the inheritable pipe write handle*/ 
   bSuccess = CloseHandle(hWritePipe);
   hWritePipe = NULL;
   bSuccess = CloseHandle(hWritePipe2);
   hWritePipe2 = NULL;

   len = 0;    // accumulated length
   buf = _s_tebuff;  // buffer for it

   /* read from the pipe until we get an ERROR_BROKEN_PIPE
   */ 

   for (;;)
   {
      DWORD dwerr;
      cchReadBuffer = 0;
      if( frh && ( ferror(frh) || feof(frh) ) )
      {
            len = sprintf(buf, "Break on ferror() or feof()!\n");
            if( VFH(win_GetOutHandle()) ) WriteFile(win_GetOutHandle(),buf,len,&dwWritten,NULL);
#ifndef NDEBUG
            WriteFile( hParentStdOut, buf, len, &cchReadBuffer, NULL );
#endif   // !NDEBUG
         break;
      }
      bSuccess = PeekNamedPipe( hReadPipe, NULL, 0, NULL, NULL, NULL );
      dwerr = GetLastError();
      if( !bSuccess )
      {
         len = sprintf( buf, "Break on FAILED Peek: %d\n%s\n",
            dwerr,
            GetWinErrorText(szErr,sizeof(szErr),dwerr));
         if( VFH(win_GetOutHandle()) ) WriteFile(win_GetOutHandle(),buf,len,&dwWritten,NULL);
#ifndef NDEBUG
         WriteFile( hParentStdOut, buf, len, &cchReadBuffer, NULL );
#endif   // !NDEBUG
         break;
      }
      bSuccess = ReadFile(hReadPipe,
         chReadBuffer, 
         sizeof(chReadBuffer),
         &cchReadBuffer,
         NULL );
      dwerr = GetLastError();
      if ( bSuccess )
      {
         // successful read
         if( cchReadBuffer )
         {
            strncpy( buf + len, chReadBuffer, cchReadBuffer );
            if( VFH(win_GetOutHandle()) ) WriteFile(win_GetOutHandle(),chReadBuffer,cchReadBuffer,&dwWritten,NULL);
            WriteFile( hParentStdOut, chReadBuffer, cchReadBuffer, &dwWritten, NULL );
            len += cchReadBuffer;
            buf[len] = 0;  // zero terminate
         }
         else
         {
            /* When a synchronous read operation reaches the end of a file,
               ReadFile returns TRUE and sets *lpNumberOfBytesRead to zero.
               */
            len = sprintf(buf, "Break on NULL FILE Read!\n%s\n",
               GetWinErrorText(szErr,sizeof(szErr),dwerr));
            if( VFH(win_GetOutHandle()) ) WriteFile(win_GetOutHandle(),buf,len,&dwWritten,NULL);
#ifndef NDEBUG
            WriteFile( hParentStdOut, buf, len, &cchReadBuffer, NULL );
#endif   // !NDEBUG
            break;
         }
      }
      else
      {
         if( dwerr == ERROR_IO_PENDING )
            continue;   // this is NOT an ERROR
         len = sprintf(buf, "Break on FAILED Read: %d\n%s\n",
            cchReadBuffer,
            GetWinErrorText(szErr,sizeof(szErr),dwerr));
         if( VFH(win_GetOutHandle()) ) WriteFile(win_GetOutHandle(),buf,len,&dwWritten,NULL);
#ifndef NDEBUG
         WriteFile( hParentStdOut, buf, len, &cchReadBuffer, NULL );
#endif   // !NDEBUG
         break;
      }

      while ( len )
      {
         char * p;
         char c;
         p = buf;
         // get an end of this line
         while (*p && (*p >= ' '))
            p++;
         if( *p && ( p > buf ) )
         {
            // we are interested - got start, and end of a line
            char * start;
            size_t sz;
            *p = 0;
            p++;
            while( *p && ( *p < ' ' ) )
               p++;
            start = p;  // start of next line, or zero
            while( *p )
               p++;
            sz = p - start;
            //fprintf (stderr, "%s\n", buf);
            // now look for check point
            p = buf; // back to start
            while (*p && (*p != ':'))
               p++;  // move up to ':' character
            if (*p == ':')
            {
               for (p++; *p && isspace (*p); p++);
               if (*p
                  && memcmp (p, CHECKPOINT_TEXT, sizeof CHECKPOINT_TEXT - 1) == 0)
               {
                  char *end = p;
                  size_t n = strtoul (p + sizeof CHECKPOINT_TEXT - 1, &end, 10);
                  if (!(*end && !isspace (*end)))
                  {
                     sprintf(szErr,"CHECK POINT %d.\r\n", n);
                     if( VFH(win_GetOutHandle()) ) WriteFile(win_GetOutHandle(),szErr, strlen(szErr),&dwWritten,NULL);
                     process_checkpoint (n);
                  }
               }
            }
            if( sz )
            {
               // more data - to copy it up to beginning
               memmove( buf, start, sz );
               buf[sz] = 0;
               len = strlen(buf);
            }
            else
               len = 0; // no data after this line ...
         }
         else
            break;
      }

#if 0    // OLD CODE
      if ( bSuccess && cchReadBuffer )
      {
         if(out_sock)
            send( out_sock, chReadBuffer, cchReadBuffer, 0);

         // write buffer (of specified length) to console
         printf("%.*s", cchReadBuffer, chReadBuffer);

         p = chReadBuffer;
         while (*p && !isspace (*p) && *p != ':')
            p++;

         if (*p == ':')
         {
            for (p++; *p && isspace (*p); p++);
            if (*p
               && memcmp (p, CHECKPOINT_TEXT, sizeof CHECKPOINT_TEXT - 1) == 0)
            {
               char *end;
               size_t n = strtoul (p + sizeof CHECKPOINT_TEXT - 1, &end, 10);
               if (!(*end && !isspace (*end)))
               {
                  process_checkpoint (n);
                  continue;
               }
            }
         }
         //fprintf (stderr, "%s", buf);
      }

      if (cchReadBuffer < 64) // why this???
      {
#ifndef NDEBUG
         printf( "CHECK: Read only %d bytes!! Why exit loop???\n", cchReadBuffer );
         // I GUESS it is because of buffering
         // ReadFile() waits for the length, and if less, then process exited
#endif   // !NDEBUG
         break;
      }
#endif   // OLD CODE

   }

   cchReadBuffer = 0;
   bSuccess = GetExitCodeProcess( pi.hProcess, // HANDLE hProcess
      &cchReadBuffer );   // LPDWORD lpExitCode

   /* close the pipe handle */ 
   CloseHandle(hReadPipe);
   hReadPipe = NULL;

   // *** handle_SIGCHLD(pi); ?? WHAT IS THIS????
   // /* Called when the child process quits. */
   // void handle_sigchld(int dummy) {
   /* Leave it to GDK to find out about. */
   // write(sigchld_pipe[1], "!", 1); }
   // AND
   //	if (opt & OPT_WATCHCHILD)
   //   signal(SIGCHLD, handle_sigchld);
   // else /* prevent dead children from becoming zombies */
   //   signal(SIGCHLD, SIG_IGN); // predefined constants SIG_DFL or SIG_IGN
   // from MSDN HELP
   // Sets interrupt signal handling.
   // void (__cdecl *signal(
   // int sig, 
   // void (__cdecl *func ) (int [, int ] ))) 
   // (int);
   /* WIN32 signal constants
      SIGABRT - Abnormal termination
      SIGFPE  - Floating-point error
      SIGILL  - Illegal instruction
      SIGINT  - CTRL+C signal
      SIGSEGV - Illegal storage access
      SIGTERM - Termination request
      ------------------------------------------ */
   if( bSuccess )
   {
      len = sprintf(buf, "Process exited with %d\n", cchReadBuffer );
      if( VFH(win_GetOutHandle()) ) WriteFile(win_GetOutHandle(),buf,len,&dwWritten,NULL);
#ifndef NDEBUG
      WriteFile( hParentStdOut, buf, len, &dwWritten, NULL );
#endif /* !NDEBUG */
   }
   bSuccess = TRUE;

cleanup:

   /* allow system to clean up, but will happen on exit anyway */

   //if( pszArgs )
   //   free(pszArgs);

   if( pi.hProcess )
      CloseHandle( pi.hProcess );

   if( pi.hThread )
      CloseHandle(pi.hThread);

   if( hWritePipe )
      CloseHandle(hWritePipe);

   if( hWritePipe2 )
      CloseHandle(hWritePipe2);

   if( hReadPipe )
      CloseHandle(hReadPipe);

   if(sockpid)
      *sockpid = pi.dwProcessId; // return the PID of the process

   if( VFH(win_GetOutHandle()) )
      CloseHandle(win_GetOutHandle());

   return iret;
}


PSECURITY_DESCRIPTOR create_security_descriptor( void )
{
   DWORD dwRes, dwDisposition;
   PSID pEveryoneSID = NULL, pAdminSID = NULL;
   PACL pACL = NULL;
   PSECURITY_DESCRIPTOR pSD = NULL;
   EXPLICIT_ACCESS ea[2];
   SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
   SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
   SECURITY_ATTRIBUTES sa;
   LONG lRes;
   HKEY hkSub = NULL;
   BOOL bSuccess = FALSE;

    // Create a well-known SID for the Everyone group.
    if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
                     SECURITY_WORLD_RID,
                     0, 0, 0, 0, 0, 0, 0,
                     &pEveryoneSID))
    {
       dw_winLastError = GetLastError();
        printf("AllocateAndInitializeSid Error %u\n%s\n", dw_winLastError,
           GetWinErrorText(szErr,sizeof(szErr), dw_winLastError));
        goto Cleanup;
    }

    // Initialize an EXPLICIT_ACCESS structure for an ACE.
    // The ACE will allow Everyone read access to the key.
    ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
    ea[0].grfAccessPermissions   = KEY_READ;
    ea[0].grfAccessMode          = SET_ACCESS;
    ea[0].grfInheritance         = NO_INHERITANCE;
    ea[0].Trustee.TrusteeForm    = TRUSTEE_IS_SID;
    ea[0].Trustee.TrusteeType    = TRUSTEE_IS_WELL_KNOWN_GROUP;
    ea[0].Trustee.ptstrName      = (LPTSTR) pEveryoneSID;

    // Create a SID for the BUILTIN\Administrators group.
    if(! AllocateAndInitializeSid(&SIDAuthNT, 2,
                     SECURITY_BUILTIN_DOMAIN_RID,
                     DOMAIN_ALIAS_RID_ADMINS,
                     0, 0, 0, 0, 0, 0,
                     &pAdminSID)) 
    {
        printf("AllocateAndInitializeSid Error %u\n", GetLastError());
        goto Cleanup; 
    }

    // Initialize an EXPLICIT_ACCESS structure for an ACE.
    // The ACE will allow the Administrators group full access to
    // the key.
    ea[1].grfAccessPermissions = KEY_ALL_ACCESS;
    ea[1].grfAccessMode = SET_ACCESS;
    ea[1].grfInheritance= NO_INHERITANCE;
    ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
    ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
    ea[1].Trustee.ptstrName  = (LPTSTR) pAdminSID;

    // Create a new ACL that contains the new ACEs.
    dwRes = SetEntriesInAcl(2, ea, NULL, &pACL);
    if (ERROR_SUCCESS != dwRes) 
    {
        printf("SetEntriesInAcl Error %u\n", GetLastError());
        goto Cleanup;
    }

    // Initialize a security descriptor.  
    pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, 
                             SECURITY_DESCRIPTOR_MIN_LENGTH); 
    if (NULL == pSD) 
    { 
        printf("LocalAlloc Error %u\n", GetLastError());
        goto Cleanup; 
    } 
 
    if (!InitializeSecurityDescriptor(pSD,SECURITY_DESCRIPTOR_REVISION)) 
    {  
        printf("InitializeSecurityDescriptor Error %u\n",
                                GetLastError());
        goto Cleanup; 
    } 
 
    // Add the ACL to the security descriptor. 
    if (!SetSecurityDescriptorDacl(pSD, 
            TRUE,     // bDaclPresent flag   
            pACL, 
            FALSE))   // not a default DACL 
    {  
        printf("SetSecurityDescriptorDacl Error %u\n",
                GetLastError());
        goto Cleanup; 
    } 

    // Initialize a security attributes structure.
    sa.nLength = sizeof (SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = pSD;
    sa.bInheritHandle = FALSE;

    // Use the security attributes to set the security descriptor 
    // when you create a key.
    lRes = RegCreateKeyEx(HKEY_CURRENT_USER, "mykey", 0, "", 0, 
            KEY_READ | KEY_WRITE, &sa, &hkSub, &dwDisposition);

    if ( lRes != ERROR_SUCCESS )
    {
       printf("RegCreateKeyEx result %u\n", lRes );
       goto Cleanup; 
    }

    bSuccess = TRUE;

Cleanup:

    if (pEveryoneSID) 
        FreeSid(pEveryoneSID);
    if (pAdminSID) 
        FreeSid(pAdminSID);
    if (pACL)
    {
       if( !bSuccess )
       {
           LocalFree(pACL);
       }
    }
    if (pSD)
    {
       if( !bSuccess )
       {
         LocalFree(pSD);
         pSD = NULL;
       }
    }
    if (hkSub) 
        RegCloseKey(hkSub);

    return pSD;
}

// eof - win_thread.c

/* MORE SAMPLE REDIRECTION CODE
   ============================
 */

#define BUFSIZE 4096 
 
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;

HANDLE g_hInputFile = NULL;
 
void CreateChildProcess(void); 
void WriteToPipe(void); 
void ReadFromPipe(void); 
void ErrorExit(PTSTR); 
 
int sample_tmain(int argc, TCHAR *argv[]) 
{ 
   SECURITY_ATTRIBUTES saAttr; 
 
   printf("\n->Start of parent execution.\n");

// Set the bInheritHandle flag so pipe handles are inherited. 
 
   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
   saAttr.bInheritHandle = TRUE; 
   saAttr.lpSecurityDescriptor = NULL; 

// Create a pipe for the child process's STDOUT. 
 
   if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) ) 
      ErrorExit(TEXT("StdoutRd CreatePipe")); 

// Ensure the read handle to the pipe for STDOUT is not inherited.

   if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
      ErrorExit(TEXT("Stdout SetHandleInformation")); 

// Create a pipe for the child process's STDIN. 
 
   if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) 
      ErrorExit(TEXT("Stdin CreatePipe")); 

// Ensure the write handle to the pipe for STDIN is not inherited. 
 
   if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
      ErrorExit(TEXT("Stdin SetHandleInformation")); 
 
// Create the child process. 
   
   CreateChildProcess();

// Get a handle to an input file for the parent. 
// This example assumes a plain text file and uses string output to verify data flow. 
 
   if (argc == 1) 
      ErrorExit(TEXT("Please specify an input file.\n")); 

   g_hInputFile = CreateFile(
       argv[1], 
       GENERIC_READ, 
       0, 
       NULL, 
       OPEN_EXISTING, 
       FILE_ATTRIBUTE_READONLY, 
       NULL); 

   if ( g_hInputFile == INVALID_HANDLE_VALUE ) 
      ErrorExit(TEXT("CreateFile")); 
 
// Write to the pipe that is the standard input for a child process. 
// Data is written to the pipe's buffers, so it is not necessary to wait
// until the child process is running before writing data.
 
   WriteToPipe(); 
   printf( "\n->Contents of %s written to child STDIN pipe.\n", argv[1]);
 
// Read from pipe that is the standard output for child process. 
 
   printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
   ReadFromPipe(); 

   printf("\n->End of parent execution.\n");

// The remaining open handles are cleaned up when this process terminates. 
// To avoid resource leaks in a larger application, close handles explicitly. 

   return 0; 
} 
 
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{ 
   TCHAR szCmdline[]=TEXT("child");
   PROCESS_INFORMATION piProcInfo; 
   STARTUPINFO siStartInfo;
   BOOL bSuccess = FALSE; 
 
// Set up members of the PROCESS_INFORMATION structure. 
 
   ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
 
// Set up members of the STARTUPINFO structure. 
// This structure specifies the STDIN and STDOUT handles for redirection.
 
   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
   siStartInfo.cb = sizeof(STARTUPINFO); 
   siStartInfo.hStdError = g_hChildStd_OUT_Wr;
   siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
   siStartInfo.hStdInput = g_hChildStd_IN_Rd;
   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
 
// Create the child process. 
    
   bSuccess = CreateProcess(NULL, 
      szCmdline,     // command line 
      NULL,          // process security attributes 
      NULL,          // primary thread security attributes 
      TRUE,          // handles are inherited 
      0,             // creation flags 
      NULL,          // use parent's environment 
      NULL,          // use parent's current directory 
      &siStartInfo,  // STARTUPINFO pointer 
      &piProcInfo);  // receives PROCESS_INFORMATION 
   
   // If an error occurs, exit the application. 
   if ( ! bSuccess ) 
      ErrorExit(TEXT("CreateProcess"));
   else 
   {
      // Close handles to the child process and its primary thread.
      // Some applications might keep these handles to monitor the status
      // of the child process, for example. 

      CloseHandle(piProcInfo.hProcess);
      CloseHandle(piProcInfo.hThread);
   }
}
 
void WriteToPipe(void) 
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data. 
{ 
   DWORD dwRead, dwWritten; 
   CHAR chBuf[BUFSIZE];
   BOOL bSuccess = FALSE;
 
   for (;;) 
   { 
      bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
      if ( ! bSuccess || dwRead == 0 ) break; 
      
      bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
      if ( ! bSuccess ) break; 
   } 
 
// Close the pipe handle so the child process stops reading. 
 
   if ( ! CloseHandle(g_hChildStd_IN_Wr) ) 
      ErrorExit(TEXT("StdInWr CloseHandle")); 
} 
 
void ReadFromPipe(void) 
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT. 
// Stop when there is no more data. 
{ 
   DWORD dwRead, dwWritten; 
   CHAR chBuf[BUFSIZE]; 
   BOOL bSuccess = FALSE;
   HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

// Close the write end of the pipe before reading from the 
// read end of the pipe, to control child process execution.
// The pipe is assumed to have enough buffer space to hold the
// data the child process has already written to it.
 
   if (!CloseHandle(g_hChildStd_OUT_Wr)) 
      ErrorExit(TEXT("StdOutWr CloseHandle")); 
 
   for (;;) 
   { 
      bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
      if( ! bSuccess || dwRead == 0 ) break; 

      bSuccess = WriteFile(hParentStdOut, chBuf, 
                           dwRead, &dwWritten, NULL);
      if (! bSuccess ) break; 
   } 
} 
 
void ErrorExit(PTSTR lpszFunction) 
// Format a readable error message, display a message box, 
// and exit from the application.
{ 
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(1);
}

