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

 

posted @ 2014-09-16 18:32  Jojodru  阅读(2563)  评论(0编辑  收藏  举报