IPC :: Open3在Apache下运行失败

时间:2010-01-19 21:30:03

标签: perl apache ipc mod-perl2

我有一个模块使用IPC :: Open3(或IPC :: Open2,都表现出这个问题)来调用外部二进制文件(在这种情况下是bogofilter)并通过子输入文件句柄输入一些输入,然后读取子输出句柄的结果。在大多数环境中运行时,代码工作正常。但是,此模块的主要用途是在Apache 2.2.6下运行的Web服务中。在那种环境下,我得到错误:

无法fdopen STDOUT:参数无效

仅当代码在Apache下运行时才会发生这种情况。以前,代码构造了一个非常复杂的命令,其中包含输入的here-document,并使用back-ticks运行它。虽然有效,但速度非常慢,并且容易以独特和令人困惑的方式打破。我不想回到旧版本,但我无法解决这个问题。

4 个答案:

答案 0 :(得分:1)

可能是因为mod_perl 2关闭了STDOUT吗?我刚刚发现了这个并发布了它:

http://marc.info/?l=apache-modperl&m=126296015910250&w=2

我认为这是一个令人讨厌的错误,但到目前为止似乎没有人关心它。如果您的问题是相关的并且您希望它得到关注,请在mod_perl列表上发布一个跟进。

乔恩

答案 1 :(得分:0)

Bogofilter会针对垃圾邮件/非垃圾邮件返回不同的退出代码。

您可以通过将stdout重定向到/ dev / null

来“修复”此问题
system("bogofilter < $input > /dev/null") >> 8;

垃圾邮件会返回0,非垃圾邮件会返回1,默认情况下会返回2(&gt;&gt; 8是因为perl帮助纠正退出代码,这会修复损坏)。

注意:缺少环境也可能会阻止bogofilter查找其wordlist,因此也明确地传递它:

system("bogofilter -d /path/to/.bogofilter/ < $input > /dev/null") >> 8;

(其中/path/to/.bogofilter包含wordlist.db)

你无法检索bogofilter给出的实际评级,但它确实能为你提供一些东西。

答案 2 :(得分:0)

如果您的代码只在Linux / Unix系统上运行,那么编写一个不会失败的open3替换是很容易的,因为STDOUT不是真正的文件句柄:

sub my_open3 {
    # untested!
    pipe my($inr), my($inw) or die;
    pipe my($outr), my($outw) or die;
    pipe my($errr), my($errw) or die;
    my $pid = fork;
    unless ($pid) {
        defined $pid or die;
        POSIX::dup2($inr, 0);
        POSIX::dup2($outw, 1);
        POSIX::dup2($errw, 2);
        exec @_;
        POSIX::_exit(1);
    }
    return ($inw, $outr, $errr);
}

my ($in, $out, $err) = my_open3('ls /etc/');

答案 3 :(得分:0)

警告Emptor:我不是一个perl巫师。

正如@JonathanSwartz所说,我认为问题是apache2 mod_perl会关闭STDIN和STDOUT。这不应该与IPC :: Open3正在做什么有关,但它有一个错误,described here

总之(这是我不太清楚的部分),open3尝试将子进程STDIN / OUT / ERR与您的进程匹配,或者如果那是请求的话,将其复制。由于某些未记录的方式可以打开(&#39;&gt;&amp; = X&#39;),它通常可以正常工作,除非STDIN / OUT / ERR关闭。

Another link深入细节。

一种解决方案是修复IPC :: Open3,如两个链接中所述。另一个对我有用的是暂时在你的mod_perl代码中打开STDIN / OUT,然后关闭它:

my ($save_stdin,$save_stdout);
open $save_stdin, '>&STDIN';
open $save_stdout, '>&STDOUT';
open STDIN, '>&=0';
open STDOUT, '>&=1';

#make your normal IPC::Open3::open3 call here

close(STDIN);
close(STDOUT);
open STDIN, '>&', $save_stdin;
open STDOUT, '>&', $save_stdout;

另外,我注意到网络上有很多关于IPC :: Run3遇到同样问题的抱怨,所以如果有人遇到同样的问题,我怀疑同样的解决方案会起作用。