2014-09-01 209 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

相关问题