2014-09-01 80 views
0

我正在研究一個Unbuntu的C++程序,該程序使用curl_easy_perform下載tar歸檔文件,並將歸檔文件下載到/ tmp中後使用popen執行適當的tar命令行。popen(「tar xvf tarball.tar」)工作在調試但不是發佈版本

當我運行程序的調試版本時,popen(「tar xvf /tmp/example.tar -C/tmp/existingdir」)可以工作,但是當我在發佈版本中運行此命令時,popen調用總是失敗。

這裏是我的代碼,大多數錯誤檢查和無關的東西去掉:

//tl;dr version: 
// first I download a tar archive from url using Curl and save it to filelocation, 
// then I untar it using pOpen. 
// pOpen always works in debug, never in release builds 
//// 
Status ExpandTarBall(const MyString& FileName) 
{ 
    //extract the tar ball into a previously created temporary directory, tempDirPath 
    MyString args = "tar xvf /tmp/ + FileName + " -C " + tempDirPath; 
    cout << "running:" << args << endl; 
    // args example: 
    // tar xvf /tmp/UserIdXXxCtnAl/examplepackage -C /tmp/UserIdXXxCtnAl 
    // 
    Status result = ER_OPEN_FAILED; 
    FILE* fp = popen(args.c_str(), "re"); //<========== always works in debug builds, fails with 0 returned in release builds! :(
    if (fp) 
    { 
     result = pclose(fp) == 0 ? ER_OK : ER_INVALID_DATA; 
    } 

    return result; 
} 



    //Note: MyString is an std::string class with some local extensions 
Status SslDownloader::DownloadFile(MyString url, MyString fileLocation, bool sslVerify) { 

    CURL* curl = NULL; 
    CurlInitHelper helper(curl); 

    cout << "downloading from " << url.c_str() << " to " << fileLocation.c_str() << endl; 

    if (!curl) { 
     return ER_SSL_INIT; 
    } 
    FILE* fp = fopen(fileLocation.c_str(), "wb"); 
    if(NULL == fp) { 
     return ER_OPEN_FAILED; 
    } 
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); 
    curl_easy_setopt(curl, CURLOPT_USERAGENT, AJPM_USER_AGENT); 
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); 
    curl_easy_setopt(curl, CURLOPT_FAILONERROR, true); 
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); 

    if (sslVerify) { 
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); 
     curl_easy_setopt(curl, CURLOPT_CAINFO, AJPM_CERT_STORE_LOCATION); 
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); 
    } else { 
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); 
     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 
    } 

    CURLcode res = curl_easy_perform(curl); 

    if (0 != fclose(fp)) { 
     return ER_WRITE_ERROR; 
    } 

    if (res != CURLE_OK) { 
      return res == ER_SSL_CONNECT; 
     } 

    cout << "SSL download of " << fileLocation.c_str() << " succeeded\n"; // works every time 
    return ExpandTarBall(const MyString& FileName); 
} 

我缺少什麼簡單的事情?

+0

什麼意思* debug *或* release *模式,是否有一些編譯標誌? – gipi 2014-09-01 21:11:23

+0

你應該檢查你正在裝配你的'tar'命令行的那一行。 – user2719058 2014-09-01 23:25:08

回答

2

使用popen啓動子進程後,您立即調用pclose(),而不讀取由popen()返回的文件。

tar的xvf選項會在其標準輸出(這是一個管道)上轉儲文件列表,並使用popen將管道的讀取端返回給您。

pclose()首先關閉管道,然後等待子進程終止。

在父進程和子進程併發運行的情況下,如果父進程在子進程啓動之前贏得競爭並關閉管道,那麼當子進程嘗試寫入其標準輸出時,管道,它會得到一個SIGPIPE信號,殺死子進程。

在應用程序的「調試」和「發佈」版本之間,運行時配置文件的差異很可能足以使版本規模向版本構建階段發展,而無論您實際意味着什麼通過「調試構建」將使事情變得緩慢,這樣子進程就有時間在父進程開始關閉管道之前泄漏其標準輸出。

請記住,一旦tarball包含足夠數量的文件,即使tar在此比賽中領先,它的輸出將填滿管道緩衝區和塊,並且一旦父進程開始關閉管道,它會SIGPIPE子進程,並且tar總是失敗。

故事的道德:當使用popen從開始的子進程讀取時,總是從管道中讀取並使用子進程的輸出,直到獲得EOF,然後再執行pclose()。

+0

另外,如果你不需要輸出,不要使用'v'選項來打... – pqnet 2014-09-01 23:38:16

+0

其實,我不認爲在這裏使用'popen'是有意義的。 '系統'應該做的伎倆。 – user2719058 2014-09-01 23:43:31

+0

閱讀輸出固定比賽的管道,謝謝! – 2014-09-02 01:42:31

相關問題