⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 subprocess.cpp

📁 基于ecos的redboot
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//####COPYRIGHTBEGIN####
//                                                                          
// ----------------------------------------------------------------------------
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
//
// This program is part of the eCos host tools.
//
// This program is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the Free 
// Software Foundation; either version 2 of the License, or (at your option) 
// any later version.
// 
// This program is distributed in the hope that it will be useful, but WITHOUT 
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
// more details.
// 
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 
// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// ----------------------------------------------------------------------------
//                                                                          
//####COPYRIGHTEND####
//===========================================================================
//===========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): 	sdf
// Contact(s):	sdf
// Date:		1998/08/11
// Version:		0.01
// Purpose:	
// Description:	This is the implementation of the class which allows for spawning subprocesses
//
// Requires:	
// Provides:	
// See also:    
// Known bugs:	
// Usage:	
//
//####DESCRIPTIONEND####
//
//===========================================================================
#include "eCosTrace.h"
#include "Subprocess.h"
#ifdef _WIN32
  #include <tlhelp32.h>

  HINSTANCE CSubprocess::hInstLib1 = VER_PLATFORM_WIN32_NT==CSubprocess::GetPlatform()?LoadLibrary(_T("PSAPI.DLL")):LoadLibrary(_T("Kernel32.DLL")) ;
  HINSTANCE CSubprocess::hInstLib2 = VER_PLATFORM_WIN32_NT==CSubprocess::GetPlatform()?LoadLibrary(_T("NTDLL.DLL")):NULL;

#endif

//#define CloseHandle(x) TRACE(_T("CSubprocess::CloseHandle %x\n"),x);::CloseHandle(x)

const unsigned int CSubprocess::PROCESS_KILL_EXIT_CODE=0xCCFFCCFF;

CSubprocess::CSubprocess(bool bAutoDelete):
  m_pfnContinue(DefaultContinuationFunc),
  m_pContinuationFuncParam(0),
  m_bAutoDelete(bAutoDelete),
  m_bThreadTerminated(true),
  m_bVerbose(false),
  m_nExitCode(-1),
  m_idProcess(0),
  m_pLogparam(0),
  m_pfnLogfunc(0),
  m_bKillThread(false)
{
#ifdef _WIN32
  m_hProcess=0;
#endif
}

CSubprocess::~CSubprocess()
{
  Kill();
  if(!CeCosThreadUtils::WaitFor(m_bThreadTerminated,1000)){
    m_bKillThread=true;
    CeCosThreadUtils::WaitFor(m_bThreadTerminated);  
  }
#ifdef _WIN32
  if(m_hProcess){
    CloseHandle(m_hProcess);
  }
#endif
}

bool CSubprocess::Run(LogFunc *pfnLog,void * pLogparam, LPCTSTR pszCmd,bool bBlock/*=true*/)
{
  bool rc;
  if(!m_bThreadTerminated){
    rc=false;
  } else {
    m_pfnLogfunc=pfnLog;
    m_pLogparam=pLogparam;
#ifdef _WIN32 
    // UNIX does it from the thread func.  WIN32 could too, but it's nice to know at the time 
    // of calling run whether the process is successfully created.
    if(m_hProcess){
      // Normally done in the dtor
      CloseHandle(m_hProcess);
    }
    rc=CreateProcess(pszCmd);
#else 
    m_strCmd=pszCmd;
    rc=true;
#endif
    if(rc){
      m_bKillThread=false;
      if(bBlock){
        // When using RunThread, the manipulation of this Boolean is taken care of.
        // Here we must do it ourselves.
        m_bThreadTerminated=false;
        ThreadFunc();
        m_bThreadTerminated=true;
      } else {
        CeCosThreadUtils::RunThread(SThreadFunc,this,&m_bThreadTerminated,String::SFormat(_T("subprocess %d read"),m_idProcess));
      }
    }
  }
  return rc;
}

