diff host/tools/Utils/win32/SubProcess.cpp @ 76:435cced73e2f ecos-v1_3_1-release

eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
author jlarmour
date Tue, 28 Mar 2000 14:10:45 +0000
parents
children
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/host/tools/Utils/win32/SubProcess.cpp
@@ -0,0 +1,542 @@
+//####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 "subprocess.h"
+
+const UINT CSubprocess::PROCESS_KILL_EXIT_CODE=0xCCFFCCFF;
+
+CSubprocess::CSubprocess(bool bVerbose):
+  m_dwParentThreadId(0),
+  m_pstrOutput(0),
+  m_idProcess(0),
+  m_bThreadRunning(false),
+  m_bVerbose(bVerbose),
+  m_dwExitCode(0xffffffff),
+  m_bAutoDelete(false),
+  m_hThread(0),
+  m_pfnLogfunc(0),
+  m_pLogparam(0),
+  m_hwndParent(0)
+{
+}
+
+// Run (non-blocking and discarding output)
+/* static */ unsigned int CSubprocess::Run(LPCTSTR pszCmd,LPCTSTR pszDir)
+{
+  CSubprocess *psp=new CSubprocess(false);
+  psp->m_bAutoDelete=true;
+  String strOutput;
+  return psp->Run(strOutput,pszCmd,pszDir);
+}
+
+unsigned int CSubprocess::Run(LogFunc *pfnLog,void * pLogparam, LPCTSTR pszCmd,LPCTSTR pszDir)
+{
+  unsigned int rc;
+  if(m_bThreadRunning){
+    rc=0;
+  } else {
+    m_pfnLogfunc=pfnLog;
+    m_pLogparam=pLogparam;
+    m_strCmd=pszCmd;
+    m_strDir=pszDir;
+    rc=CreateProcess();
+    if(rc){
+      ThreadFunc();
+    }
+  }
+  return rc;
+}
+
+#ifdef _WIN32
+// Non-blocking: WM_SUBPROCESS messages are posted to HWND supplied as argument
+unsigned int CSubprocess::Run(HWND hwndParent,LPCTSTR pszCmd,LPCTSTR pszDir)
+{
+  unsigned int rc;
+  if(m_bThreadRunning){
+    rc=0;
+  } else {
+    m_hwndParent=hwndParent;
+    m_strCmd=pszCmd;
+    m_strDir=pszDir;
+    rc=CreateProcess();
+    if(rc){
+      DWORD dwID;
+      m_bThreadRunning=true;
+      m_hThread=::CreateThread(NULL,0,ThreadFunc,this,0,&dwID);
+    }
+  }
+  return rc;
+}
+
+// Non-blocking: WM_SUBPROCESS messages are posted to thread supplied as argument
+unsigned int CSubprocess::Run(DWORD dwParentThreadId,LPCTSTR pszCmd,LPCTSTR pszDir)
+{
+  unsigned int rc;
+  if(m_bThreadRunning){
+    rc=0;
+  } else {
+    m_dwParentThreadId=dwParentThreadId;
+    m_strCmd=pszCmd;
+    m_strDir=pszDir;
+    rc=CreateProcess();
+    if(rc){
+      DWORD dwID;
+      m_bThreadRunning=true;
+      m_hThread=::CreateThread(NULL,0,ThreadFunc,this,0,&dwID);
+    }
+  }
+  return rc;
+}
+#endif
+
+// Blocking: output is placed in string supplied as argument
+unsigned int CSubprocess::Run(String &strOutput,LPCTSTR pszCmd,LPCTSTR pszDir)
+{
+  strOutput=_T("");
+  m_strCmd=pszCmd;
+  m_strDir=pszDir;
+  m_pstrOutput=&strOutput;
+  unsigned int rc=CreateProcess();
+  if(rc){
+    ThreadFunc();
+  }
+  return rc;
+}
+
+CSubprocess::~CSubprocess()
+{
+  Kill();
+  
+  if(m_hThread){ // running non-blocking and thread started (but possibly already finished)
+    if(WAIT_TIMEOUT==WaitForSingleObject(m_hThread,1000)){
+      // In general this is bad news, but we can't allow the thread to live on if
+      // the class object is about to be deleted.
+      if(m_bVerbose){
+        Output(_T("*** CSubprocess: Forcibly terminating thread\n"));
+      }
+      ::TerminateThread(m_hThread,0);
+    }
+    ::CloseHandle(m_hThread);
+  }
+}
+
+#ifdef _WIN32
+unsigned int CSubprocess::CreateProcess()
+{
+  
+  STARTUPINFO   si;                    // For CreateProcess call
+  HANDLE        hrPipe,hwPipe,hwPipe2;
+  // Create the anonymous pipe
+  
+  SECURITY_ATTRIBUTES saPipe;          // Security for anonymous pipe
+  saPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
+  saPipe.lpSecurityDescriptor = NULL;
+  saPipe.bInheritHandle = true;
+  
+  ::CreatePipe(&m_hrPipe,&hwPipe,&saPipe,80);
+  
+  // 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_hwPipe,&saPipe,80);
+  
+  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=0;
+  if(m_strDir.GetLength()){
+    pszDir=m_strDir; // NULL = start in current
+  }
+  
+  PROCESS_INFORMATION pi;
+  unsigned int rc=(::CreateProcess(NULL,    // Application name
+    m_strCmd.GetBuffer(m_strCmd.GetLength()),  // Full command line for child
+    NULL,    // Process security descriptor
+    NULL,    // Thread security descriptor
+    true,    // Inherit handles? Also use if STARTF_USESTDHANDLES
+    // Creation flags of 
+    // CREATE_NEW_PROCESS_GROUP so we can use 
+    // GenerateConsoleCtrlEvent to 
+    // terminate the child
+    DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
+    NULL,    // Inherited environment address
+    pszDir,&si,&pi) ? pi.dwProcessId : 0);
+  
+  m_strCmd.ReleaseBuffer();
+  String strMsg;
+  
+  if(rc){
+    m_idProcess=pi.dwProcessId;
+    m_hProcess=pi.hProcess;
+    strMsg.Format(_T("*** Process %d created \"%s\"\n"),m_idProcess,(LPCTSTR)m_strCmd);
+    m_dwExitCode=STILL_ACTIVE;
+  } else {
+    strMsg.Format(_T("*** Failed to create process \"%s\"\n"),(LPCTSTR)m_strCmd);
+    m_dwExitCode=GetLastError();
+  }
+  
+  if(m_bVerbose){
+    Output(strMsg);
+  }
+  
+  ::CloseHandle(hrPipe);
+  ::CloseHandle(hwPipe);
+  ::CloseHandle(hwPipe2);
+  ::CloseHandle(pi.hThread);
+  
+  return rc;
+  
+}
+#else // UNIX
+unsigned int CSubprocess::CreateProcess()
+{
+  int pipe_ends_w[2];
+  if (pipe(pipe_ends_w) < 0 ) { 
+    Log(_T("Failed to create pipe_ends_w - %s\n"),strerror(errno));
+  } else {
+    int pipe_ends_r[2];
+    if (pipe(pipe_ends_r) < 0 ) { 
+      Log(_T("Failed to create pipe_ends_r - %s\n"),strerror(errno));
+    } else {
+      int pid=fork();
+      
+      switch (pid) {
+        // Fork failed
+        case -1:
+          Log(_T("Failed to create gdb process - %s\n"),strerror(errno));
+          pid=0;
+          break;
+        case 0:
+          // Process is created (we're the child)
+          // No point in outputting except via output streams 
+        
+          // Input to child process
+          if (dup2(pipe_ends_w[0], 0) < 0) {
+            _ftprintf(stderr,(_T("dup2 error\n"));
+            exit(1);
+          }
+        
+          // Output from process
+          if (dup2(pipe_ends_r[1], 2) < 0) {
+            _ftprintf(stderr,(_T("dup2 error\n"));
+            exit(2);
+          }
+          if (dup2(pipe_ends_r[1], 1) < 0) {
+            _ftprintf(stderr,(_T("dup2 error\n"));
+            exit(3);
+          }
+          setvbuf(stdout,0,_IONBF,0);
+          setvbuf(stderr,0,_IONBF,0);
+          if(m_bVerbose){
+            strMsg.Format(_T("*** Process %d created \"%s\"\n"),pid,(LPCTSTR)m_strCmd);
+          }
+
+          StringArray ar;
+          int argc=m_strCmd.Chop(ar,_TCHAR(' '),true);
+          TCHAR **argv=new TCHAR *[1+argc];
+          int i;
+          for(i=0;i<argc;i++){
+            argv[i]=ar[i];  
+          }
+          argv[i]=0;
+          _texecvp(argv[0], argv);
+          delete [] argv;
+          _ftprintf(stderr,(_T("exec error - %s\n"),strerror(errno));
+          
+          exit(4);
+
+        default:
+          // Process is created (we're the parent)
+
+          TRACE(_T("Forked to create gdb process %s - pid is <%d>\n"), pszCmdline, pid);
+          if (fcntl(pipe_ends_r[0], F_SETFL, O_NONBLOCK) <0) {
+            Log(_T("Couldn't set pipe non-blocking - %s\n"),strerror(errno));
+          } else {
+            m_rPipeHandle=(void *)pipe_ends_r[0];
+            m_wPipeHandle=(void *)pipe_ends_w[1];
+          }
+          break;
+      }
+      close (pipe_ends_r[0]);
+      close (pipe_ends_r[1]);
+    }
+    close (pipe_ends_w[0]);
+    close (pipe_ends_w[1]);
+  }
+  return pid;
+}
+#endif
+
+DWORD CSubprocess::ThreadFunc()
+
+{
+  
+  DWORD dwAvail;
+  
+  while (::PeekNamedPipe(m_hrPipe, NULL, 0, 0, &dwAvail, NULL)){
+    if(dwAvail){
+      DWORD dwRead;
+      char *buf=new char[dwAvail+1];
+      if(!::ReadFile(m_hrPipe, buf, dwAvail, &dwRead, NULL)){
+        delete [] buf;
+        break;
+      }
+      buf[dwRead]='\0';
+      Output(String::CStrToUnicodeStr(buf));
+      delete [] buf;
+    } else {
+      DWORD dw;
+      ::GetExitCodeProcess(m_hProcess, &dw);
+      
+      if(STILL_ACTIVE!=dw){
+        break;
+      }
+      
+      Sleep(250);
+    }
+  }
+  
+  ::GetExitCodeProcess(m_hProcess, &m_dwExitCode);
+  String strMsg;
+  if(m_bVerbose){
+    if(PROCESS_KILL_EXIT_CODE==m_dwExitCode){
+      strMsg.Format(_T("\n*** Process %d killed\n"),m_idProcess);
+    } else {
+      strMsg.Format(_T("\n*** Process %d terminated (rc=%d)\n"),m_idProcess,m_dwExitCode);
+    }
+    Output(strMsg);
+  }
+
+  if(m_dwParentThreadId || m_hwndParent){
+    Post(0);
+  }
+  m_idProcess=0;
+
+  ::CloseHandle(m_hrPipe);
+  ::CloseHandle(m_hwPipe);
+  ::CloseHandle(m_hProcess);
+  m_bThreadRunning=false;
+  DWORD rc=m_dwExitCode; // protect against auto-delete
+
+  if(m_bAutoDelete){
+    delete this;
+  }
+
+  return rc;
+
+}
+
+bool CSubprocess::Kill()
+{
+  if(0!=m_idProcess){
+    //TRACE(_T("*** Killing process %d(ctrl/c)\n"),m_idProcess);
+    //::GenerateConsoleCtrlEvent( CTRL_C_EVENT, m_idProcess);
+    //if(WAIT_TIMEOUT==WaitForSingleObject(m_hThread,1000)){
+      //TRACE(_T("*** Killing process %d(ctrl/break)\n"),m_idProcess);
+      //::GenerateConsoleCtrlEvent( CTRL_BREAK_EVENT, m_idProcess);
+      //if(WAIT_TIMEOUT==WaitForSingleObject(m_hThread,1000)){
+        if(m_bVerbose){
+          String strMsg;
+          strMsg.Format(_T("*** Killing process %d (TerminateProcess)\n"),m_idProcess);
+          Output(strMsg);
+        }
+        ::TerminateProcess(m_hProcess,PROCESS_KILL_EXIT_CODE);
+      //}
+    //}
+  }
+  return 0==m_idProcess;
+}
+
+void CSubprocess::Output (LPCTSTR psz)
+{
+  if(m_dwParentThreadId || m_hwndParent){
+    TCHAR *pszCopy=new TCHAR[1+_tcslen(psz)];
+    _tcscpy(pszCopy,psz);
+    Post((LPARAM)pszCopy);
+  } else if (m_pfnLogfunc) {
+    m_pfnLogfunc(m_pLogparam,psz);
+  } else {
+    assert(NULL!=m_pstrOutput);
+    (*m_pstrOutput)+=psz;
+  }
+}
+
+
+void CSubprocess::Post(LPARAM lParam)
+{
+  if(m_hwndParent){
+    ::PostMessage(m_hwndParent,WM_SUBPROCESS,(WPARAM)this,lParam);
+  } else {
+    assert(m_dwParentThreadId);
+    ::PostThreadMessage(m_dwParentThreadId,WM_SUBPROCESS,(WPARAM)m_idProcess,lParam);
+  }
+}
+
+void CSubprocess::CygKill()
+{
+  
+  // Map win32 pids to cygwin format
+  PtrArray map;
+  
+  int nKillCount;
+  int nGid=-1;
+  do {
+    nKillCount=0;
+    // Execute a "ps -l" to find out what's out there in Cygwin land
+    CSubprocess sp;
+    String strOutput;
+    sp.Run(strOutput,_T("ps -l"));
+    if(0==strOutput.GetLength()){
+      if(m_bVerbose){
+        Output(_T("*** Warning: could not run ps to effect a Cygkill()\n"));
+      }
+    } else {
+      // Walk through the lines by line and extract what we need
+      for(LPCTSTR psz=-1+(LPCTSTR)strOutput;psz;psz=_tcschr(psz,_TCHAR('\n'))){
+        psz++; // move over the '\n'
+        CygProcessInfo cpi;
+        if(0==_tcsstr(psz,_T("ps.exe")) && 4==_stscanf(2+psz,_T("%d %d %d %d"),&cpi.nPid,&cpi.nPpid,&cpi.nPgid,&cpi.nWinpid) && cpi.nWinpid){
+          for(unsigned int i=0;i<map.size();i++){
+            if(((CygProcessInfo *)map[i])->nWinpid==cpi.nWinpid){
+              break;
+            }
+          }
+
+          if(i==map.size()){
+            // No existing match - add:
+            map.push_back(new CygProcessInfo(cpi));
+            if((int)m_idProcess==cpi.nWinpid){
+              nGid=cpi.nPgid;
+            }
+          }
+        }
+      }
+      
+      // Kill everything in the same group as our process
+      if(-1!=nGid){
+        for (unsigned int i=0;i<map.size();i++){
+          CygProcessInfo *pcpi=(CygProcessInfo *)map[i];
+          if(pcpi->nPgid==nGid){
+            // Check that the process exists
+            HANDLE hProcess=OpenProcess(PROCESS_TERMINATE,false,pcpi->nWinpid);
+            if(hProcess){
+              CloseHandle(hProcess);
+              String strCmd,strOut;
+              strCmd.Format(_T("kill %d"),pcpi->nPid);
+              pcpi->nPgid=-pcpi->nPgid; // prevent this being done more than once
+              CSubprocess::Run(strCmd);
+              nKillCount++;
+            }
+          }
+        }
+      }
+
+    }
+  } while (nKillCount>0);
+
+  // Now we've sent a kill to every process.  Go back and check they are really dead (use windows API this time)
+  for(unsigned int i=0;i<map.size();i++){
+    CygProcessInfo *pcpi=(CygProcessInfo *)map[i];
+    if(abs(pcpi->nPgid)==nGid){
+      HANDLE hProcess=OpenProcess(PROCESS_TERMINATE,false,pcpi->nWinpid);
+      if(hProcess){
+        ::TerminateProcess(hProcess,0x7fffffff);
+        CloseHandle(hProcess);
+      }
+    }
+  }
+
+  for(i=0;i<map.size();i++){
+    delete (CygProcessInfo *)map[i];
+  }
+}
+
+void CSubprocess::Send(LPCTSTR str)
+{
+  int nLength=_tcslen(str);
+  char *psz=new char[1+nLength];
+#ifdef _UNICODE
+  WideCharToMultiByte(CP_ACP, 0, str, -1, psz, 1+nLength, NULL, NULL);
+#else
+  strcpy(psz,str);
+#endif
+  
+  DWORD dwWritten;
+  ::WriteFile(m_hwPipe,psz,nLength,&dwWritten,0);
+  delete [] psz;
+}
+
+
+
+void CSubprocess::CloseInput()
+{
+  ::CloseHandle(m_hwPipe);
+}
+