linux c redirect 重定向
用execvp实现时,运行adb,如果adb 服务没有启动,会启动adb服务,启动adb服务时,pipe返回的管道在读的时候堵塞了。
查看了popen的源码,发现popen是用sh -c来执行的,避免了这个问题
不知道sh -c做了些什么操作,使得popen可以避免这个问题
代码如下:
1 #ifndef __RG_REDIRECT_H 2 #define __RG_REDIRECT_H 3 4 5 //#ifdef _LINUX //for linux version 6 7 #include <string> 8 9 #ifdef _LINUX 10 #include <signal.h> 11 #endif 12 13 #define WAIT_FOREVER 0 14 15 typedef void (*pReDirectCallBack)(const char* strOutput, bool bOver); 16 17 class CRGReDirect 18 { 19 public: 20 CRGReDirect(); 21 ~CRGReDirect(); 22 23 public: 24 //同步方式运行ADB命令,会阻塞。命令结果保存在strResult里面返回 25 //strCmd是需要执行的ADB命令。例如:adb_path devices 26 int RunCmdSync(std::string& strResult, const std::string& strCmd); 27 int RunCmdSync(std::string& strResult, const char *szCmd); 28 29 30 //nTimeOut单位是秒 31 int RunCmdWithTimeOut(std::string& strResult, const std::string& strCmd, int nTimeOut = WAIT_FOREVER); 32 int RunCmdWithTimeOut(std::string& strResult, const char *szCmd, int nTimeOut = WAIT_FOREVER); 33 34 //异步方式执行ADB命令,不会阻塞,直接返回线程的pid(失败返回-1) 35 //strCmd是需要执行的ADB命令。例如:adb_path devices 36 //ADB运行完成之后主动条用CallBack函数pFunc 37 //成功返回非零,失败返回-1 38 int RunCmdAsync(pReDirectCallBack pFunc, const std::string&strCmd); 39 int RunCmdAsync(pReDirectCallBack pFunc, const char *szCmd); 40 // const char* GetCmdResultString(void) const { return m_strResult.c_str(); } 41 42 protected: 43 int RunCmd(pReDirectCallBack pFunc = NULL); 44 45 void CancelCommand(); 46 47 #ifdef _WIN32 48 HANDLE m_hTimer; 49 HANDLE m_hTimerQueue; 50 HANDLE m_hChildProcess; 51 52 HANDLE m_hCmdThread; 53 static DWORD WINAPI RunProcess(void *arg); 54 static VOID CALLBACK HandleTimeOut( PVOID lpParameter, BOOLEAN TimerOrWaitFired); 55 #else 56 int SetTimer(int nSeconds, int nTimerID); 57 int KillTimer(timer_t nTimerID); 58 59 timer_t m_timer_id; 60 pthread_t m_thread_id; 61 static void* RunProcess(void* arg); 62 static void HandleTimeOut(sigval_t v); 63 64 pid_t *m_childpid; /* ptr to array allocated at run-time */ 65 int m_maxfd; /* from our open_max(), {Prog openmax} */ 66 FILE* m_hChildProcess; 67 68 int open_max(void); 69 FILE * rgpopen(const char *cmdstring, const char *type); //模拟popen实现的版本 70 int rgpkill(FILE* fp); //根据文件指针关掉相应的进程 71 int rgpclose(FILE *fp); //关闭文件指针 72 #endif 73 74 private: 75 std::string m_strResult; 76 std::string m_strCmd; 77 pReDirectCallBack m_adbCB; 78 volatile bool m_bAbort; 79 }; 80 81 //#endif 82 83 #endif
#ifndef _WIN32 #include <unistd.h> #include <signal.h> #include <time.h> #include <pthread.h> // Compile and link with -pthread. #include <cstring> #include <cstdlib> #include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> #include <errno.h> #else #include <Windows.h> #endif #include "RGReDirect.h" #include <cassert> #include <cstdio> #define RG_TIMER_ID 51
std::string Trim(const std::string& strIn)
{
std::string strMatch = " \t\r\n";
size_t i = strIn.find_first_not_of(strMatch);
size_t j = strIn.find_last_not_of(strMatch);
if (i != std::string::npos)
return strIn.substr(i, j-i+1);
return "";
}
CRGReDirect::CRGReDirect() : m_adbCB(NULL) #ifdef _WIN32 , m_hChildProcess(NULL) , m_hCmdThread(NULL) , m_hTimer(NULL) , m_hTimerQueue(NULL) #else , m_thread_id(0) , m_timer_id(0) , m_childpid(NULL) , m_maxfd(0) , m_hChildProcess(NULL) #endif , m_bAbort(false) { } CRGReDirect::~CRGReDirect() { #ifdef _WIN32 if (m_hCmdThread) { WaitForSingleObject(m_hCmdThread, INFINITE); CloseHandle(m_hCmdThread); } if (m_hTimer) DeleteTimerQueueTimer(m_hTimerQueue, m_hTimer, NULL); if (m_hTimerQueue) DeleteTimerQueue(m_hTimerQueue); #else if (m_thread_id) pthread_join(m_thread_id, NULL); if(m_timer_id) timer_delete(m_timer_id); if(m_childpid) free(m_childpid); #endif } int CRGReDirect::RunCmd(pReDirectCallBack pFunc /* = NULL */) { m_bAbort = false; m_strResult.clear(); #ifdef _WIN32 std::string strTmpCmd = m_strCmd; HANDLE hChildStdoutRd, hChildStdoutWr, hStdout; SECURITY_ATTRIBUTES saAttr; // Set the bInheritHandle flag so pipe handles are inherited. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Get the handle to the current STDOUT. hStdout = GetStdHandle(STD_OUTPUT_HANDLE); // Create a pipe for the child process's STDOUT. if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) return -1; // Ensure the read handle to the pipe for STDOUT is not inherited. SetHandleInformation( hChildStdoutRd, HANDLE_FLAG_INHERIT, 0); char *szCmdline = new char[strTmpCmd.size() + 1]; memset(szCmdline, 0, strTmpCmd.size() + 1); strcpy(szCmdline, strTmpCmd.c_str()); PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; // Set up members of the PROCESS_INFORMATION structure. ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); // Set up members of the STARTUPINFO structure. ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = hChildStdoutWr; siStartInfo.hStdOutput = hChildStdoutWr; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; // Create the child process. BOOL bFuncRetn = CreateProcess(NULL, szCmdline, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited CREATE_NO_WINDOW, // creation flags NULL, // use parent's environment NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer &piProcInfo); // receives PROCESS_INFORMATION if (bFuncRetn == 0) { delete [] szCmdline; return -1; } else { m_hChildProcess = piProcInfo.hProcess; CloseHandle(piProcInfo.hThread); const int BUFSIZE = 4096; DWORD dwRead; char chBuf[BUFSIZE] = {0}; // Close the write end of the pipe before reading from the // read end of the pipe. if (!CloseHandle(hChildStdoutWr)) { delete [] szCmdline; return -1; } // Read output from the child process while (m_bAbort == false) { if( !ReadFile( hChildStdoutRd, chBuf, BUFSIZE, &dwRead, NULL) || dwRead == 0) break; chBuf[dwRead] = '\0'; if(pFunc) pFunc(chBuf, false); else m_strResult += chBuf; } CloseHandle(hChildStdoutRd); if(m_bAbort == false) //非终止进程,则自然关闭 { CloseHandle(m_hChildProcess); m_hChildProcess = NULL; } if(pFunc) pFunc("", true); delete [] szCmdline; return 0; } #else char line[1024] = {0}; std::string strTmpCmd = m_strCmd + " 2>&1"; //重定向stderr至stdout if((m_hChildProcess = rgpopen(strTmpCmd.c_str(), "r")) == NULL) { fprintf(stderr, "rgpopen error\n"); return -1; } while(m_bAbort == false) { if(fgets(line, 1024, m_hChildProcess) == NULL) break; if(pFunc) pFunc(line, false); else m_strResult += line; } if(pFunc) pFunc("", true); if (m_bAbort == false) //非终止进程,则自然关闭 { rgpclose(m_hChildProcess); m_hChildProcess = NULL; } return 0; #endif } int CRGReDirect::RunCmdSync(std::string& strResult, const std::string& strCmd) { return RunCmdSync(strResult, strCmd.c_str()); } int CRGReDirect::RunCmdSync(std::string& strResult, const char *szCmd) { m_strCmd = szCmd; m_adbCB = NULL; int nRet = RunCmd(m_adbCB); strResult = m_strResult; return nRet; } int CRGReDirect::RunCmdAsync(pReDirectCallBack pFunc, const std::string&strCmd) { return RunCmdAsync(pFunc, strCmd.c_str()); } int CRGReDirect::RunCmdAsync(pReDirectCallBack pFunc, const char *szCmd) { assert(pFunc); m_adbCB = pFunc; m_strCmd = szCmd; #ifdef _WIN32 DWORD dwThread; HANDLE hThread = CreateThread(NULL, 0, RunProcess, this, 0, &dwThread); if (hThread) { m_hCmdThread = hThread; return 0; } else { return -1; } #else pthread_t pid; if (pthread_create(&pid, NULL, RunProcess, this) == 0) { m_thread_id = pid; return 0; } else { return -1; } #endif } int CRGReDirect::RunCmdWithTimeOut(std::string& strResult, const std::string& strCmd, int nTimeOut) { return RunCmdWithTimeOut(strResult, strCmd.c_str(), nTimeOut); } int CRGReDirect::RunCmdWithTimeOut(std::string& strResult, const char* szCmd, int nTimeOut) { if(nTimeOut == WAIT_FOREVER) { return RunCmdSync(strResult, szCmd); } m_adbCB = NULL; m_strCmd = szCmd; #ifdef _WIN32 DWORD dwThread; HANDLE hThread = CreateThread(NULL, 0, RunProcess, this, 0, &dwThread); if (hThread) { m_hCmdThread = hThread; m_hTimerQueue = CreateTimerQueue(); CreateTimerQueueTimer(&m_hTimer, m_hTimerQueue, HandleTimeOut, this, nTimeOut * 1000, 0, 0); WaitForSingleObject(m_hCmdThread, INFINITE); CloseHandle(m_hCmdThread); m_hCmdThread = NULL; DeleteTimerQueueTimer(m_hTimerQueue, m_hTimer, NULL); DeleteTimerQueue(m_hTimerQueue); m_hTimer = m_hTimerQueue = NULL; strResult = m_strResult; LOG("%s\n", strResult); return 0; } else { return -1; } #else pthread_t pid; if (pthread_create(&pid, NULL, RunProcess, this) == 0) { m_thread_id = pid; SetTimer(nTimeOut, RG_TIMER_ID); pthread_join(m_thread_id, NULL); m_thread_id = 0; KillTimer(m_timer_id); m_timer_id = 0; strResult = m_strResult; return 0; } else { return -1; } #endif } #ifdef _WIN32 void CRGReDirect::HandleTimeOut(PVOID lpParameter, BOOLEAN TimerOrWaitFired) #else void CRGReDirect::HandleTimeOut(sigval_t v) #endif { #ifdef _WIN32 CRGReDirect* pThis = (CRGReDirect*)lpParameter; #else CRGReDirect* pThis = (CRGReDirect*)(v.sival_ptr); #endif if (pThis) { printf("cancel command on timeout\n"); pThis->CancelCommand(); } #ifdef _DEBUG printf("TimeOut on exit.\n"); #endif } void CRGReDirect::CancelCommand() { m_bAbort = true; #ifdef _WIN32 if (m_hChildProcess) //进程未退出,强行终止 { TerminateProcess(m_hChildProcess, 1); CloseHandle(m_hChildProcess); m_hChildProcess = NULL; } #else if (m_hChildProcess) { rgpkill(m_hChildProcess); m_hChildProcess = NULL; } #endif } #ifdef _WIN32 DWORD WINAPI CRGReDirect::RunProcess(void* arg) #else void* CRGReDirect::RunProcess(void* arg) #endif { CRGReDirect* pThis = (CRGReDirect *)arg; assert(pThis); pThis->RunCmd(pThis->m_adbCB); #ifdef _WIN32 return 0; #else return (void *)0; #endif } #ifndef _WIN32 int CRGReDirect::SetTimer(int nSeconds, int nTimerID) { timer_t tid; struct sigevent se; memset(&se, 0, sizeof(se)); se.sigev_notify = SIGEV_THREAD; se.sigev_notify_function = HandleTimeOut; se.sigev_value.sival_ptr = this; if(timer_create(CLOCK_REALTIME, &se, &tid) < 0) { #ifdef _DEBUG perror("timer_create:"); #endif return -1; } struct itimerspec ts, ots; ts.it_value.tv_sec = nSeconds; ts.it_value.tv_nsec = 0; ts.it_interval.tv_sec = 0; ts.it_interval.tv_nsec = 0; if(timer_settime(tid, 0, &ts, &ots) < 0) { #ifdef _DEBUG perror("timer_settime:"); #endif return -1; } m_timer_id = tid; return 0; } int CRGReDirect::KillTimer(timer_t nTimerID) { return timer_delete(nTimerID); } #ifdef _ARM #define SHELL "/system/bin/sh" #else #define SHELL "/bin/sh" #endif int CRGReDirect::open_max() { struct rlimit rl; if(getrlimit(RLIMIT_NOFILE, &rl) == 0) return rl.rlim_max; return 256; //default } FILE* CRGReDirect::rgpopen(const char *cmdstring, const char *type) { int i, pfd[2]; pid_t pid; FILE *fp; /* only allow "r" or "w" */ if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) { errno = EINVAL; return(NULL); } if (m_childpid == NULL) { /* first time through */ /* allocate zeroed out array for child pids */ m_maxfd = open_max(); if ( (m_childpid = (pid_t *)calloc(m_maxfd, sizeof(pid_t))) == NULL) return(NULL); } if (pipe(pfd) < 0) return(NULL); /* errno set by pipe() */ if ( (pid = fork()) < 0) return(NULL); /* errno set by fork() */ else if (pid == 0) { /* child */ if (*type == 'r') { close(pfd[0]); if (pfd[1] != STDOUT_FILENO) { dup2(pfd[1], STDOUT_FILENO); close(pfd[1]); } } else { close(pfd[1]); if (pfd[0] != STDIN_FILENO) { dup2(pfd[0], STDIN_FILENO); close(pfd[0]); } } /* close all descriptors in childpid[] */ for (i = 0; i < m_maxfd; i++) if (m_childpid[ i ] > 0) close(i); execl(SHELL, "sh", "-c", cmdstring, (char *) 0); _exit(127); } /* parent */ if (*type == 'r') { close(pfd[1]); if ( (fp = fdopen(pfd[0], type)) == NULL) return(NULL); } else { close(pfd[0]); if ( (fp = fdopen(pfd[1], type)) == NULL) return(NULL); } m_childpid[fileno(fp)] = pid; /* remember child pid for this fd */ return(fp); } int CRGReDirect::rgpkill(FILE* fp) { int fd, stat; pid_t pid; if (m_childpid == NULL) return(-1); /* rgpopen() has never been called */ fd = fileno(fp); if ( (pid = m_childpid[fd]) == 0) return(-1); /* fp wasn't opened by rgpopen() */ m_childpid[fd] = 0; fclose(fp); return kill(pid, SIGKILL); } int CRGReDirect::rgpclose(FILE *fp) { int fd, stat; pid_t pid; if (m_childpid == NULL) return(-1); /* rgpopen() has never been called */ fd = fileno(fp); if ( (pid = m_childpid[fd]) == 0) return(-1); /* fp wasn't opened by rgpopen() */ m_childpid[fd] = 0; if (fclose(fp) == EOF) return(-1); while (waitpid(pid, &stat, 0) < 0) if (errno != EINTR) return(-1); /* error other than EINTR from waitpid() */ return(stat); /* return child's termination status */ } #endif