#ifdef _WIN32
bool CSubprocess::CreateProcess(LPCTSTR pszCmdline)
{
  
  STARTUPINFO   si;                    // For CreateProcess call
  HANDLE        hrPipe,hwPipe,hwPipe2,m_hrPipeTemp,m_hwPipeTemp;
  // Create the anonymous pipe
  
  SECURITY_ATTRIBUTES saPipe;          // Security for anonymous pipe
  saPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
  saPipe.lpSecurityDescriptor = NULL;
  saPipe.bInheritHandle = true;
  
  ::CreatePipe(&m_hrPipeTemp,&hwPipe,&saPipe,10240);
  
  // In most cases you can get away with using the same anonymous
  // pipe write handle for both the child's standard output and
  // standard error, but this may cause problems if the child app
  // explicitly closes one of its standard output or error handles. If
  // that happens, the anonymous pipe will close, since the child's
  // standard output and error handles are really the same handle. The
  // child won't be able to write to the other write handle since the
  // pipe is now gone, and parent reads from the pipe will return
  // ERROR_BROKEN_PIPE and child output will be lost. To solve this
  // problem, simply duplicate the write end of the pipe to create
  // another distinct, separate handle to the write end of the pipe.
  // One pipe write handle will serve as standard out, the other as
  // standard error. Now *both* write handles must be closed before the
  // write end of the pipe actually closes.
  
  ::DuplicateHandle(::GetCurrentProcess(),			// Source process
    hwPipe,		        // Handle to duplicate
    ::GetCurrentProcess(),   // Destination process
    &hwPipe2,	            // New handle, used as stderr by child 
    0,                     // New access flags - ignored since DUPLICATE_SAME_ACCESS
    true,                  // It's inheritable
    DUPLICATE_SAME_ACCESS);
  
  ::CreatePipe(&hrPipe,&m_hwPipeTemp,&saPipe,10240);
  

  // Create new output read handle and the input write handles, setting
  // the Properties to FALSE. Otherwise, the child inherits the
  // properties and, as a result, non-closeable handles to the pipes
  // are created.
  DuplicateHandle(GetCurrentProcess(),m_hrPipeTemp,
                       GetCurrentProcess(),
                       &m_hrPipe, // Address of new handle.
                       0,FALSE, // Make it uninheritable.
                       DUPLICATE_SAME_ACCESS);

  DuplicateHandle(GetCurrentProcess(),m_hwPipeTemp,
                       GetCurrentProcess(),
                       &m_hwPipe, // Address of new handle.
                       0,FALSE, // Make it uninheritable.
                       DUPLICATE_SAME_ACCESS);

  // Close inheritable copies of the handles we do not want to be inherited:
  CloseHandle(m_hrPipeTemp);
  CloseHandle(m_hwPipeTemp);


  memset(&si, 0, sizeof(si));
  si.cb = sizeof(si);
  
  si.hStdOutput = hwPipe;
  si.hStdError =  hwPipe2;
  si.hStdInput =  hrPipe;
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  si.wShowWindow = SW_SHOW;
  
  LPCTSTR pszDir;
  if(m_strDir.empty()){
    pszDir=NULL; // current directory
  } else {
    pszDir=m_strDir;
  }
  
  PROCESS_INFORMATION pi;
  String strCmd(pszCmdline);

  String strOrigpath;
  if(!m_strPath.empty()){
	  int nSize=GetEnvironmentVariable(_T("PATH"), NULL, 0);
	  if(nSize>0){
      GetEnvironmentVariable(_T("PATH"),strOrigpath.GetBuffer(nSize),nSize);
      strOrigpath.ReleaseBuffer();
      SetEnvironmentVariable(_T("PATH"),m_strPath);
    }
  }

  bool rc=(TRUE==::CreateProcess(NULL,strCmd.GetBuffer(),NULL,NULL,true,DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,NULL,pszDir,&si,&pi));

  if(!m_strPath.empty()){
    SetEnvironmentVariable(_T("PATH"),strOrigpath);
  }

  m_nErr=GetLastError();

  strCmd.ReleaseBuffer();
  
  if(rc){
    m_idProcess=pi.dwProcessId;
    m_hProcess=pi.hProcess;
    if(m_bVerbose){
      Output(String::SFormat(_T("*** Process %d created \"%s\"\n"),m_idProcess,pszCmdline));
    }
    TRACE(String::SFormat(_T("Process %d created \"%s\"\n"),m_idProcess,pszCmdline));
    m_nExitCode=STILL_ACTIVE;
    CloseHandle(pi.hThread);
  } else {
    m_idProcess=0;
    if(m_bVerbose){
      Output(String::SFormat(_T("*** Failed to create process \"%s\" %s\n"),pszCmdline,(LPCTSTR)ErrorString()));
    }
    TRACE(String::SFormat(_T("Failed to create process \"%s\" %s\n"),pszCmdline,(LPCTSTR)ErrorString()));
    m_nExitCode=GetLastError();
    CloseHandle(m_hrPipe);m_hrPipe=INVALID_HANDLE_VALUE;
    CloseHandle(m_hwPipe);m_hwPipe=INVALID_HANDLE_VALUE;
  }
  
  CloseHandle(hrPipe);
  CloseHandle(hwPipe);
  CloseHandle(hwPipe2);
  
  return rc;
  
}

