在 Perl 中,是否可以在 close(SENDMAIL) 之前杀死 open(SENDMAIL, "|$sendmail") [英] In Perl, is it possible to kill an open(SENDMAIL, "|$sendmail") before close(SENDMAIL)

查看:59
本文介绍了在 Perl 中,是否可以在 close(SENDMAIL) 之前杀死 open(SENDMAIL, "|$sendmail")的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要破解一个使用 open(SENDMAIL, "|$sendmail")close(SENDMAIL) 的旧系统.一旦流打开,是否可以停止发送电子邮件?在这种情况下,如果发现了一些意外的垃圾内容.

I'm needing to hack an old system that uses open(SENDMAIL, "|$sendmail") and close(SENDMAIL). Is it possible to stop the email from being sent once the stream is open? In this case, if some unexpected spammy content is discovered.

我试过了,但没有运气:

I've tried this with no luck:

$pid = open(SENDMAIL, "|$sendmail");
while (<>) {
    ....do lots of stuff in here....
    # Oops, we need to abort
    if ($needToAbort) {
        kill 9, $pid;
        exit(0);
    }
}
close(SENDMAIL);

即使当循环达到 $needToAbort === true 时,电子邮件仍然会发出.我能找到的最好解释是 kill 9, $pid, 只是强行关闭流,而不是真正杀死它.

Even when the loop hits $needToAbort === true, the email still goes out. Best explanation I can find is that kill 9, $pid, is only closing off the stream forcibly, not actually killing it.

为了验证 $pid 是否存在,我尝试添加到 if:

In order to verify $pid exists, I'd tried add to the if:

if ($needToAbort) {
    $exists = kill 0, $pid;
    if ($exists) {
        kill 9, $pid;
        exit(0);
    }
}

使用日志记录,有时 $pid 似乎存在,有时则不存在.系统使用 perl 5,版本 16.

Using logging, sometimes the $pid seems to exist and sometimes it doesn't. The system uses perl 5, version 16.

问题:是否有可能,我将如何编辑代码以阻止发送电子邮件?

Question: Is it possible, and how would I edit my code to stop the email from being sent?

推荐答案

似乎命令 $sendmail 没有直接启动 sendmail 程序,所以 open 返回的 >$pid 不是 sendmail 的(而是 shell 的?).

It seems that the command $sendmail isn't starting the sendmail program directly so the $pid returned by open isn't sendmail's (but shell's?).

找到 sendmail 进程本身的 PID,kill 应该可以工作.(或者考虑杀死整个进程组,见文末).

Find the PID of the sendmail process itself and kill should work. (Or consder killing the whole process group, see the end).

您可以使用 ps="nofollow noreferrer">Proc::ProcessTable

Instead of doing that by manually parsing ps you can use Proc::ProcessTable

use Proc::ProcessTable;

my $pid = open my $sm_fh, '|-', $sendmail or die "Can't open sendmail: $!";

my $pid_sm;
my $pt = Proc::ProcessTable->new();
foreach my $proc (@{$pt->table}) {
    if ($proc->cmndline =~ /^sendmail/) {  # adjust regex for your system
        $pid_sm = $proc->pid;
        say "Sendmail command-line: ", $proc->cmndline;
        say "Sendmail process pid:  ", $proc->pid;
    }   
}

kill 9, $pid_sm;
my $gone_pid = waitpid $pid_sm, 0;
say "Process $gone_pid is gone";

# need a handler for SIGPIPE for prints to $sm_fh further in code

在我的系统上,CMD 字段以 sendmail 开头,根据您的情况进行调整.如果可能有多个 sendmail 进程,这很有可能,您需要更彻底的分析.

On my system the CMD field starts with sendmail, adjust for how it is on yours. If there may be multiple sendmail processes, what is quite possible, you'll need a more thorough analysis.

因为你需要把这东西从水里吹出来,所以我认为它的以下印刷品无法修改以进行检查.(否则你可以用更简洁的方式解决这个问题.)

Since you need to blow the thing out of the water I presume that its following prints can't be modified for a check. (Otherwise you could solve this in much cleaner ways.)

