📄 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 _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 + -