IPC::Open3 在 Apache 下运行失败

发布于 2024-08-18 19:38:17 字数 353 浏览 5 评论 0原文

我有一个模块,它使用 IPC::Open3 (或 IPC::Open2,两者都存在此问题)来调用外部二进制文件(在本例中为 bogofilter)并通过子输入文件句柄向其提供一些输入,然后从子输出句柄。该代码在大多数环境中运行时都可以正常工作。然而,该模块的主要用途是在 Apache 2.2.6 下运行的 Web 服务中。在该环境下,我收到错误:

Cannot fdopen STDOUT: Invalid argument

这仅当代码在 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. 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. And under that environment, I get the error:

Cannot fdopen STDOUT: Invalid argument

This only happens when the code runs under Apache. Previously, the code constructed a horribly complex command, which included a here-document for the input, and ran it with 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦过后 2024-08-25 19:38:17

难道是因为 mod_perl 2 关闭了 STDOUT?我刚刚发现了这一点并发布了相关信息:

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

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

乔恩

Could it be because mod_perl 2 closes 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.

Jon

清风挽心 2024-08-25 19:38:17

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 给出的实际评级那样,但它确实会给你带来一些东西。

Bogofilter returns different exit codes for spam/nonspam.

You can "fix" this by redirecting stdout to /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).

Note: the lack of an environment may also prevent bogofilter from finding its wordlist, so pass that in explicitly as well:

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

(where /path/to/.bogofilter contains the wordlist.db)

You can't retrieve the actual rating that bogofilter gave that way, but it does get you something.

谜兔 2024-08-25 19:38:17

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

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:

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/');
当爱已成负担 2024-08-25 19:38:17

买者自负:我不是 Perl 向导。

正如 @JonathanSwartz 所建议的,我认为问题是 apache2 mod_perl 关闭了 STDIN 和 STDOUT。这不应该与 IPC::Open3 正在做的事情相关,但它有一个错误, 此处描述

总之(这是我不太清楚的部分),open3 尝试将子进程 STDIN/OUT/ERR 与您的进程匹配,或者如果这是所要求的,则复制它。由于 open('>&=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 遇到同样问题的抱怨,所以如果有人遇到同样的问题,我怀疑同样的解决方案会起作用。

Caveat Emptor: I am not a perl wizard.

As @JonathanSwartz suggested, I believe the issue is that apache2 mod_perl closes STDIN and STDOUT. That shouldn't be relevant to what IPC::Open3 is doing, but it has a bug in it, described here.

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. Due to some undocumented ways that open('>&=X') works, it generally works fine, except in the case where STDIN/OUT/ERR are closed.

Another link that gets deep into the details.

One solution is to fix IPC::Open3, as described in both of those links. The other, which worked for me, is to temporarily open STDIN/OUT in your mod_perl code and then close it afterwards:

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文