2

Hi I'm trying to log everything that users do from my server. I have a script that replaces ssh and log everything.

The problem is when the user stops the ssh session, the child process that logs the actions isn't killed.

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";

I put also

$SIG{CHLD} = "IGNORE";

If I deleted the instruction system (the instruction that launch the original ssh command of the user) the child is killed but that makes my script useless, too.

Any ideas how to terminate the process successfully?

EDIT : when the system commands end the script continue, printf is executed, and print the child's pid.

EDIT2 :

This is a 'ps faux' during an ssh session

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

At the end of the session : ^D from the user

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

And the result of the 'ps faux' :

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

So at the end there is still these two process attach to nothing.

  • There's some quite significant privacy and potentially legal considerations to monitoring someone's access to a third party server. Are you sure that's what you want to be doing? – Sobrique Nov 26 '14 at 11:15
  • No there is no problem with that, the usage is for the company to monitor the actions done on all the servers. – Gecko Splinter Nov 26 '14 at 12:47
  • Is there some reason you can't use the built in accounting and audit mechanisms then? – Sobrique Nov 26 '14 at 13:13

2 Answers2

4

I figured that the problem was to kill the all process tree and That SO answer solves the problem

I had this to the child process

setpgrp(0, 0);

And change the kill instruction to

kill 9, -$pid;

Looks like this now

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";

Thanks for helping me

Community
  • 1
  • 1
1

You will have a problem with this line

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

since system return value will be 0 when the command have returned successfully. Hence your program would exit too early here, before killing its child.

Have a look at this answer: Perl: After a successful system call, "or die" command still ends script

I think you should handle differently the way you check a user's command have been executed successfully, or simply by making sure that the exit value and outputs are propagated to the user.

You may go this way:

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;
...
Community
  • 1
  • 1
Wiil
  • 629
  • 3
  • 8
  • 22
  • Thanks but that's not the problem here, after the system instruction the script continue, I put a print after the system and it's always executed. – Gecko Splinter Nov 26 '14 at 12:45
  • Then consider replacing/updating your example. I don't think you really meant "always" since your child process won't be killed when 'system' returns 0. – Wiil Nov 26 '14 at 14:41