differenceengine has asked for the wisdom of the Perl Monks concerning the following question:

Greetings monks...

I have a Perl program which is an interactive shell style affair which is run by sshd on a remote server via sshd's ForceCommand. So I can ssh to one of my servers and I get to issue commands to my Perl program to run other Perl scripts which carry out system maintenance etc. It all works quite nicely. But if I want to tail -f a file like so...

if($cmd[0] eq "tail") { open(TAIL,"|tail -f /var/log/messages"); while(<TAIL>) { print $_; } } }

... I need a way to break out of it. So I came up with this...

if($cmd[0] eq "tail") { open(TAIL,"|tail -f /var/log/messages"); my $tailing = 1; sub __int_tail { $tailing = 0; } $SIG{'INT'} = \&__int_tail; while($tailing) { while(<TAIL>) { print $_; } } return 0; }

... which works perfectly if I run if I run my Perl shell from a real shell. But if I ssh to my host the SIGINT gets captured by the ssh client on the local host.

So the question is, how do I capture or override SIGINT in a parent process that has forked (?) Perl? In fact, what even is the parent process in this case? sshd, a pseudo tty, my ssh client?

I guess this can be done because a real Unix shell in a ssh session can capture Ctrl-C issued locally and use it on the remote session.

Any wisdom would be most appreciated!

Replies are listed 'Best First'.
Re: Perl SIG INT handling conundrum.
by Neighbour (Friar) on Aug 16, 2011 at 14:51 UTC
    Have you tried other signals? I would expect SIGHUP to be passed on to the child-processes of the sshclient.
Re: Perl SIG INT handling conundrum.
by qleem (Initiate) on Aug 16, 2011 at 18:16 UTC

    You may try editing your ssh command to allocate a tty. I'm not sure if this will work with forcecommand though, as I believe it is often used to prohibit this behavior.

     ssh -t user@remote

    -t Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty.

      Thanks for your responses chaps...

      Have been working on this since I posted and found the solution....

      system("trap '' INT;");

      It turns out sshd forces commands by passing them to the user's shell with -c, e.g. /bin/sh -c "forced command".

      So it was the remote shell which was catching the Ctrl-C and once the remote ssh client noticed the shell had exited it was also exiting, making it look somewhat like the local side had caught the INT.

      The above Perl statement runs a shell command that tells the shell to ignore signal INT so it is available to Perl. I guess when I was running locally from a login shell something in Perl automatically overrides this when you set a custom signal handler.

      Not all shells support trap - sh, bash, ksh do but csh and tcsh do not. At least on FreeBSD which is what i'm developing with.

      Thanks again for the suggestions and I hope this might be useful to someone sometime!

        Just as a side note, thinking about it there won't be anything in Perl that overrides signal handling. The important thing here is the order in which the processes receive the signal. Using ssh, the shell receives the signal first and it will only let it "bubble" (to borrow event driven terminology) if it doesn't handle it. Running a Perl script from a shell, the script receives the signal first and if there's no handler passes it down to the shell. Which is as it should be.