【问题标题】:popen ("tar xvf tarball.tar") works in debug but not release buildspopen ("tar xvf tarball.tar") 在调试中有效,但在发布版本中无效
【发布时间】:2014-09-01 20:57:33
【问题描述】:

我正在开发一个用于 Ubuntu 的 C++ 程序,该程序使用 curl_easy_perform 下载 tar 存档,并且在将存档下载到 /tmp 后,我使用 popen 执行适当的 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);
}

我缺少什么简单的东西?

【问题讨论】:

  • 什么是debugrelease模式,有没有一些编译标志?
  • 你应该检查你正在组装你的tar命令行的那一行。

标签: linux libcurl tar popen


【解决方案1】:

在你使用popen启动子进程后,你立即调用pclose(),而不是从popen()返回的文件中读取。

tar 的 xvf 选项将在其标准输出(管道)上转储文件列表,popen 将管道的读取端返回给您。

pclose() 先关闭管道,然后等待子进程终止。

在父进程和子进程同时运行的情况下,如果父进程赢得比赛并在子进程启动之前关闭管道,那么当子进程尝试写入其标准输出时, pipe,它会得到一个 SIGPIPE 信号,杀死子进程。

很有可能,应用程序的“调试”和“发布”构建之间的运行时配置文件的差异足以将规模推向发布构建的结果,而无论您的实际意思是什么,额外的开销“调试构建”会减慢速度,以便子进程有时间在父进程关闭管道之前溢出其标准输出。

请记住,一旦您的 tarball 包含足够数量的文件,即使 tar 在这场比赛中领先,它的输出也会填满管道缓冲区并阻塞,并且一旦父进程开始关闭管道,它会 SIGPIPE 子进程,并且 tar 总是会失败。

故事的寓意:当使用 popen 从启动的子进程中读取时,始终从管道中读取并使用子进程的输出,直到获得 EOF,然后再 pclose() 它。

【讨论】:

  • 实际上,我认为在这里使用popen 根本没有意义。 system 应该可以解决问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多