📄 subprocess.cpp
字号:
//####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 _WIN32bool 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 // UNIXbool 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 + -