# ho_sping.pl # # $Id: ho_sping.pl,v 1.2 2004/09/04 11:20:45 jvunder REL_0_3 $ # # Part of the Hybrid Oper Script Collection. # # Does a latency check of all linked servers on a network. # # Ping choopa from efnet.nl: # /quote ping irc.efnet.nl :irc.choopa.net use strict; use vars qw($VERSION %IRSSI $SCRIPT_NAME); use Irssi; use Irssi::Irc; # necessary for redirect_register() use HOSC::again; use HOSC::again 'HOSC::Base'; use HOSC::again 'HOSC::Tools'; eval { require Time::HiRes; }; if ($@) { Irssi::print("You need Time::HiRes for this script. Please install ". "it or upgrade to Perl 5.8."); return 0; } import Time::HiRes qw(gettimeofday tv_interval); # Any server replying slower than this is considered to be slow. my $SLOW_TIME = 2; # --------------------------------------------------------------------- ($VERSION) = '$Revision: 1.2 $' =~ / (\d+\.\d+) /; %IRSSI = ( authors => 'Garion', contact => 'garion@efnet.nl', name => 'ho_sping', description => 'Checks the latency of all linked servers.', license => 'Public Domain', url => 'http://www.garion.org/irssi/hosc.php', changed => '07 August 2004 12:27:30', ); $SCRIPT_NAME = 'Sping'; # Hashtable with server latency. # Key is the server name. # Value is the ping delay, in seconds. my %server_pings; my %data = ( currently_busy => 0, servers_linked => 0, servers_processed => 0, my_server_tag => undef, timer_gather_id => undef, timer_gather_done_id => undef, time_started_tv => undef, ); # --------------------------------------------------------------------- sub cmd_sping { my ($data, $server, $item) = @_; if ($data =~ m/^[(help)]/i ) { Irssi::command_runsub ('sping', $data, $server, $item); return; } if ($data{currently_busy}) { ho_print_error("Sorry, already performing a latency check."); return; } ho_print("Checking latency of all linked servers on " . $server->{tag} . "."); ho_print("Please wait up to " . Irssi::settings_get_int('ho_sping_max_time') . " seconds."); $server->redirect_event('command cmd_sping', 1, undef, 0, undef, { 'event 364' => 'redir event_links_line', 'event 365' => 'redir event_links_end', } ); delete $server_pings{$_} for keys %server_pings; $data{currently_busy} = 1; $data{servers_linked} = 0; $data{servers_processed} = 0; $data{my_server_tag} = $server->{tag}; $data{time_started_tv} = [gettimeofday()]; # Now send LINKS to obtain a list of all linked servers. Then we can # send a PING for each server. $server->send_raw_now('LINKS'); } # --------------------------------------------------------------------- sub cmd_sping_help { print_help(); } # --------------------------------------------------------------------- sub event_links_line { my ($server, $args, $nick, $address) = @_; if ($args =~ /^\S+\s+(\S+)\s/) { $server_pings{$1} = undef; } Irssi::signal_stop(); $data{servers_linked}++; } # --------------------------------------------------------------------- sub event_links_end { my ($server, $args, $nick, $address) = @_; # We've obtained the complete list of servers. Now go send a PING # for each one. send_pings($server); Irssi::signal_stop(); } # --------------------------------------------------------------------- sub send_pings { my ($server) = @_; # Here we'll send a PING $myserver :$servername for each server. # Then we wait until the last pong gets back, or up to # sversion_max_time seconds, whichever occurs first. During this # time we will steal all PONG replies and signal_stop them. my $own_name = $server->{real_address}; for my $sname (keys %server_pings) { $server->command("QUOTE PING $own_name :$sname"); #print ("QUOTE PING $own_name :$sname"); } # We -must- have a timeout on this latency gathering in case one or # more servers fail to reply. The latency gathering is considered to # be complete as soon as all pong replies have been received, or # this timer is executed, whichever occurs first. my $time = Irssi::settings_get_int('ho_sping_max_time'); $time = 10 if $time < 10; $data{timer_gather_id} = Irssi::timeout_add($time * 1000, 'gather_completed', undef); } # --------------------------------------------------------------------- sub event_pong { my ($server, $args, $nick, $address, $target) = @_; return unless $data{currently_busy}; my ($sname, $me) = $args =~ /^(\S+)\s+(\S+)$/; if ($sname) { $server_pings{$sname} = [gettimeofday()]; Irssi::signal_stop(); $data{servers_processed}++; if ($data{servers_linked} == $data{servers_processed}) { gather_completed(); } } } # --------------------------------------------------------------------- sub gather_completed { if ($data{timer_gather_id}) { Irssi::timeout_remove($data{timer_gather_id}); undef $data{timer_gather_id}; } if ($data{timer_gather_done_id}) { Irssi::timeout_remove($data{timer_gather_done_id}); undef $data{timer_gather_done_id}; } $data{currently_busy} = 0; print_pings(); } # --------------------------------------------------------------------- sub print_pings { my ($server) = @_; my @slow_servers = (); my $num_total_servers = scalar keys %server_pings; my %time_diffs; for my $sname (keys %server_pings) { my $timediff = tv_interval($data{time_started_tv}, $server_pings{$sname}); my $timediff_fmt = sprintf "%.2f", $timediff; $time_diffs{$sname} = $timediff_fmt; if ($timediff > $SLOW_TIME) { push @slow_servers, $sname; } } # Print short report. if (scalar @slow_servers == 0) { ho_print("All $num_total_servers servers replied within ". "$SLOW_TIME seconds."); } elsif (scalar @slow_servers == 1) { ho_print("All $num_total_servers servers except $slow_servers[0] ". "replied within $SLOW_TIME seconds."); } else { ho_print("Out of $num_total_servers servers, the following " . (scalar @slow_servers) . " servers ". "replied slower than $SLOW_TIME seconds:"); ho_print(join ' ', @slow_servers); } # If desired, print full report. if (Irssi::settings_get_bool('ho_sping_full_report')) { ho_print('Server pings:'); for my $sname (sort keys %server_pings) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'ho_sping_line', $sname, $time_diffs{$sname}); } ho_print("Total servers linked: $data{servers_linked}."); } } # --------------------------------------------------------------------- ho_print_init_begin(); # The redirect for LINKS output. Irssi::Irc::Server::redirect_register('command cmd_sping', 0, 0, { 'event 364' => 1, }, { 'event 365' => 1, }, undef ); Irssi::signal_add('redir event_links_line', 'event_links_line'); Irssi::signal_add('redir event_links_end', 'event_links_end'); Irssi::signal_add_first('event pong', 'event_pong'); Irssi::command_bind('sping', 'cmd_sping'); Irssi::command_bind('sping help', 'cmd_sping_help'); Irssi::settings_add_int('ho', 'ho_sping_max_time', 20); Irssi::settings_add_bool('ho', 'ho_sping_full_report', 0); Irssi::theme_register([ 'ho_sping_line', '$[25]0 - $1s', ]); ho_print_init_end(); ho_print("Use /SPING HELP for help."); # --------------------------------------------------------------------- sub print_help { ho_print_help('head', $SCRIPT_NAME); ho_print_help('section', 'Description'); ho_print_help("This script does a latency check ". "of all servers on the network."); ho_print_help("It does so by first issuing /LINKS and then doing a ". "/PING for each server."); ho_print_help("Make sure your settings 'cmds_max_at_once' and ". "'cmd_queue_speed' are set to proper values so this script can ". "issue the /PING commands as quickly as possible without ". "being disconnected for excess flood.\n"); ho_print_help('section', 'Syntax'); ho_print_help('syntax', 'SPING [HELP]'); ho_print_help('section', 'Settings'); ho_print_help('setting', 'ho_sping_max_time', 'Maximum time to wait for PONG replies.'); ho_print_help('setting', 'ho_sping_full_report', 'Whether or not to print a full report.'); } # ---------------------------------------------------------------------