【问题标题】:In Perl, is it possible to kill an open(SENDMAIL, "|$sendmail") before close(SENDMAIL)在 Perl 中,是否可以在 close(SENDMAIL) 之前杀死一个 open(SENDMAIL, "|$sendmail")
【发布时间】:2017-05-04 23:43:49
【问题描述】:

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

我试过这个没有运气:

$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 只是强制关闭流,而不是真正杀死它。

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

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

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

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

【问题讨论】:

  • 发送电子邮件的不是close SENDMAIL,而是print SENDMAIL ...。您需要在此之前进行干预。
  • 在打印 SENDMAIL 之前确认我们肯定会进行干预。
  • 你可能正在杀死一个 shell,这取决于 $sendmail 的内容。
  • 难道不能等到知道要发送邮件后再打开管道吗?您可以将 push 字符串放到数组中,而不是 print 将它们放到管道中。然后,如果您发现不希望发送数组的内容,则可以丢弃它。
  • 或者确保找到实际 sendmail 进程的 pid,然后将其杀死。

标签: perl


【解决方案1】:

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

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

您可以使用Proc::ProcessTable,而不是手动解析ps

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 进程,这很有可能,您需要进行更彻底的分析。

由于您需要将其从水中吹出,因此我认为无法修改其后续打印以进行检查。 (否则你可以用更简洁的方式解决这个问题。)

那么您必须SIGPIPE 安装一个信号处理程序,否则程序将在下一次尝试打印到该文件句柄时死掉,因为它会得到一个SIGPIPE 并且它的处置是终止.

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


更多细节。明确命令为:/usr/lib/sendmail -f$sender -t

模块的对象(上面的$pt)有很多进程表字段,由$pt-&gt;fields列出,描述在它的"stub module"。我发现打印和查看所有感兴趣的对象会提供更多信息。 execcwd 和各种ids 可能对此有所帮助。

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

上面的例子扩展了一点

$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' 的内容。


另一种选择是杀死process groupkill 9, -$pid(注意减号)。这应该会捕获sendmail 进程本身,但当然要确保你知道什么被吹走了。

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

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

【讨论】:

  • $sendmail 似乎是“/usr/lib/sendmail -f\$sender -t”,所以最后open (SENDMAIL, "|/usr/lib/sendmail -f\$sender -t"); 是正在运行的。你有 waitpid ......更多的研究表明,在我到达 close() 之前,$pid 可能不会被杀死?现在进行更多测试。
  • @OutofControl 使用forkopen 的形式是一个过程,一旦我们kill 它,我们应该无论如何wait 为它。 waitpid 允许等待特定进程(通过 PID)。然后更改正则表达式,以匹配将识别系统上进程的内容。我会补充一点。
  • @OutofControl 您需要在打印到其文件句柄之前终止该进程。一旦这些发生,它就在postfix(我认为还有postdrop)手中,他们发送他们队列和临时文件中的任何内容。通过“关闭前”(但在文件句柄打印后)终止,我们只会弄乱我们的进程,但不会停止电子邮件。
  • 如果 open() 已经执行,但没有打印,我可以杀死 $pid 并且没有发送电子邮件吗?
  • @OutofControl 是的——kill 失败将是一个很大的惊喜。如果sendmail 在任何打印之前被杀死,则没有电子邮件,并且在我的测试中,没有任何内容发送给该收件人,没有消息。 (您需要在您的系统上进行测试。)唯一的事情就是确定正确的 pid。 (您甚至可以在命令中使用sendmail 打印所有进程,以查看发生了什么。)更新了答案,将进行更多编辑。让我知道需要解释什么
猜你喜欢
  • 2020-12-21
  • 2017-01-03
  • 2012-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-15
  • 2010-10-01
相关资源
最近更新 更多