【问题标题】:Perl fork exec, system in parent and kill childPerl fork exec, system in parent and kill child
【发布时间】:2015-01-24 13:59:38
【问题描述】:

您好,我正在尝试记录用户在我的服务器上所做的一切。我有一个脚本可以替换 ssh 并记录所有内容。

问题是当用户停止 ssh 会话时,记录操作的子进程没有被杀死。

my $pid = fork();
die "unable to fork: $!" unless defined($pid);
if (!$pid) {  # child
    exec("tail -f $logfile | logger -t $ENV{SUDO_USER}:$target ");
    die "unable to exec: $!";
}

$show_cmd && print "% $cmd\n" ;
system $cmd or die "exec() failed: $!\n" ;
printf "Session end pid to kill %d\n", $pid;
kill 1, $pid;
waitpid $pid, 0;
printf "End of the script.\n";

我也放了

$SIG{CHLD} = "IGNORE";

如果我删除了指令系统(启动用户原始 ssh 命令的指令),孩子会被杀死,但这也会使我的脚本无用。

任何想法如何成功终止进程?

编辑: 当系统命令结束脚本继续时, printf 被执行,并打印孩子的 pid。

EDIT2:

这是 ssh 会话期间的“ps faux”

root      4976  [...]   \_ /usr/bin/perl -w /usr/bin/ssh **.**.**.62
root      4977  [...]      \_ sh -c tail -f /var/log/log-session/2014-11-26.155910.*******:root@**.**.**.62 | logger -t *********:root@**.**.**.62
root      4979  [...]      |   \_ tail -f /var/log/log-session/2014-11-26.155910.*********:root@**.**.**.62
root      4980  [...]      |   \_ logger -t ********:root@**.**.**.62
root      4978  [...]      \_ sh -c /usr/bin/realssh -o UserKnownHostsFile=/etc/ssh/known_hosts_Securite -i /etc/ssh/id_dsa_Securite **.**.**.62 | ...
root      4981  [...]         \_ /usr/bin/realssh -o UserKnownHostsFile=/etc/ssh/known_hosts_Securite -i /etc/ssh/id_dsa_Securite **.**.**.62
root      4982  [...]         \_ /usr/bin/tee -a /var/log/log-session/2014-11-26.155910.********:root@**.**.**.62

会话结束时:来自用户的 ^D

Connection to **.**.**.62 closed.
Session end pid to kill: 4977
End of the script.

以及“ps faux”的结果:

 root      4979  [...]        tail -f /var/log/log-session/2014-11-26.155910.*********:root@**.**.**.62
 root      4980  [...]        logger -t ********:root@**.**.**.62

所以最后这两个进程仍然是无所事事的。

【问题讨论】:

  • 监控某人对第三方服务器的访问有一些相当重要的隐私和潜在的法律考虑。你确定这就是你想要做的吗?
  • 不,没有问题,用于公司监控所有服务器上的操作。
  • 那有什么原因你不能使用内置的会计和审计机制吗?

标签: perl ssh exec fork kill


【解决方案1】:

我认为问题在于杀死所有进程树并 That SO answer solves the problem

我有这个给子进程

setpgrp(0, 0);

并将杀死指令更改为

kill 9, -$pid;

现在看起来像这样

my $pid = fork();
die "unable to fork: $!" unless defined($pid);
if (!$pid) {  # child
    setpgrp(0, 0);
    exec("tail -f $logfile | logger -t $ENV{SUDO_USER}:$target ");
    die "unable to exec: $!";
}

$show_cmd && print "% $cmd\n" ;
system $cmd or die "exec() failed: $!\n" ;
printf "Session end pid to kill %d\n", $pid;
kill 9, -$pid;
waitpid $pid, 0;
printf "End of the script.\n";

谢谢你帮助我

【讨论】:

    【解决方案2】:

    这行会有问题

    system $cmd or die "exec() failed: $!\n" ;
    

    因为system命令返回成功后返回值为0。因此,您的程序会在杀死它的孩子之前过早退出。

    看看这个答案: Perl: After a successful system call, "or die" command still ends script

    我认为您应该以不同的方式处理检查用户命令是否已成功执行,或者只是确保将退出值和输出传播给用户。

    你可以这样走:

    system($cmd);
    
    if ($? == -1) {
        print "failed to execute: $!\n";
        kill 1, $pid;
        exit($?);
    } elsif ($? & 127) {
        printf "child died with signal %d, %s coredump\n", ($? & 127),  ($? & 128) ? 'with' : 'without';
        kill 1, $pid;
        exit(3);
    }
    my $exitval =  $? >> 8;
    ...
    

    【讨论】:

    • 谢谢,但这不是这里的问题,在系统指令脚本继续执行后,我在系统后打印,它总是执行。
    • 然后考虑替换/更新您的示例。我不认为你的意思是“总是”,因为当 'system' 返回 0 时,你的子进程不会被杀死。
    猜你喜欢
    • 2013-11-20
    • 1970-01-01
    • 2013-01-27
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    • 2019-01-09
    • 2018-09-22
    • 2011-11-04
    相关资源
    最近更新 更多