Reputation: 3011
This is my program
my $input;
my $finish = 0;
my $timer = 5; # 5 seconds
eval {
while( ! $finish ) {
local $SIG{ALRM} = sub {
# check counter and set alarm again
if (--$timer) { alarm 1 }
# no more waiting
else { die "timeout getting the input \n" }
};
# alarm every second
alarm 1;
$input = <STDIN>;
alarm 0;
if ( $input ) {
chomp $input;
if( $input ){
print( "Received input : $input" );
$finish = 1;
} else {
print( "Please enter valid input" );
}
} else {
print( "input is undefined" );
last;
}
}
};
if ($@ || !$input) {
print( "Timeout in getting input." );
return undef;
}
return $input;
STDIN works fine in foreground. But fails when running the same program background. The logic is to go out of the loop if user did not enter any input within 5 seconds. But when running in background, the process should exit within x seconds but the process gets stuck in line <STDIN>
.
How to fix this?
Upvotes: 2
Views: 171
Reputation: 239682
When a backgrounded process attempts terminal input, it gets sent the signal SIGTTIN
, which has a default action of "stop" — i.e. the process is paused, the same as if it had received SIGSTOP
. Since it's stopped, it doesn't get a chance to process its alarm, or exit, or do anything else.
So, you're going to want to set a SIGTTIN
handler to override that default stop behavior.
The simplest thing you could do would be local $SIG{TTIN} = 'IGNORE';
which would cause signal to be ignored. The read of STDIN
would then immediately fail (returns undef with $!
set to EIO
), and you would hit the "input is undefined" case in your code.
You could also set local $SIG{TTIN} = sub { die "process is backgrounded" };
which would let you distinguish that case more clearly.
Or you could set up something clever with a signal handler and retrying the read, so that in case the user decided to background and re-attach before the timeout expired, they would still be able to provide input, but I leave that up to you.
Upvotes: 3