然后您必须SIGPIPE安装一个信号处理程序,否则程序将在下一次尝试打印到该文件句柄时死亡,因为它将获得一个SIGPIPE 和它的处置是终止.

Then you must install a signal handler for SIGPIPE or the program will die at the next attempt to print to that filehandle, since it will get a SIGPIPE and its disposition is to terminate.

另一种解决方案是将 sendmail 处理包装在 Expect,它设置了一个伪终端,以便您可以在需要时发送 Ctrl-C.(它自己的 hard_close 方法也可以在我的测试中完成这项工作.)但是为此,应该修改打印语句,因此这里可能不可行.

Another solution is to wrap sendmail handling in Expect, which sets up a pseudo-terminal so you can send a Ctrl-C when needed. (Its own hard_close method does the job in my tests, too.) But for this the printing statements should be modified so it may be a no-go here.

再详细一点.澄清命令是:/usr/lib/sendmail -f$sender -t

A little more detail. It was clarified that the command is: /usr/lib/sendmail -f$sender -t

模块的对象(上面的$pt)有很多进程表字段,由$pt->fields列出,在其存根模块".我发现打印并查看所有感兴趣的对象会提供更多信息.一些可能对此有帮助的是 execcwd 和各种 id.

The module's object ($pt above) has a lot of process table fields, listed by $pt->fields, with descriptions at its "stub module". I find it more informative to print and review them all for objects of interest. Some that may be helpful for this purpose are exec, cwd, and various ids.

如何准确识别进程取决于系统的详细信息,但一种方法是查看命令行详细信息.

How exactly to identify a process depends on details of the system, but one way is to look at the command line details.

上面的例子有点扩展

$SIG{PIPE} = sub { print "Got $_[0]\n" };  # or just $SIG{PIPE} = 'IGNORE';

my $pid_sm;
foreach my $proc (@{$pt->table}) {
    my $cmd = $proc->cmndline;
    next if $cmd !~ m{^/usr/lib/sendmail};
    if ( (split ' ', $cmd)[1] eq "-f$sender" ) {
        $pid_sm = $proc->pid;
        say "Our process $pid_sm: $cmd";
    }
    else { say "Some other sendmail: $cmd" }
}
warn "Didn't find our sendmail process" if not $pid_sm;

if ($needToAbort and $pid_sm) {
    kill 9, $pid_sm;
    my $gone_pid = waitpid $pid_sm, 0;
    if    ($gone_pid == -1) { say "No process $pid_sm" }
    elsif ($gone_pid ==  0) { say "Process $pid_sm still around?" }
    else                    { say "Process $gone_pid is gone" }
};

根据确切的短语 "-f$sender" 检查命令行的第二个字段,使用正则表达式而不是 eq 可以重新定义什么.查看为上述所有进程打印的命令行并根据需要进行调整.如果出现问题,打印出任何包含 'sendmail' 的内容.

The second field of the command-line is checked against the exact phrase "-f$sender", what can be relexad by using regex instead of eq. Review command-lines printed for all processes above and adjust as needed. If there are problems print out anything that has 'sendmail' in it.

另一种选择是终止进程组: kill 9, -$pid (注意减号).这应该会捕获 sendmail 进程本身,但当然要确保你知道什么被吹走了.

Another option is to kill the process group: kill 9, -$pid (note the minus). This should catch the sendmail process itself, but of course make sure you know what is getting blown away.

补充一点,我怀疑您是否需要使用 SIGKILL (9).一旦找到正确的 pid,SIGTERM(在我的系统上为 15,请参阅 man 7 信号)可能已经足够好了,还有什么更好的.

To add, I doubt that you need to use SIGKILL (9). Once the right pid is found the SIGTERM (15 on my system, see man 7 signal) may well be good enough, what is much nicer.

最后,进程可能会被操作系统束缚并处于不可中断状态,尤其是在某些 I/O 操作中.但是,这在这里似乎不太可能,我会首先尝试上述两种方法.

Finally, a process can get tied down by the OS and be in an uninterruptible state, in particular in some I/O operation. However, that doesn't seem likely here and I'd first try the two approaches above.

这篇关于在 Perl 中,是否可以在 close(SENDMAIL) 之前杀死 open(SENDMAIL, "|$sendmail")的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