我正在Linux(Ubuntu 14.04)上运行一个简单的apache Web服务器,并使用perl CGI脚本处理一些请求.该脚本使用系统功能启动系统命令,但是我希望它立即返回,而不管系统调用的结果如何.
我一直在传递给系统的标量参数的末尾添加“&”号(我知道命令注入攻击的含义),尽管这确实会导致系统命令立即返回,但脚本直到下层脚本仍不会退出命令已完成.
如果我使用来自perl CGI的系统调用以10秒钟的睡眠时间触发了一个虚拟的ruby脚本,那么我对Web服务器的请求仍会等待10秒,直到最终获得响应.我在系统调用后放置了一条日志语句,该语句在发出Web请求时立即出现,因此系统调用肯定会立即返回,但脚本仍在最后等待.
This question与之类似,但是没有一个解决方案对我有用.
这是一些示例代码:
#!/usr/bin/perl
use strict;
use warnings;
use CGI;
use Log::Log4perl qw(:easy);
Log::Log4perl->easy_init(
{ level => $DEBUG, file => ">>/var/log/script.log" } );
print "Content-type: application/json\n\n";
my $cgi = CGI->new();
INFO("Executing command...");
system('sudo -u on-behalf-of-user /tmp/test.rb one two &');
INFO("Command initiated - will return now...");
print '{"error":false}';
编辑:
使用sudo -u执行命令调用是因为apache用户www-data需要代表脚本所有者执行脚本的权限,并且为此我已经适当地更新了sudoers文件.这不是我的问题的原因,因为我还尝试将脚本所有权更改为www-data并运行system(“ / tmp / test.rb一二&”),但是结果是相同的.
编辑2:
我也尝试将出口0添加到脚本的最后,但这没有任何区别.脚本是否有可能立即退出,但是apache服务器会一直保持响应,直到调用perl CGI的脚本完成?还是操作系统的某些设置或配置导致了此问题?
编辑3:
直接从终端运行perl CGI脚本可以正常工作. perl脚本立即结束,因此这不是Perl固有的问题.这大概只能意味着Apache Web服务器将保留请求,直到从系统调用的命令完成为止.为什么?
解决方法:
Web服务器创建一个管道以接收响应.在完成请求之前,它等待管道到达EOF.当关闭所有编写器句柄副本时,管道达到EOF.
管道的写入器端设置为子代的STDOUT.该文件句柄被复制为外壳的STDOUT,然后再次复制到mycmd的STDOUT.因此,即使CGI脚本和外壳程序结束并因此关闭了文件句柄的末端,mycmd仍将句柄保持打开状态,因此Web服务器仍在等待响应完成.
您需要做的所有事情都是关闭管道的writer端的最后一个句柄.或者更确切地说,可以通过将不同的手柄附加到mycmd STDOUT上来避免将其放在第一位.
mycmd arg1 arg2 </dev/null >/dev/null 2>&1 &