"Fossies" - the Fresh Open Source Software Archive

Member "muscle/dataio/ChildProcessDataIO.cpp" (21 Nov 2020, 35083 Bytes) of package /linux/privat/muscle7.62.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ChildProcessDataIO.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 7.61_vs_7.62.

    1 /* This file is Copyright 2000-2013 Meyer Sound Laboratories Inc.  See the included LICENSE.txt file for details. */  
    2 
    3 #include <limits.h>   // for PATH_MAX
    4 #include "dataio/ChildProcessDataIO.h"
    5 #include "util/MiscUtilityFunctions.h"     // for ExitWithoutCleanup()
    6 #include "util/NetworkUtilityFunctions.h"  // SendData() and ReceiveData()
    7 #include "util/SocketMultiplexer.h"
    8 
    9 #if defined(WIN32) || defined(__CYGWIN__)
   10 # include <process.h>     // for _beginthreadex()
   11 # if defined(_UNICODE) || defined(UNICODE)
   12 #  undef GetEnvironmentStrings   // here because Windows headers are FUBAR ( https://devblogs.microsoft.com/oldnewthing/20130117-00/?p=5533 )
   13 # endif
   14 # define USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
   15 #else
   16 # if defined(__linux__)
   17 #  include <pty.h>     // for forkpty() on Linux
   18 # elif !defined(MUSCLE_AVOID_FORKPTY)
   19 #  include <util.h>    // for forkpty() on MacOS/X
   20 # endif
   21 # include <termios.h>
   22 # include <signal.h>  // for SIGHUP, etc
   23 # include <sys/wait.h>  // for waitpid()
   24 #endif
   25 
   26 #if defined(__APPLE__) && defined(MUSCLE_ENABLE_AUTHORIZATION_EXECUTE_WITH_PRIVILEGES)
   27 # include <fcntl.h>    // for F_GETOWN
   28 # include <Security/Authorization.h>
   29 # include <Security/AuthorizationTags.h>
   30 #endif
   31 
   32 #include "util/NetworkUtilityFunctions.h"
   33 #include "util/String.h"
   34 #include "util/StringTokenizer.h"
   35 
   36 namespace muscle {
   37 
   38 ChildProcessDataIO :: ChildProcessDataIO(bool blocking)
   39    : _blocking(blocking)
   40    , _killChildOkay(true)
   41    , _maxChildWaitTime(0)
   42    , _signalNumber(-1)
   43    , _childProcessCrashed(false)
   44    , _childProcessIsIndependent(false)
   45 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
   46    , _readFromStdout(INVALID_HANDLE_VALUE), _writeToStdin(INVALID_HANDLE_VALUE), _ioThread(INVALID_HANDLE_VALUE), _wakeupSignal(INVALID_HANDLE_VALUE), _childProcess(INVALID_HANDLE_VALUE), _childThread(INVALID_HANDLE_VALUE), _requestThreadExit(false)
   47 #else
   48    , _childPID(-1)
   49 #endif
   50 #if defined(__APPLE__) && defined(MUSCLE_ENABLE_AUTHORIZATION_EXECUTE_WITH_PRIVILEGES)
   51    , _authRef(NULL)
   52 #endif
   53 {
   54    // empty
   55 }
   56 
   57 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
   58 static void SafeCloseHandle(::HANDLE & h)
   59 {
   60    if (h != INVALID_HANDLE_VALUE)
   61    {
   62       CloseHandle(h);
   63       h = INVALID_HANDLE_VALUE;
   64    }
   65 }
   66 #endif
   67 
   68 status_t ChildProcessDataIO :: LaunchChildProcess(const Queue<String> & argq, ChildProcessLaunchFlags launchFlags, const char * optDirectory, const Hashtable<String, String> * optEnvironmentVariables)
   69 {
   70    const uint32 numItems = argq.GetNumItems();
   71    if (numItems == 0) return B_BAD_ARGUMENT;
   72 
   73    const char ** argv = newnothrow_array(const char *, numItems+1);
   74    if (argv == NULL) RETURN_OUT_OF_MEMORY;
   75    for (uint32 i=0; i<numItems; i++) argv[i] = argq[i]();
   76    argv[numItems] = NULL;
   77    status_t ret = LaunchChildProcess(numItems, argv, launchFlags, optDirectory, optEnvironmentVariables);
   78    delete [] argv;
   79    return ret;
   80 }
   81 
   82 void ChildProcessDataIO :: SetChildProcessShutdownBehavior(bool okayToKillChild, int sendSignalNumber, uint64 maxChildWaitTime)
   83 {
   84    _killChildOkay    = okayToKillChild;
   85    _signalNumber     = sendSignalNumber;
   86    _maxChildWaitTime = maxChildWaitTime;
   87 }
   88 
   89 status_t ChildProcessDataIO :: LaunchChildProcessAux(int argc, const void * args, ChildProcessLaunchFlags launchFlags, const char * optDirectory, const Hashtable<String, String> * optEnvironmentVariables)
   90 {
   91    TCHECKPOINT;
   92 
   93    Close();  // paranoia
   94    _childProcessCrashed = false;  // we don't care about the crashed-flag of a previous process anymore!
   95 
   96 #ifdef MUSCLE_AVOID_FORKPTY
   97    launchFlags.ClearBit(CHILD_PROCESS_LAUNCH_FLAG_USE_FORKPTY);   // no sense trying to use pseudo-terminals if they were forbidden at compile time
   98 #endif
   99 
  100 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
  101    SECURITY_ATTRIBUTES saAttr;
  102    {
  103       memset(&saAttr, 0, sizeof(saAttr));
  104       saAttr.nLength        = sizeof(saAttr);
  105       saAttr.bInheritHandle = true;
  106    }
  107 
  108    status_t ret;
  109 
  110    ::HANDLE childStdoutRead, childStdoutWrite;
  111    if (CreatePipe(&childStdoutRead, &childStdoutWrite, &saAttr, 0))
  112    {
  113       if (DuplicateHandle(GetCurrentProcess(), childStdoutRead, GetCurrentProcess(), &_readFromStdout, 0, false, DUPLICATE_SAME_ACCESS))
  114       {
  115          SafeCloseHandle(childStdoutRead);  // we'll use the dup from now on
  116 
  117          ::HANDLE childStdinRead, childStdinWrite;
  118          if (CreatePipe(&childStdinRead, &childStdinWrite, &saAttr, 0))
  119          {
  120             if (DuplicateHandle(GetCurrentProcess(), childStdinWrite, GetCurrentProcess(), &_writeToStdin, 0, false, DUPLICATE_SAME_ACCESS))
  121             {
  122                SafeCloseHandle(childStdinWrite);  // we'll use the dup from now on
  123 
  124                PROCESS_INFORMATION piProcInfo; 
  125                memset(&piProcInfo, 0, sizeof(piProcInfo));
  126 
  127                STARTUPINFOA siStartInfo;         
  128                {
  129                   const bool hideChildGUI = launchFlags.IsBitSet(CHILD_PROCESS_LAUNCH_FLAG_WIN32_HIDE_GUI);
  130 
  131                   memset(&siStartInfo, 0, sizeof(siStartInfo));
  132                   siStartInfo.cb          = sizeof(siStartInfo);
  133                   siStartInfo.hStdError   = childStdoutWrite;
  134                   siStartInfo.hStdOutput  = childStdoutWrite;
  135                   siStartInfo.hStdInput   = childStdinRead;
  136                   siStartInfo.dwFlags     = STARTF_USESTDHANDLES | (hideChildGUI ? STARTF_USESHOWWINDOW : 0);
  137                   siStartInfo.wShowWindow = hideChildGUI ? SW_HIDE : 0;
  138                }
  139 
  140                String cmd;
  141                if (argc < 0) cmd = (const char *) args;
  142                else
  143                {
  144                   const char ** argv = (const char **) args;
  145                   Queue<String> tmpQ; (void) tmpQ.EnsureSize(argc);
  146                   for (int i=0; i<argc; i++) (void) tmpQ.AddTail(argv[i]);
  147                   cmd = UnparseArgs(tmpQ);
  148                }
  149 
  150                // If environment-vars are specified, we need to create a new environment-variable-block
  151                // for the child process to use.  It will be the same as our own, but with the specified
  152                // env-vars added/updated in it.
  153                void * envVars  = NULL;
  154                char * newBlock = NULL;
  155                if ((optEnvironmentVariables)&&(optEnvironmentVariables->HasItems()))
  156                {
  157                   Hashtable<String, String> curEnvVars;
  158 
  159                   char * oldEnvs = GetEnvironmentStrings();
  160                   if (oldEnvs)
  161                   {
  162                      const char * s = oldEnvs;
  163                      while(s)
  164                      {
  165                         if (*s)
  166                         {
  167                            const char * equals = strchr(s, '=');
  168                            if ((equals ? curEnvVars.Put(String(s, (uint32)(equals-s)), equals+1) : curEnvVars.Put(s, GetEmptyString())).IsOK(ret)) s = strchr(s, '\0')+1;
  169                                                                                                                                               else break;
  170                         }
  171                         else break;
  172                      }
  173 
  174                      FreeEnvironmentStringsA(oldEnvs);
  175                   }
  176 
  177                   (void) curEnvVars.Put(*optEnvironmentVariables);  // update our existing vars with the specified ones
  178 
  179                   // Now we can make a new environment-variables-block out of (curEnvVars)
  180                   uint32 newBlockSize = 1;  // this represents the final NUL terminator (after the last string)
  181                   for (HashtableIterator<String, String> iter(curEnvVars); iter.HasData(); iter++) newBlockSize += iter.GetKey().FlattenedSize()+iter.GetValue().FlattenedSize();  // includes NUL terminators
  182 
  183                   uint8 * newBlock = newnothrow uint8[newBlockSize];
  184                   if (newBlock)
  185                   {
  186                      uint8 * s = newBlock;
  187                      for (HashtableIterator<String, String> iter(curEnvVars); iter.HasData(); iter++)
  188                      {
  189                         iter.GetKey().Flatten(s);   s += iter.GetKey().FlattenedSize();
  190                         *(s-1) = '=';  // replace key's trailing-NUL with an '=' sign
  191                         iter.GetValue().Flatten(s); s += iter.GetValue().FlattenedSize();
  192                      }
  193                      *s++ = '\0';
  194 
  195                      envVars = newBlock;
  196                   }
  197                   else
  198                   {
  199                      WARN_OUT_OF_MEMORY;
  200                      ret = B_OUT_OF_MEMORY;
  201                   }
  202                }
  203              
  204                if (ret == B_NO_ERROR)
  205                {
  206                   if (CreateProcessA((argc>=0)?(((const char **)args)[0]):NULL, (char *)cmd(), NULL, NULL, TRUE, 0, envVars, optDirectory, &siStartInfo, &piProcInfo))
  207                   {
  208                      delete [] newBlock;
  209                      newBlock = NULL;  // void possible double-delete below
  210    
  211                      _childProcess   = piProcInfo.hProcess;
  212                      _childThread    = piProcInfo.hThread;
  213    
  214                      if (_blocking) return B_NO_ERROR;  // done!
  215                      else 
  216                      {
  217                         // For non-blocking, we must have a separate proxy thread do the I/O for us :^P
  218                         _wakeupSignal = CreateEvent(0, false, false, 0);
  219                              if (_wakeupSignal == INVALID_HANDLE_VALUE) ret = B_ERRNO;
  220                         else if (CreateConnectedSocketPair(_masterNotifySocket, _slaveNotifySocket, false).IsOK(ret))
  221                         {
  222                            DWORD junkThreadID;
  223                            typedef unsigned (__stdcall *PTHREAD_START) (void *);
  224                            if ((_ioThread = (::HANDLE) _beginthreadex(NULL, 0, (PTHREAD_START)IOThreadEntryFunc, this, 0, (unsigned *) &junkThreadID)) != INVALID_HANDLE_VALUE) return B_NO_ERROR;
  225                                                                                                                                                                            else ret = B_ERRNO;
  226                         }
  227                      }
  228                   }
  229                   else ret = B_ERRNO;
  230                }
  231 
  232                delete [] newBlock;
  233             }
  234             else ret = B_ERRNO;
  235 
  236             SafeCloseHandle(childStdinRead);     // cleanup
  237             SafeCloseHandle(childStdinWrite);    // cleanup
  238          }
  239          else ret = B_ERRNO;
  240       }
  241       else ret = B_ERRNO;
  242 
  243       SafeCloseHandle(childStdoutRead);    // cleanup
  244       SafeCloseHandle(childStdoutWrite);   // cleanup
  245    }
  246    else ret = B_ERRNO;
  247 
  248    Close();  // free all allocated object state we may have
  249    return ret | B_ERROR;
  250 #else
  251    status_t ret;
  252 
  253    // First, set up our arguments array here in the parent process, since the child process won't be able to do any dynamic allocations.
  254    Queue<String> scratchChildArgQ;  // holds the strings that the pointers in the argv buffer will point to
  255    const bool isParsed = (argc<0);
  256    if (argc < 0)
  257    {
  258       if (ParseArgs(String((const char *)args), scratchChildArgQ).IsError(ret)) return ret;
  259       argc = scratchChildArgQ.GetNumItems();
  260    }
  261 
  262    Queue<const char *> scratchChildArgv;  // the child process's argv array, NULL terminated
  263    if (scratchChildArgv.EnsureSize(argc+1, true).IsError(ret)) return ret;
  264 
  265    // Populate the argv array for our child process to use
  266    const char ** argv = scratchChildArgv.HeadPointer();
  267    if (isParsed) for (int i=0; i<argc; i++) argv[i] = scratchChildArgQ[i]();
  268             else memcpy(argv, args, argc*sizeof(char *));
  269    argv[argc] = NULL; // argv array must be NULL terminated!
  270 
  271 #if defined(__APPLE__) && defined(MUSCLE_ENABLE_AUTHORIZATION_EXECUTE_WITH_PRIVILEGES)
  272    if (_dialogPrompt.HasChars()) return LaunchPrivilegedChildProcess(argv);
  273 #endif
  274    
  275    pid_t pid = (pid_t) -1;
  276    if (launchFlags.IsBitSet(CHILD_PROCESS_LAUNCH_FLAG_USE_FORKPTY))
  277    {
  278 # ifdef MUSCLE_AVOID_FORKPTY
  279       return B_UNIMPLEMENTED;  // this branch should never be taken, due to the ifdef at the top of this function... but just in case
  280 # else
  281       // New-fangled forkpty() implementation
  282       int masterFD = -1;
  283       pid = forkpty(&masterFD, NULL, NULL, NULL);
  284            if (pid > 0) _handle = GetConstSocketRefFromPool(masterFD); 
  285       else if (pid == 0)
  286       {
  287          // Turn off the echo, we don't want to see that back on stdout
  288          struct termios tios;
  289          if (tcgetattr(STDIN_FILENO, &tios) >= 0)
  290          {
  291             tios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
  292             tios.c_oflag &= ~(ONLCR); /* also turn off NL to CR/NL mapping on output */
  293             (void) tcsetattr(STDIN_FILENO, TCSANOW, &tios);
  294          }
  295       }
  296 #endif
  297    }
  298    else
  299    {
  300       // Old-fashioned fork() implementation
  301       ConstSocketRef masterSock, slaveSock;
  302       if (CreateConnectedSocketPair(masterSock, slaveSock, true).IsError(ret)) return ret;
  303       pid = fork();
  304            if (pid > 0) _handle = masterSock;
  305       else if (pid == 0)
  306       {
  307          int fd = slaveSock()->GetFileDescriptor();
  308          if ((launchFlags.IsBitSet(CHILD_PROCESS_LAUNCH_FLAG_EXCLUDE_STDIN)  == false)&&(dup2(fd, STDIN_FILENO)  < 0)) ExitWithoutCleanup(20);
  309          if ((launchFlags.IsBitSet(CHILD_PROCESS_LAUNCH_FLAG_EXCLUDE_STDOUT) == false)&&(dup2(fd, STDOUT_FILENO) < 0)) ExitWithoutCleanup(20);
  310          if ((launchFlags.IsBitSet(CHILD_PROCESS_LAUNCH_FLAG_EXCLUDE_STDERR) == false)&&(dup2(fd, STDERR_FILENO) < 0)) ExitWithoutCleanup(20);
  311       }
  312    }
  313 
  314         if (pid < 0) return B_ERRNO;  // fork failure!
  315    else if (pid == 0)
  316    {
  317       // we are the child process
  318       (void) signal(SIGHUP, SIG_DFL);  // FogBugz #2918
  319 
  320       // Close any file descriptors leftover from the parent process
  321       if (launchFlags.IsBitSet(CHILD_PROCESS_LAUNCH_FLAG_INHERIT_FDS) == false)
  322       {
  323          const int fdlimit = (int)sysconf(_SC_OPEN_MAX);
  324          for (int i=STDERR_FILENO+1; i<fdlimit; i++) close(i);
  325       }
  326 
  327       if (_childProcessIsIndependent) (void) BecomeDaemonProcess();  // used by the LaunchIndependentChildProcess() static methods only
  328 
  329       char absArgv0[PATH_MAX];
  330       const char ** zargv = &scratchChildArgv[0];
  331       const char * zargv0 = scratchChildArgv[0];
  332       if (optDirectory)
  333       {
  334          // If we are going to change to a different directory, then we need to
  335          // generate an absolute-filepath for zargv[0] first, otherwise we won't
  336          // be able to find the executable to run!
  337          if (realpath(zargv[0], absArgv0) != NULL) zargv[0] = zargv0 = absArgv0;
  338          if (chdir(optDirectory) < 0) perror("ChildProcessDataIO::chdir");  // FogBugz #10023
  339       }
  340 
  341       if (optEnvironmentVariables)
  342       {
  343          for (HashtableIterator<String,String> iter(*optEnvironmentVariables); iter.HasData(); iter++) (void) setenv(iter.GetKey()(), iter.GetValue()(), 1);
  344       }
  345 
  346       if (ChildProcessReadyToRun().IsOK(ret))
  347       {
  348          if (execvp(zargv0, const_cast<char **>(zargv)) < 0) perror("ChildProcessDataIO::execvp");  // execvp() should never return
  349       }
  350       else LogTime(MUSCLE_LOG_ERROR, "ChildProcessDataIO:  ChildProcessReadyToRun() returned [%s], not running child process!\n", ret());
  351 
  352       ExitWithoutCleanup(20);
  353    }
  354    else if (_handle())   // if we got this far, we are the parent process
  355    {
  356       _childPID = pid;
  357       if (SetSocketBlockingEnabled(_handle, _blocking).IsOK(ret)) return B_NO_ERROR;
  358    }
  359 
  360    Close();  // roll back!
  361    return ret | B_ERROR;
  362 #endif
  363 }
  364 
  365 #if defined(__APPLE__) && defined(MUSCLE_ENABLE_AUTHORIZATION_EXECUTE_WITH_PRIVILEGES)
  366 status_t ChildProcessDataIO :: LaunchPrivilegedChildProcess(const char ** argv)
  367 {
  368    // Code adapted from Performant Design's cocoasudo program 
  369    // at git://github.com/performantdesign/cocoasudo.git
  370    OSStatus status;
  371    AuthorizationRef authRef = NULL;
  372 
  373    AuthorizationItem right = { kAuthorizationRightExecute, strlen(argv[0]), &argv[0], 0 };
  374    AuthorizationRights rightSet = { 1, &right };
  375 
  376    AuthorizationEnvironment myAuthorizationEnvironment; memset(&myAuthorizationEnvironment, 0, sizeof(myAuthorizationEnvironment));
  377    AuthorizationItem kAuthEnv;                          memset(&kAuthEnv, 0, sizeof(kAuthEnv));
  378    myAuthorizationEnvironment.items = &kAuthEnv;
  379 
  380    kAuthEnv.name        = kAuthorizationEnvironmentPrompt;
  381    kAuthEnv.valueLength = _dialogPrompt.Length();
  382    kAuthEnv.value       = (void *) _dialogPrompt();
  383    kAuthEnv.flags       = 0;
  384    myAuthorizationEnvironment.count = 1;
  385 
  386    if (AuthorizationCreate(NULL, &myAuthorizationEnvironment, kAuthorizationFlagDefaults, &authRef) != errAuthorizationSuccess)
  387    {
  388       LogTime(MUSCLE_LOG_ERROR, "ChildProcessDataIO::LaunchPrivilegedChildProcess():  Could not create authorization reference object.\n");
  389       return B_ERROR("AuthorizationCreate() failed");
  390    }
  391    else status = AuthorizationCopyRights(authRef, &rightSet, &myAuthorizationEnvironment, kAuthorizationFlagDefaults|kAuthorizationFlagPreAuthorize|kAuthorizationFlagInteractionAllowed|kAuthorizationFlagExtendRights, NULL);
  392 
  393    if (status == errAuthorizationSuccess)
  394    {
  395       FILE *ioPipe;
  396       status = AuthorizationExecuteWithPrivileges(authRef, argv[0], kAuthorizationFlagDefaults, (char **)(&argv[1]), &ioPipe);  // +1 because it doesn't take the traditional argv[0]
  397       if (status == errAuthorizationSuccess)
  398       {
  399          _ioPipe.SetFile(ioPipe);
  400          _handle  = _ioPipe.GetReadSelectSocket();
  401          _authRef = authRef;
  402          return _handle() ? SetSocketBlockingEnabled(_handle, false) : B_ERROR;
  403       }
  404       else 
  405       {
  406          AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
  407          return (status == errAuthorizationCanceled) ? B_ACCESS_DENIED : B_ERROR("AuthorizationExecuteWithPrivileges() failed");
  408       }
  409    }
  410    else
  411    {
  412       AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
  413       return (status == errAuthorizationCanceled) ? B_ACCESS_DENIED : B_ERROR("AuthorizationCopyRights() pre-authorization failed");
  414    }
  415 }
  416 #endif
  417 
  418 ChildProcessDataIO :: ~ChildProcessDataIO()
  419 {
  420    TCHECKPOINT;
  421    Close();
  422 }
  423 
  424 bool ChildProcessDataIO :: IsChildProcessAvailable() const
  425 {
  426 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
  427    return (_readFromStdout != INVALID_HANDLE_VALUE);
  428 #else
  429    return (_handle.GetFileDescriptor() >= 0);
  430 #endif
  431 } 
  432 
  433 status_t ChildProcessDataIO :: KillChildProcess()
  434 {
  435    TCHECKPOINT;
  436 
  437 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
  438    if (_childProcess == INVALID_HANDLE_VALUE) return B_BAD_OBJECT;
  439    return TerminateProcess(_childProcess, 0) ? B_NO_ERROR : B_ERRNO;
  440 #else
  441 # if defined(__APPLE__) && defined(MUSCLE_ENABLE_AUTHORIZATION_EXECUTE_WITH_PRIVILEGES)
  442    if (_ioPipe.GetFile()) return B_UNIMPLEMENTED;  // no can do
  443 # endif
  444    if (_childPID < 0) return B_BAD_OBJECT;
  445    if (kill(_childPID, SIGKILL) == 0)
  446    {
  447       (void) waitpid(_childPID, NULL, 0);  // avoid creating a zombie process
  448       _childPID = -1;
  449       return B_NO_ERROR;
  450    }
  451    else return B_ERRNO;
  452 #endif
  453 }
  454 
  455 status_t ChildProcessDataIO :: SignalChildProcess(int sigNum)
  456 {
  457    TCHECKPOINT;
  458 
  459 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
  460    (void) sigNum;   // to shut the compiler up
  461    return B_UNIMPLEMENTED;  // Not implemented under Windows!
  462 #else
  463 # if defined(__APPLE__) && defined(MUSCLE_ENABLE_AUTHORIZATION_EXECUTE_WITH_PRIVILEGES)
  464    if (_ioPipe.GetFile()) return B_UNIMPLEMENTED;  // no can do
  465 # endif
  466    if (_childPID < 0) return B_BAD_OBJECT;
  467    return (kill(_childPID, sigNum) == 0) ? B_NO_ERROR : B_ERRNO; // Yes, kill() is a misnomer.  Silly Unix people!
  468 #endif
  469 }
  470 
  471 void ChildProcessDataIO :: Close()
  472 {
  473    TCHECKPOINT;
  474 
  475 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
  476    if (_ioThread != INVALID_HANDLE_VALUE)  // if this is valid, _wakeupSignal is guaranteed valid too
  477    {
  478       _requestThreadExit = true;                // set the "Please go away" flag
  479       SetEvent(_wakeupSignal);                  // wake the thread up so he'll check the flag
  480       WaitForSingleObject(_ioThread, INFINITE); // then wait for him to go away
  481       ::CloseHandle(_ioThread);                 // fix handle leak
  482       _ioThread = INVALID_HANDLE_VALUE;
  483    }
  484    _masterNotifySocket.Reset();
  485    _slaveNotifySocket.Reset();
  486    SafeCloseHandle(_wakeupSignal);
  487    SafeCloseHandle(_readFromStdout);
  488    SafeCloseHandle(_writeToStdin);
  489    if ((_childProcess != INVALID_HANDLE_VALUE)&&(_childProcessIsIndependent == false)) DoGracefulChildShutdown();  // Windows can't double-fork, so in the independent case we just won't wait for him
  490    SafeCloseHandle(_childProcess);
  491    SafeCloseHandle(_childThread);
  492 #else
  493 
  494    _handle.Reset();
  495 
  496    if (_childPID >= 0) DoGracefulChildShutdown();
  497    _childPID = -1;
  498 
  499 # if defined(__APPLE__) && defined(MUSCLE_ENABLE_AUTHORIZATION_EXECUTE_WITH_PRIVILEGES)
  500    _ioPipe.Shutdown();
  501    if (_authRef)
  502    {  
  503       AuthorizationFree((AuthorizationRef)_authRef, kAuthorizationFlagDestroyRights);
  504       _authRef = NULL;
  505    }
  506 # endif
  507 #endif
  508 }
  509 
  510 void ChildProcessDataIO :: DoGracefulChildShutdown()
  511 {
  512    if (_signalNumber >= 0) (void) SignalChildProcess(_signalNumber);
  513    if ((WaitForChildProcessToExit(_maxChildWaitTime) == false)&&(_killChildOkay)) (void) KillChildProcess();
  514 }
  515 
  516 bool ChildProcessDataIO :: WaitForChildProcessToExit(uint64 maxWaitTimeMicros)
  517 {
  518 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
  519    if (_childProcess == INVALID_HANDLE_VALUE) return true; // a non-existent child process is an exited child process, if you ask me.
  520    _childProcessCrashed = false;                           // reset the flag only when there is an actual child process to wait for
  521 
  522    if (WaitForSingleObject(_childProcess, (maxWaitTimeMicros==MUSCLE_TIME_NEVER)?INFINITE:((DWORD)(maxWaitTimeMicros/1000))) == WAIT_OBJECT_0)
  523    {
  524       DWORD exitCode;
  525       if (GetExitCodeProcess(_childProcess, &exitCode))
  526       {
  527          // Note that this is only a heuristic since a sufficiently
  528          // demented child process could return a code that matches
  529          // this criterion as part of its normal exit(), and a crashed
  530          // program could (conceivably) have an exit code that doesn't
  531          // meet this criterion.  But in general this will work.  --jaf
  532          _childProcessCrashed = ((exitCode & (0xC0000000)) != 0);
  533       }
  534       return true;
  535    }
  536 #else
  537 # if defined(__APPLE__) && defined(MUSCLE_ENABLE_AUTHORIZATION_EXECUTE_WITH_PRIVILEGES)
  538    if (_ioPipe.GetFile())
  539    {
  540       const int fd = fileno(_ioPipe.GetFile());
  541 
  542       // Since AuthorizationExecuteWithPrivileges() doesn't give us a _childPID to wait on, all we can do is 
  543       // block-and-read until either we read EOF from the _ioPipe or we reach the timeout-time.
  544       bool sawEOF = false;
  545       SocketMultiplexer sm;
  546       const uint64 endTime = (maxWaitTimeMicros == MUSCLE_TIME_NEVER) ? MUSCLE_TIME_NEVER : (GetRunTime64()+maxWaitTimeMicros);
  547       while(GetRunTime64() < endTime)
  548       {
  549          if ((sm.RegisterSocketForReadReady(fd) != B_NO_ERROR)||(sm.WaitForEvents(endTime) < 0)) break;
  550          else
  551          {
  552             char junk[1024] = "";
  553             if ((fread(junk, sizeof(junk), 1, _ioPipe.GetFile()) < 0)||(feof(_ioPipe.GetFile())))
  554             {
  555                sawEOF = true;
  556                break;
  557             }
  558          }
  559       }
  560       return sawEOF;  // if we saw EOF on the _ioPipe, we'll take that to mean the child process exited -- best we can do
  561    }
  562 # endif
  563 
  564    if (_childPID < 0) return true;   // a non-existent child process is an exited child process, if you ask me.
  565    _childProcessCrashed = false;     // reset the flag only when there is an actual child process to wait for
  566 
  567    if (maxWaitTimeMicros == MUSCLE_TIME_NEVER) 
  568    {
  569       int status = 0;
  570       const int pid = waitpid(_childPID, &status, 0);
  571       if (pid == _childPID)
  572       {
  573          _childProcessCrashed = WIFSIGNALED(status);
  574          return true;
  575       }
  576    }
  577    else
  578    {
  579       // The tricky case... waiting for the child process to exit, with a timeout.
  580       // I'm implementing it via a polling loop, which is a sucky way to implement
  581       // it but the only alternative would involve mucking about with signal handlers,
  582       // and doing it that way would be unreliable in multithreaded environments.
  583       const uint64 endTime = GetRunTime64()+maxWaitTimeMicros;
  584       uint64 pollInterval = 0;  // we'll start quickly, and start polling more slowly only if the child process doesn't exit soon
  585       while(1)
  586       {
  587          int status = 0;
  588          const int r = waitpid(_childPID, &status, WNOHANG);  // WNOHANG should guarantee that this call will not block
  589          if (r == _childPID) 
  590          {
  591             _childProcessCrashed = WIFSIGNALED(status);
  592             return true;  // yay, he exited!
  593          }
  594          else if (r == -1) break;      // fail on error
  595 
  596          const int64 microsLeft = endTime-GetRunTime64();
  597          if (microsLeft <= 0) break;   // we're out of time!
  598 
  599          // At this point, r was probably zero because the child wasn't ready to exit
  600          if ((int64)pollInterval < MillisToMicros(200)) pollInterval += MillisToMicros(10);
  601          Snooze64(muscleMin(pollInterval, (uint64)microsLeft));
  602       }
  603    }
  604 #endif
  605 
  606    return false;
  607 }
  608 
  609 int32 ChildProcessDataIO :: Read(void *buf, uint32 len)
  610 {
  611    TCHECKPOINT;
  612 
  613    if (IsChildProcessAvailable())
  614    {
  615 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
  616       if (_blocking)
  617       {
  618          DWORD actual_read;
  619          if (ReadFile(_readFromStdout, buf, len, &actual_read, NULL)) return actual_read;
  620       }
  621       else 
  622       {
  623          const int32 ret = ReceiveData(_masterNotifySocket, buf, len, _blocking);
  624          if (ret >= 0) SetEvent(_wakeupSignal);  // wake up the thread in case he has more data to give us
  625          return ret;
  626       }
  627 #else
  628       const long r = read_ignore_eintr(_handle.GetFileDescriptor(), buf, len);
  629       return _blocking ? (int32)r : ConvertReturnValueToMuscleSemantics(r, len, _blocking);
  630 #endif
  631    }
  632    return -1;
  633 }
  634 
  635 int32 ChildProcessDataIO :: Write(const void *buf, uint32 len)
  636 {
  637    TCHECKPOINT;
  638 
  639    if (IsChildProcessAvailable())
  640    {
  641 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
  642       if (_blocking)
  643       {
  644          DWORD actual_write;
  645          if (WriteFile(_writeToStdin, buf, len, &actual_write, 0)) return actual_write;
  646       }
  647       else 
  648       {
  649          const int32 ret = SendData(_masterNotifySocket, buf, len, _blocking);
  650          if (ret > 0) SetEvent(_wakeupSignal);  // wake up the thread so he'll check his socket for our new data
  651          return ret;
  652       }
  653 #else
  654       return ConvertReturnValueToMuscleSemantics(write_ignore_eintr(_handle.GetFileDescriptor(), buf, len), len, _blocking);
  655 #endif
  656    }
  657    return -1;
  658 }
  659 
  660 void ChildProcessDataIO :: FlushOutput()
  661 { 
  662    // not implemented
  663 }
  664 
  665 status_t ChildProcessDataIO :: ChildProcessReadyToRun()
  666 {
  667    return B_NO_ERROR;
  668 }
  669 
  670 const ConstSocketRef & ChildProcessDataIO :: GetChildSelectSocket() const
  671 {
  672 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
  673    return _blocking ? GetNullSocket() : _masterNotifySocket;
  674 #else 
  675    return _handle;
  676 #endif
  677 }
  678 
  679 void ChildProcessDataIO :: Shutdown()
  680 {
  681    Close();
  682 }
  683 
  684 #ifdef USE_WINDOWS_CHILDPROCESSDATAIO_IMPLEMENTATION
  685 
  686 const uint32 CHILD_BUFFER_SIZE = 1024;
  687 
  688 // Used as a temporary holding area for data in transit
  689 class ChildProcessBuffer
  690 {
  691 public:
  692    ChildProcessBuffer()
  693       : _length(0)
  694       , _index(0) 
  695    {
  696       // empty
  697    }
  698 
  699    char _buf[CHILD_BUFFER_SIZE];
  700    uint32 _length;  // how many bytes in _buf are actually valid
  701    uint32 _index;   // Index of the next byte to process
  702 };
  703 
  704 // to be called from within the I/O thread only!
  705 void ChildProcessDataIO :: IOThreadAbort()
  706 {
  707    // If we read zero bytes, that means EOF!  Child process has gone away!
  708    _slaveNotifySocket.Reset();
  709    _requestThreadExit = true;  // this will cause the I/O thread to go away now
  710 }
  711 
  712 void ChildProcessDataIO :: IOThreadEntry()
  713 {
  714    bool childProcessExited = false;
  715 
  716    ChildProcessBuffer inBuf;  // bytes from the child process's stdout, waiting to go to the _slaveNotifySocket
  717    ChildProcessBuffer outBuf; // bytes from the _slaveNotifySocket, waiting to go to the child process's stdin
  718 
  719    ::HANDLE events[] = {_wakeupSignal, _childProcess};
  720    while(_requestThreadExit == false)
  721    {
  722       // IOThread <-> UserThread i/o handling here
  723       {
  724          // While we have any data in inBuf, send as much of it as possible back to the user thread.  This won't block.
  725          while(inBuf._index < inBuf._length)
  726          {
  727             const int32 bytesToWrite = inBuf._length-inBuf._index;
  728             const int32 bytesWritten = (bytesToWrite > 0) ? SendData(_slaveNotifySocket, &inBuf._buf[inBuf._index], bytesToWrite, false) : 0;
  729             if (bytesWritten > 0)
  730             {
  731                inBuf._index += bytesWritten;
  732                if (inBuf._index == inBuf._length) inBuf._index = inBuf._length = 0;
  733             }
  734             else
  735             {
  736                if (bytesWritten < 0) IOThreadAbort();  // use thread connection closed!?
  737                break;  // can't write any more, for now
  738             }
  739          }
  740 
  741          // While we have room in our outBuf, try to read some more data into it from the slave socket.  This won't block.
  742          while(outBuf._length < sizeof(outBuf._buf))
  743          {
  744             const int32 maxLen = sizeof(outBuf._buf)-outBuf._length;
  745             const int32 ret = ReceiveData(_slaveNotifySocket, &outBuf._buf[outBuf._length], maxLen, false);
  746             if (ret > 0) outBuf._length += ret;
  747             else
  748             {
  749                if (ret < 0) IOThreadAbort();  // user thread connection closed!?
  750                break;  // no more to read, for now
  751             }
  752          }
  753       }
  754 
  755       // IOThread <-> ChildProcess i/o handling (and blocking) here
  756       {
  757          if (childProcessExited)
  758          {
  759             if (inBuf._index == inBuf._length) IOThreadAbort();
  760             break;
  761          }
  762 
  763          // block here until an event happens... gotta poll because
  764          // the Window anonymous pipes system doesn't allow me to
  765          // to check for events on the pipe using WaitForMultipleObjects().
  766          // It may be worth it to use named pipes some day to get around this...
  767          const int evt = WaitForMultipleObjects(ARRAYITEMS(events)-(childProcessExited?1:0), events, false, 250)-WAIT_OBJECT_0;
  768          if (evt == 1) childProcessExited = true;
  769 
  770          int32 numBytesToRead;
  771          while((numBytesToRead = sizeof(inBuf._buf)-inBuf._length) > 0)
  772          {
  773             // See if there is actually any data available for reading first
  774             DWORD pipeSize;
  775             if (PeekNamedPipe(_readFromStdout, NULL, 0, NULL, &pipeSize, NULL))
  776             {
  777                if (pipeSize > 0)
  778                {
  779                   DWORD numBytesRead;
  780                   if (ReadFile(_readFromStdout, &inBuf._buf[inBuf._length], numBytesToRead, &numBytesRead, NULL))
  781                   {
  782                      inBuf._length += numBytesRead;
  783                   }
  784                   else
  785                   {
  786                      IOThreadAbort();  // child process exited?
  787                      break;
  788                   }
  789                }
  790                else break;
  791             }
  792             else 
  793             {
  794                IOThreadAbort();  // child process exited?
  795                break;
  796             }
  797          }
  798 
  799          int32 numBytesToWrite;
  800          while((numBytesToWrite = outBuf._length-outBuf._index) > 0)
  801          {
  802             DWORD bytesWritten;
  803             if (WriteFile(_writeToStdin, &outBuf._buf[outBuf._index], numBytesToWrite, &bytesWritten, 0))
  804             {
  805                if (bytesWritten > 0)
  806                {
  807                   outBuf._index += bytesWritten;
  808                   if (outBuf._index == outBuf._length) outBuf._index = outBuf._length = 0;
  809                }
  810                else break;  // no more space to write to, for now
  811             }
  812             else IOThreadAbort();  // wtf?
  813          }
  814       }
  815    }
  816 }
  817 #endif
  818 
  819 status_t ChildProcessDataIO :: System(int argc, const char * argv[], ChildProcessLaunchFlags launchFlags, uint64 maxWaitTimeMicros, const char * optDirectory, const Hashtable<String, String> * optEnvironmentVariables)
  820 {
  821    status_t ret;
  822    ChildProcessDataIO cpdio(false);
  823    if (cpdio.LaunchChildProcess(argc, argv, launchFlags, optDirectory, optEnvironmentVariables).IsOK(ret))
  824    {
  825       (void) cpdio.WaitForChildProcessToExit(maxWaitTimeMicros);
  826       return B_NO_ERROR;
  827    }
  828    else return ret;
  829 }
  830 
  831 status_t ChildProcessDataIO :: System(const Queue<String> & argq, ChildProcessLaunchFlags launchFlags, uint64 maxWaitTimeMicros, const char * optDirectory, const Hashtable<String, String> * optEnvironmentVariables)
  832 {
  833    const uint32 numItems = argq.GetNumItems();
  834    if (numItems == 0) return B_BAD_ARGUMENT;
  835 
  836    const char ** argv = newnothrow_array(const char *, numItems+1);
  837    if (argv == NULL) RETURN_OUT_OF_MEMORY;
  838    for (uint32 i=0; i<numItems; i++) argv[i] = argq[i]();
  839    argv[numItems] = NULL;
  840    const status_t ret = System(numItems, argv, launchFlags, maxWaitTimeMicros, optDirectory, optEnvironmentVariables);
  841    delete [] argv;
  842    return ret;
  843 }
  844 
  845 status_t ChildProcessDataIO :: System(const char * cmdLine, ChildProcessLaunchFlags launchFlags, uint64 maxWaitTimeMicros, const char * optDirectory, const Hashtable<String, String> * optEnvironmentVariables)
  846 {
  847    ChildProcessDataIO cpdio(false);
  848    status_t ret;
  849    if (cpdio.LaunchChildProcess(cmdLine, launchFlags, optDirectory, optEnvironmentVariables).IsOK(ret))
  850    {
  851       cpdio.WaitForChildProcessToExit(maxWaitTimeMicros);
  852       return B_NO_ERROR;
  853    }
  854    else return ret;
  855 }
  856 
  857 status_t ChildProcessDataIO :: LaunchIndependentChildProcess(int argc, const char * argv[], const char * optDirectory, ChildProcessLaunchFlags launchFlags, const Hashtable<String, String> * optEnvironmentVariables)
  858 {
  859    ChildProcessDataIO cpdio(true);
  860    cpdio._childProcessIsIndependent = true;  // so the cpdio dtor won't block waiting for the child to exit
  861    cpdio.SetChildProcessShutdownBehavior(false);
  862    return cpdio.LaunchChildProcess(argc, argv, launchFlags, optDirectory, optEnvironmentVariables);
  863 }
  864 
  865 status_t ChildProcessDataIO :: LaunchIndependentChildProcess(const char * cmdLine, const char * optDirectory, ChildProcessLaunchFlags launchFlags, const Hashtable<String, String> * optEnvironmentVariables)
  866 {
  867    ChildProcessDataIO cpdio(true);
  868    cpdio._childProcessIsIndependent = true;  // so the cpdio dtor won't block waiting for the child to exit
  869    cpdio.SetChildProcessShutdownBehavior(false);
  870    return cpdio.LaunchChildProcess(cmdLine, launchFlags, optDirectory, optEnvironmentVariables);
  871 }
  872 
  873 status_t ChildProcessDataIO :: LaunchIndependentChildProcess(const Queue<String> & argv, const char * optDirectory, ChildProcessLaunchFlags launchFlags, const Hashtable<String, String> * optEnvironmentVariables)
  874 {
  875    ChildProcessDataIO cpdio(true);
  876    cpdio._childProcessIsIndependent = true;  // so the cpdio dtor won't block waiting for the child to exit
  877    cpdio.SetChildProcessShutdownBehavior(false);
  878    return cpdio.LaunchChildProcess(argv, launchFlags, optDirectory, optEnvironmentVariables);
  879 }
  880 
  881 } // end namespace muscle