IPC :: Open3无法在Apache下运行

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

不能fdopen STDOUT:无效的参数

只有在Apache下运行代码时才会发生这种情况。 在此之前,代码构build了一个非常复杂的命令,其中包括一个在这里input的文档,并用反标记运行。 这工作,但非常缓慢,容易打破独特和令人困惑的方式。 我恨不得不恢复到旧版本,但我不能破解这个。

难道是因为mod_perl 2关闭了STDOUT? 我刚刚发现了这个,并发布了它:

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

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

乔恩

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

你可以通过将stdout重定向到/ dev / null来“解决”这个问题

 system("bogofilter < $input > /dev/null") >> 8; 

将为垃圾邮件返回0,为非垃圾邮件返回1,为未知返回2(>> 8是因为perl有助于纠正退出代码,这个修复损坏)。

注意:缺乏环境也可能阻止bogofilter找到它的单词列表,因此也要明确地传递它:

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

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

您无法检索bogofilter以这种方式给出的实际评分,但它确实为您提供了一些帮助。

如果你的代码只能在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/'); 

警告Emptor:我不是一个perl向导。

正如@JonathanSwartz所建议的,我认为问题在于apache2 mod_perl关闭了STDIN和STDOUT。 这与IPC :: Open3没有什么关系,但是它有一个错误,在这里描述 。

总之(这是我不是很清楚的部分),open3会尝试将子进程STDIN / OUT / ERR与进程进行匹配,或者在请求的时候复制它。 由于一些未公开的方式('>&= X')可以正常工作,除了STDIN / OUT / ERR被关闭的情况外,它通常工作正常。

深入细节的另一个环节 。

一种解决方案是修复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的网络问题,如果有人遇到同样的问题,我怀疑同样的解决方案会起作用。