void CSubprocess::ThreadFunc()
{

  TRACE(_T("Reading from process %d\n"),m_idProcess);
  
  DWORD dwAvail;
  
  while (!m_bKillThread && m_pfnContinue(m_pContinuationFuncParam) && ::PeekNamedPipe(m_hrPipe, NULL, 0, 0, &dwAvail, NULL)){
//TRACE(_T("P%d\n"),dwAvail);
    if(dwAvail){
      dwAvail=MIN(dwAvail,80); // Read a maximum of 80 characters at a time
      DWORD dwRead;
      char *buf=new char[dwAvail+1];
//TRACE(_T("R%d\n"),dwAvail);
      if(!::ReadFile(m_hrPipe, buf, dwAvail, &dwRead, NULL)){
        TRACE(_T("ReadFile returns false\n"));
        delete [] buf;
        break;
      }
      buf[dwRead]='\0';
      Output(String::CStrToUnicodeStr(buf));
      delete [] buf;
    } else if(!ProcessAlive()){
      TRACE(_T("m_bThreadTerminated=%d\n"),m_bThreadTerminated);
      break;
    } else {
      CeCosThreadUtils::Sleep(250);
    }
  }

  DWORD dwExitCode;
  ::GetExitCodeProcess(m_hProcess, &dwExitCode);
  m_nExitCode=dwExitCode;
  
#ifdef _DEBUG
  String str;
  switch(dwExitCode){
    case STILL_ACTIVE:
      str=_T("still alive");
      if(m_bKillThread){
        str+=_T(" - requested to stop reading");
      }
      break;
    case PROCESS_KILL_EXIT_CODE:
      str=_T("killed");
      break;
    default:
      str.Format(_T("terminated rc=%d"),dwExitCode);
      break;
  }
  TRACE(_T("Finished reading from process %d (%s)\n"),m_idProcess,(LPCTSTR)str);
#endif

  CloseHandle(m_hrPipe);m_hrPipe=INVALID_HANDLE_VALUE;
  CloseHandle(m_hwPipe);m_hwPipe=INVALID_HANDLE_VALUE;
  
  if(m_bAutoDelete){
    m_bThreadTerminated=true; // or else the dtor will block
    delete this;
  }
}

#else // UNIX

bool CSubprocess::CreateProcess(LPCTSTR pszCmdline)
{
  m_idProcess=0;
  int fdchild=-1; // the file descriptor for the child (slave) half of the pseudo-tty pair

  // Get a free /dev/ptyp0 (master) and /dev/ttyp0 (slave) tty pair
  String strMasterTty,strChildTty;
  for(unsigned int c=0;c<64;c++){
    strMasterTty.Format("/dev/pty%c%x",'p'+c/16,c%16);
    
    m_tty=open(strMasterTty, O_RDWR | O_NOCTTY);
    if (-1!=m_tty) { 
      strChildTty.Format("/dev/tty%c%x",'p'+c/16,c%16);	
      
      fdchild = open(strChildTty, O_RDWR);
      if (-1==fdchild) {
        close(m_tty);
        m_tty=fdchild=-1;
      } else {
        VTRACE("opened %s - fd=%d\n",(LPCTSTR)strMasterTty,m_tty);
        break;
      }
    }
  }

  if(-1==m_tty){
    ERROR(_T("Failed to get a pty\n"));
    return false;
  }  
  
  TRACE(_T("Master pty %s (fd %d) slave pty %s (fd %d)\n"),(LPCTSTR)strMasterTty,m_tty,(LPCTSTR)strChildTty,fdchild);

  m_idProcess=fork();
  
  switch (m_idProcess) {
    // Fork failed
    case -1:
      TRACE(_T("Failed to create process - %s\n"),strerror(errno));
      m_idProcess=0;
      break;
    case 0:
      // Process is created (we're the child)
      {
        // Close all descriptors except the slave side of the pseudo-terminal
        for (int fd = 0; fd < (int) sysconf(_SC_OPEN_MAX); fd++) {
          if(fd!=fdchild){
            close(fd);
          }
        }
        setsid();
        
        dup2(fdchild, 0);
        dup2(fdchild, 1);
        dup2(fdchild, 2);
        
        close(fdchild);

        if(!m_strDir.empty()){
          if(0!=chdir(m_strDir)){
            if(m_bVerbose){
              fprintf(stderr,_T("*** Failed to change directory to %s\n"),(LPCTSTR)m_strDir);
            }
            exit (5);
          }
        }
        if(m_bVerbose){
          fprintf(stderr,_T("*** Process %d created \"%s\"\n"),m_idProcess,pszCmdline);
        }

        StringArray ar;
        int argc=String(pszCmdline).Chop(ar,_TCHAR(' '),true);
        TCHAR **argv=new TCHAR *[1+argc];
        for(int i=0;i<argc;i++){
          argv[i]=new TCHAR[1+strlen(ar[i])];  
          strcpy(argv[i],ar[i]);
        }
        argv[argc]=0;
        if(!m_strPath.empty()){
          _tputenv(String::SFormat(_T("PATH=%s"),(LPCTSTR)m_strPath));
        }
        _texecvp(argv[0], argv);  
      }

      fprintf(stderr,"exec error - %s\n",strerror(errno));
      exit(4);

    default:
      // Process is created (we're the parent)
      TRACE(_T("Closing fd %d\n"),fdchild);
      close(fdchild);
      TRACE(_T("Forked to create process %s - process id <%d>\n"), pszCmdline, m_idProcess);
      break;
  }
  return 0!=m_idProcess;
}

void CSubprocess::ThreadFunc()
{
  if(!CreateProcess(m_strCmd)){
    ERROR(_T("Failed to create process for %s\n"),(LPCTSTR)m_strCmd);
  } else {  
    fcntl(m_tty,F_SETFL,O_NONBLOCK);
    int rc;
    do {
      TCHAR buf[4096];
      rc=read(m_tty, buf, sizeof(buf)-1);
      if(rc>=0){
        buf[rc]='\0';
      }
      switch(rc){
        case -1:
          if(EAGAIN==errno){
            CeCosThreadUtils::Sleep(250);
          } else {
              goto Done;
          }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -