1

I have an IRC bot written in Perl, using the deprecated, undocumented and unloved Net::IRC library. Still, it runs just fine... unless the connection goes down. It appears that the library ceased to be updated before they've implemented support for reconnecting. The obvious solution would be to rewrite the whole bot to make use of the library's successors, but that would unfortunately require rewriting the whole bot.

So I'm interested in workarounds.

Current setup I have is supervisord configured to restart the bot whenever the process exits unexpectedly, and a cron job to kill the process whenever internet connectivity is lost.

This does not work as I would like it to, because the bot seems incapable of detecting that it has lost connectivity due to internet outage. It will happily continue running, doing nothing, pretending to still be connected to the IRC server.

I have the following code as the main program loop:

while (1) {
    $irc->do_one_loop;
    # can add stuff here
}

What I would like it to do is:
a) detect that the internet has gone down,
b) wait until the internet has gone up,
c) exit the script, so that supervisord can resurrect it.

Are there any other, better ways of doing this?

EDIT: The in-script method did not work, for unknown reasons. I'm trying to make a separate script to solve it.

#!/usr/bin/perl

use Net::Ping::External;

while (1) { 
    while (Net::Ping::External::ping(host => "8.8.8.8")) { sleep 5; }

    sleep 5 until Net::Ping::External::ping(host => "8.8.8.8");
    system("sudo kill `pgrep -f 'perl painbot.pl'`");
}
4

2 回答 2

1

Assuming that do_one_loop will not hang (may need to add some alarm if it does), you'll need to actively poll something to tell whether or not the network is up. Something like this should work to ping every 5 seconds after a failure until you get a response, then exit.

use Net::Ping::External;
sub connectionCheck {
    return if Net::Ping::External::ping(host => "8.8.8.8");

    sleep 5 until Net::Ping::External::ping(host => "8.8.8.8");
    exit;
}

Edit: Since do_one_loop does seem to hang, you'll need some way to wrap a timeout around it. The amount of time depends on how long you expect it to run for, and how long you are willing to wait if it becomes unresponsive. A simple way to do this is using alarm (assuming you are not on windows):

local $SIG{'ALRM'} = sub { die "Timeout" };
alarm 30; # 30 seconds
eval {
    $irc->do_one_loop;
    alarm 0;
};
于 2013-10-10T21:25:29.007 回答
1

The Net::IRC main loop has support for timeouts and scheduled events.

Try something like this (I haven't tested it, and it's been 7 years since I last used the module...):

# connect to IRC, add event handlers, etc.
$time_of_last_ping = $time_of_last_pong = time;
$irc->timeout(30);
# Can't handle PONG in Net::IRC (!), so handle "No origin specified" error
# (this may not work for you; you may rather do this some other way)
$conn->add_handler(409, sub { $time_of_last_pong = time });
while (1) {
    $irc->do_one_loop;
    # check internet connection: send PING to server
    if ( time-$time_of_last_ping > 30 ) {
        $conn->sl("PING"); # Should be "PING anything"
        $time_of_last_ping = time;
    }
    break if time-$time_of_last_pong > 90;
}
于 2013-10-13T16:51:27.227 回答