简体   繁体   English

IPC :: Open3在Apache下运行失败

[英]IPC::Open3 Fails Running Under Apache

I have a module that uses IPC::Open3 (or IPC::Open2, both exhibit this problem) to call an external binary (bogofilter in this case) and feed it some input via the child-input filehandle, then reads the result from the child-output handle. 我有一个模块使用IPC :: Open3(或IPC :: Open2,两者都表明这个问题)来调用外部二进制文件(在这种情况下是bogofilter)并通过子输入文件句柄输入一些输入,然后从中读取结果子输出句柄。 The code works fine when run in most environments. 在大多数环境中运行时,代码工作正常。 However, the main use of this module is in a web service that runs under Apache 2.2.6. 但是,此模块的主要用途是在Apache 2.2.6下运行的Web服务中。 And under that environment, I get the error: 在那种环境下,我得到了错误:

Cannot fdopen STDOUT: Invalid argument 不能fdopen STDOUT:参数无效

This only happens when the code runs under Apache. 这只在代码在Apache下运行时才会发生。 Previously, the code constructed a horribly complex command, which included a here-document for the input, and ran it with back-ticks. 以前,代码构造了一个非常复杂的命令,其中包含输入的here-document,并使用back-ticks运行它。 THAT worked, but was very slow and prone to breaking in unique and perplexing ways. 虽然有效,但速度非常慢,并且容易以独特和令人困惑的方式打破。 I would hate to have to revert to the old version, but I cannot crack this. 我不想重新使用旧版本,但我无法解决这个问题。

Could it be because mod_perl 2 closes STDOUT? 可能是因为mod_perl 2关闭STDOUT? I just discovered this and posted about it: 我刚刚发现了这个并发布了它:

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

I think it's a nasty bug, but no one seems to care about it thus far. 我认为这是一个令人讨厌的错误,但到目前为止似乎没有人关心它。 Post a follow up on the mod_perl list if your problem is related and you want it to get attention. 如果您的问题是相关的并且您希望它得到关注,请在mod_perl列表上发布一个跟进。

Jon 乔恩

Caveat Emptor: I am not a perl wizard. 警告Emptor:我不是一个perl巫师。

As @JonathanSwartz suggested, I believe the issue is that apache2 mod_perl closes STDIN and STDOUT. 正如@JonathanSwartz所说,我认为问题是apache2 mod_perl会关闭STDIN和STDOUT。 That shouldn't be relevant to what IPC::Open3 is doing, but it has a bug in it, described here . 这与IPC :: Open3正在做的事情无关,但它有一个错误,在这里描述

In summary (this is the part I'm not super clear on), open3 tries to match the child processes STDIN/OUT/ERR to your process, or duplicate it if that was what is requested. 总而言之(这是我不太清楚的部分),open3尝试将子进程STDIN / OUT / ERR与您的进程匹配,或者如果那是请求则重复它。 Due to some undocumented ways that open('>&=X') works, it generally works fine, except in the case where STDIN/OUT/ERR are closed. 由于打开('>&= X')的一些未记录的方式有效,它通常可以正常工作,除非关闭STDIN / OUT / ERR。

Another link that gets deep into the details. 另一个深入细节的链接

One solution is to fix IPC::Open3, as described in both of those links. 一种解决方案是修复IPC :: Open3,如两个链接中所述。 The other, which worked for me, is to temporarily open STDIN/OUT in your mod_perl code and then close it afterwards: 另一个对我有用的是暂时在你的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;

Also, I noticed a bunch of complaints around the net about IPC::Run3 suffering from the same problems, so if anyone runs into the same issue, I suspect the same solution would work. 另外,我注意到网络上有很多关于IPC :: Run3遭遇同样问题的抱怨,所以如果有人遇到同样的问题,我怀疑同样的解决方案会起作用。

Bogofilter returns different exit codes for spam/nonspam. Bogofilter为垃圾邮件/非垃圾邮件返回不同的退出代码。

You can "fix" this by redirecting stdout to /dev/null 您可以通过将stdout重定向到/ dev / null来“修复”此问题

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

Will return 0 for spam, 1 for nonspam, 2 for unknown (the >> 8 is because perl helpfully corrects the exit code, this fixes the damage). 将返回0表示垃圾邮件,1表示非垃圾邮件,2表示未知(>> 8表示因为perl帮助纠正退出代码,这可以修复损坏)。

Note: the lack of an environment may also prevent bogofilter from finding its wordlist, so pass that in explicitly as well: 注意:缺少环境也可能会阻止bogofilter找到其wordlist,因此也明确地传递它:

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

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

You can't retrieve the actual rating that bogofilter gave that way, but it does get you something. 你不能检索bogofilter给出的实际评级,但它确实能为你提供一些东西。

If your code is only going to be run on Linux/Unix systems it is easy to write an open3 replacement that does not fail because STDOUT is not a real file handle: 如果您的代码只在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/');

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM