Dear Perl Monks,
I have been playing around with IO::Poll in a learning-project of mine. However, when trying to implement a handler for the SIGHUP signal, it seems the errno being returned from poll() is not set to what I expect.
I have written the following code in an attempt to show the exact issue I am facing:
#!/usr/bin/perl
use strict;
use warnings;
use IO::Poll qw(POLLIN POLLHUP POLLERR);
use IO::Socket;
### Behavior of SIGHUP handler.
#
# 0 = print string and nothing else. Errno = "Interrupted system call"
+. Expected behaviour.
# 1 = same as 0 plus open/close of filehandle. Errno = "Inappropriate
+ioctl for device" regardless of contents in file.
# 2 = same as 1 plus reading a line from file into scalar before closi
+ng. If file is empty, then errno is empty. Otherwise, Errno = "Bad fi
+le descriptor"
# 3 = same as 2 but setting $/ to undef, reading until end of file int
+o scalar. Errno is emtpy regardless of contents in file.
# 4 = Similar to 2/3 but reading file into an array. Errno is empty re
+gardless of contents in file.
#
my $behaviour = 0;
### Variables you might want to change.
my $file = "file.txt";
my $poll_timeout = 5;
my $handle;
my $pret;
my $readbuffer;
my $rret;
my $string;
my @ready;
my @strings;
unless (-r $file) {
print "File \"$file\" is not readable...\n";
exit 1;
}
sub SIGHUP_handler {
print "Got SIGHUP...\n";
if ($behaviour == 1) {
open(FILE, $file);
close FILE;
}
elsif ($behaviour == 2) {
open(FILE, $file);
$string = <FILE>;
close FILE;
}
elsif ($behaviour == 3) {
open(FILE, $file);
undef $/;
$string = <FILE>;
close FILE;
}
elsif ($behaviour == 4) {
open(FILE, $file);
@strings = <FILE>;
close FILE;
}
}
$SIG{HUP} = 'SIGHUP_handler';
my $poll = IO::Poll->new;
my $sock = new IO::Socket::INET(
PeerAddr => "127.0.0.1",
PeerPort => "12000",
Proto => 'tcp') or die "Error creating socket: $!";
$poll->mask($sock, POLLIN);
while(1) {
$pret = $poll->poll($poll_timeout);
if ($pret == 0) {
syswrite(STDOUT, "Timeout occured...\n");
}
elsif ($pret == -1) {
syswrite(STDOUT, "Error occured: $!\n");
}
else {
@ready = $poll->handles();
foreach $handle (@ready) {
$rret = sysread($handle, $readbuffer, 1024);
if ($rret == 0) {
syswrite(STDOUT, "No data to read on socket, exiting...\n"
+);
exit 1;
}
syswrite(STDOUT, "msg: $readbuffer");
}
}
}
The problem is this: When the process recieves a signal during a blocking system call, poll(), I am expecting it to return EINTR, or "Interrupted system call". This is what happens when my SIGHUP handler does nothing else but outputting some text.
However, when i start to fiddle around with file handles inside my signal handler, errno is set to something else completely.
What you need to reproduce this:
- A file. The code looks for "file.txt" by default. As is described in the comments in the code, having and not having content inside the file is part of the fun.
- A TCP socket on localhost. I used nc -l -p 12000 to create this on my linux box. The code tries to connect to to port 12000 by default.
- A way to send SIGHUP to the perl process, like kill -HUP <PID> or something.
If you managed to get this far, then please go ahead and start mixing empty/non-empty file with different $behaviour modes.
Could anyone please explain to me why the contents of $! is changing in this way? Is this the expected behaviour? Am I doing something terribly wrong?
I also noticed something (presumably) strange when looking at this with strace. When running the code with $behaviour set to "2" while having some content in the file being opened, I failed to see any sign of a system call returning EBADF, even though $! seems to be set to "Bad file descriptor".
Any feedback is greatly appreciated :).