X-Git-Url: http://dxcluster.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=perl%2Fcluster.pl;h=359ab0975ddd767f923c93d0ed3fe9babe329e3d;hb=23e3e6f8dc328ab0dd7f9ddae444126b0af12867;hp=a2664b7730f3f64676fcce922d3461b66fddc0e5;hpb=78b908b46192179ea1fe23530741ebad18ffe853;p=spider.git diff --git a/perl/cluster.pl b/perl/cluster.pl index a2664b77..359ab097 100755 --- a/perl/cluster.pl +++ b/perl/cluster.pl @@ -47,6 +47,14 @@ BEGIN { $is_win = ($^O =~ /^MS/ || $^O =~ /^OS-2/) ? 1 : 0; # is it Windows? $systime = time; + + sub main::mkver + { + my $s = shift; + my ($v, $b) = $s =~ /(\d+\.\d+)(?:\.(\d+\.\d+))?/; + $main::build += sprintf "%.3f", $v; + $main::branch += sprintf("%.3f", $b) if $b; + } } use DXVars; @@ -68,7 +76,7 @@ use DXCommandmode; use DXProtVars; use DXProtout; use DXProt; -use QXProt; +use Aranea; use DXMsg; use DXCron; use DXConnect; @@ -115,256 +123,24 @@ package main; use strict; use vars qw(@inqueue $systime $version $starttime $lockfn @outstanding_connects $zombies $root @listeners $lang $myalias @debug $userfn $clusteraddr - $clusterport $mycall $decease $is_win $routeroot $me $reqreg + $clusterport $mycall $decease $is_win $routeroot $me $reqreg $bumpexisting + $allowdxby ); @inqueue = (); # the main input queue, an array of hashes $systime = 0; # the time now (in seconds) -$version = "1.51"; # the version no of the software +$version = "2.01"; # the version no of the software $starttime = 0; # the starting time of the cluster #@outstanding_connects = (); # list of outstanding connects @listeners = (); # list of listeners $reqreg = 0; # 1 = registration required, 2 = deregister people +$bumpexisting = 1; # 1 = allow new connection to disconnect old, 0 - don't allow it +$allowdxby = 0; # 1 = allow "dx by ", 0 - don't allow it -use vars qw($VERSION $BRANCH $build $branch); -$VERSION = sprintf( "%d.%03d", q$Revision$ =~ /(\d+)\.(\d+)/ ); -$BRANCH = sprintf( "%d.%03d", q$Revision$ =~ /\d+\.\d+\.(\d+)\.(\d+)/ || (0,0)); -$main::build += 1; # add an offset to make it bigger than last system -$main::build += $VERSION; -$main::branch += $BRANCH; - - -# send a message to call on conn and disconnect -sub already_conn -{ - my ($conn, $call, $mess) = @_; - - $conn->disable_read(1); - dbg("-> D $call $mess\n") if isdbg('chan'); - $conn->send_now("D$call|$mess"); - sleep(2); - $conn->disconnect; -} - -sub error_handler -{ - my $dxchan = shift; - $dxchan->{conn}->set_error(undef) if exists $dxchan->{conn}; - $dxchan->disconnect(1); -} - -# handle incoming messages -sub new_channel -{ - my ($conn, $msg) = @_; - my ($sort, $call, $line) = DXChannel::decode_input(0, $msg); - return unless defined $sort; - - unless (is_callsign($call)) { - already_conn($conn, $call, DXM::msg($lang, "illcall", $call)); - return; - } - # set up the basic channel info - # is there one already connected to me - locally? - my $user = DXUser->get_current($call); - my $dxchan = DXChannel->get($call); - if ($dxchan) { - my $mess = DXM::msg($lang, ($user && $user->is_node) ? 'concluster' : 'conother', $call, $main::mycall); - already_conn($conn, $call, $mess); - return; - } - - # is he locked out ? - my $basecall = $call; - $basecall =~ s/-\d+$//; - my $baseuser = DXUser->get_current($basecall); - my $lock = $user->lockout if $user; - if ($baseuser && $baseuser->lockout || $lock) { - if (!$user || !defined $lock || $lock) { - my $host = $conn->{peerhost} || "unknown"; - Log('DXCommand', "$call on $host is locked out, disconnected"); - $conn->disconnect; - return; - } - } - - if ($user) { - $user->{lang} = $main::lang if !$user->{lang}; # to autoupdate old systems - } else { - $user = DXUser->new($call); - } - - # create the channel - if ($user->is_node) { - $dxchan = DXProt->new($call, $conn, $user); - } elsif ($user->is_user) { - $dxchan = DXCommandmode->new($call, $conn, $user); - } elsif ($user->is_bbs) { - $dxchan = BBS->new($call, $conn, $user); - } else { - die "Invalid sort of user on $call = $sort"; - } - - # check that the conn has a callsign - $conn->conns($call) if $conn->isa('IntMsg'); - - # set callbacks - $conn->set_error(sub {error_handler($dxchan)}); - $conn->set_rproc(sub {my ($conn,$msg) = @_; rec($dxchan, $conn, $msg);}); - rec($dxchan, $conn, $msg); -} - -sub rec -{ - my ($dxchan, $conn, $msg) = @_; - - # queue the message and the channel object for later processing - if (defined $msg) { - my $self = bless {}, "inqueue"; - $self->{dxchan} = $dxchan; - $self->{data} = $msg; - push @inqueue, $self; - } -} - -# remove any outstanding entries on the inqueue after a disconnection (usually) -sub clean_inqueue -{ - my $dxchan = shift; - @inqueue = grep {$_->{dxchan} != $dxchan} @inqueue; -} - -sub login -{ - return \&new_channel; -} - -# cease running this program, close down all the connections nicely -sub cease -{ - my $dxchan; - - unless ($is_win) { - $SIG{'TERM'} = 'IGNORE'; - $SIG{'INT'} = 'IGNORE'; - } - - DXUser::sync; - - eval { - Local::finish(); # end local processing - }; - dbg("Local::finish error $@") if $@; - - # disconnect nodes - foreach $dxchan (DXChannel->get_all_nodes) { - $dxchan->disconnect(2) unless $dxchan == $main::me; - } - Msg->event_loop(100, 0.01); - - # disconnect users - foreach $dxchan (DXChannel->get_all_users) { - $dxchan->disconnect; - } - - # disconnect AGW - AGWMsg::finish(); - - # disconnect UDP customers - UDPMsg::finish(); - - # end everything else - Msg->event_loop(100, 0.01); - DXUser::finish(); - DXDupe::finish(); - - # close all databases - DXDb::closeall; - - # close all listeners - foreach my $l (@listeners) { - $l->close_server; - } - - dbg("DXSpider version $version, build $build ended") if isdbg('chan'); - Log('cluster', "DXSpider V$version, build $build ended"); - dbgclose(); - Logclose(); - unlink $lockfn; -# $SIG{__WARN__} = $SIG{__DIE__} = sub {my $a = shift; cluck($a); }; - exit(0); -} - -# the reaper of children -sub reap -{ - my $cpid; - while (($cpid = waitpid(-1, WNOHANG)) > 0) { - dbg("cpid: $cpid") if isdbg('reap'); -# Msg->pid_gone($cpid); - $zombies-- if $zombies > 0; - } - dbg("cpid: $cpid") if isdbg('reap'); -} - -# this is where the input queue is dealt with and things are dispatched off to other parts of -# the cluster -sub process_inqueue -{ - while (@inqueue) { - my $self = shift @inqueue; - return if !$self; - - my $data = $self->{data}; - my $dxchan = $self->{dxchan}; - my $error; - my ($sort, $call, $line) = DXChannel::decode_input($dxchan, $data); - return unless defined $sort; - - # do the really sexy console interface bit! (Who is going to do the TK interface then?) - dbg("<- $sort $call $line") if $sort ne 'D' && isdbg('chan'); - if ($self->{disconnecting}) { - dbg('In disconnection, ignored'); - next; - } - - # handle A records - my $user = $dxchan->user; - if ($sort eq 'A' || $sort eq 'O') { - $dxchan->start($line, $sort); - } elsif ($sort eq 'I') { - die "\$user not defined for $call" if !defined $user; - - # normal input - $dxchan->normal($line); - } elsif ($sort eq 'Z') { - $dxchan->disconnect; - } elsif ($sort eq 'D') { - ; # ignored (an echo) - } elsif ($sort eq 'G') { - $dxchan->enhanced($line); - } else { - print STDERR atime, " Unknown command letter ($sort) received from $call\n"; - } - } -} - -sub uptime -{ - my $t = $systime - $starttime; - my $days = int $t / 86400; - $t -= $days * 86400; - my $hours = int $t / 3600; - $t -= $hours * 3600; - my $mins = int $t / 60; - return sprintf "%d %02d:%02d", $days, $hours, $mins; -} +use vars qw($VERSION $BRANCH $build $branch); -sub AGWrestart -{ - AGWMsg::init(\&new_channel); -} +mkver($VERSION = q$Revision$); ############################################################# # @@ -389,7 +165,9 @@ $build = "$build.$branch" if $branch; Log('cluster', "DXSpider V$version, build $build started"); # banner -dbg("Copyright (c) 1998-2002 Dirk Koopman G1TLH"); +my ($year) = (gmtime)[5]; +$year += 1900; +dbg("Copyright (c) 1998-$year Dirk Koopman G1TLH"); dbg("DXSpider Version $version, build $build started"); # load Prefixes @@ -423,10 +201,12 @@ dbg("Internal port: $clusteraddr $clusterport using IntMsg"); foreach my $l (@main::listen) { no strict 'refs'; my $pkg = $l->[2] || 'ExtMsg'; - $conn = $pkg->new_server($l->[0], $l->[1], \&login); - $conn->conns("Server $l->[0]/$l->[1] using $pkg"); + my $login = $l->[3] || 'login'; + + $conn = $pkg->new_server($l->[0], $l->[1], \&{"${pkg}::${login}"}); + $conn->conns("Server $l->[0]/$l->[1] using ${pkg}::${login}"); push @listeners, $conn; - dbg("External Port: $l->[0] $l->[1] using $pkg"); + dbg("External Port: $l->[0] $l->[1] using ${pkg}::${login}"); } dbg("AGW Listener") if $AGWMsg::enable; @@ -486,10 +266,10 @@ Spot->init(); # initialise the protocol engine dbg("Start Protocol Engines ..."); DXProt->init(); -QXProt->init(); +Aranea->init(); # put in a DXCluster node for us here so we can add users and take them away -$routeroot = Route::Node->new($mycall, $version*100+5300, Route::here($main::me->here)|Route::conf($main::me->conf)); +$routeroot = Route::Node->new($mycall, int($version*100)+$DXProt::myprot_version, $main::me->here); # make sure that there is a routing OUTPUT node default file #unless (Filter::read_in('route', 'node_default', 0)) { @@ -530,17 +310,21 @@ for (;;) { Msg->event_loop(10, 0.010); my $timenow = time; - process_inqueue(); # read in lines from the input queue and despatch them + + DXChannel::process(); + Thingy::process(); + # $DB::trace = 0; # do timed stuff, ongoing processing happens one a second if ($timenow != $systime) { - reap if $zombies; + rand(); # keep randomising to reduce (but not eliminate) predictability + reap() if $zombies; $systime = $timenow; DXCron::process(); # do cron jobs DXCommandmode::process(); # process ongoing command mode stuff DXProt::process(); # process ongoing ak1a pcxx stuff - QXProt::process(); + Aranea::process(); DXConnect::process(); DXMsg::process(); DXDb::process(); @@ -548,9 +332,6 @@ for (;;) { DXDupe::process(); AGWMsg::process(); - # this where things really start to happen (in DXSpider 2) - Thingy::process(); - eval { Local::process(); # do any localised processing }; @@ -563,4 +344,189 @@ for (;;) { cease(0); exit(0); + +# send a message to call on conn and disconnect +sub already_conn +{ + my ($conn, $call, $mess) = @_; + + $conn->disable_read(1); + dbg("-> D $call $mess\n") if isdbg('chan'); + $conn->send_now("D$call|$mess"); + sleep(2); + $conn->disconnect; +} + +sub error_handler +{ + my $dxchan = shift; + $dxchan->{conn}->set_error(undef) if exists $dxchan->{conn}; + $dxchan->disconnect(1); +} + +# handle incoming messages +sub new_channel +{ + my ($conn, $msg) = @_; + my ($sort, $call, $line) = DXChannel::decode_input(0, $msg); + return unless defined $sort; + + unless (is_callsign($call)) { + already_conn($conn, $call, DXM::msg($lang, "illcall", $call)); + return; + } + + # set up the basic channel info + # is there one already connected to me - locally? + my $user = DXUser->get_current($call); + my $dxchan = DXChannel::get($call); + if ($dxchan) { + if ($user && $user->is_node) { + already_conn($conn, $call, DXM::msg($lang, 'concluster', $call, $main::mycall)); + return; + } + if ($bumpexisting && $call ne $main::mycall) { + my $ip = $conn->{peerhost} || 'unknown'; + $dxchan->send_now('D', DXM::msg($lang, 'conbump', $call, $ip)); + Log('DXCommand', "$call bumped off by $ip, disconnected"); + dbg("$call bumped off by $ip, disconnected"); + $dxchan->disconnect; + } else { + already_conn($conn, $call, DXM::msg($lang, 'conother', $call, $main::mycall)); + return; + } + } + + # is he locked out ? + my $basecall = $call; + $basecall =~ s/-\d+$//; + my $baseuser = DXUser->get_current($basecall); + my $lock = $user->lockout if $user; + if ($baseuser && $baseuser->lockout || $lock) { + if (!$user || !defined $lock || $lock) { + my $host = $conn->{peerhost} || "unknown"; + Log('DXCommand', "$call on $host is locked out, disconnected"); + $conn->disconnect; + return; + } + } + + if ($user) { + $user->{lang} = $main::lang if !$user->{lang}; # to autoupdate old systems + } else { + $user = DXUser->new($call); + } + + # create the channel + if ($user->is_node) { + $dxchan = DXProt->new($call, $conn, $user); + } elsif ($user->is_user) { + $dxchan = DXCommandmode->new($call, $conn, $user); + } elsif ($user->is_bbs) { + $dxchan = BBS->new($call, $conn, $user); + } else { + die "Invalid sort of user on $call = $sort"; + } + + # check that the conn has a callsign + $conn->conns($call) if $conn->isa('IntMsg'); + + # set callbacks + $conn->set_error(sub {error_handler($dxchan)}); + $conn->set_rproc(sub {my ($conn,$msg) = @_; $dxchan->rec($msg);}); + $dxchan->rec($msg); +} + + +sub login +{ + return \&new_channel; +} + +# cease running this program, close down all the connections nicely +sub cease +{ + my $dxchan; + + unless ($is_win) { + $SIG{'TERM'} = 'IGNORE'; + $SIG{'INT'} = 'IGNORE'; + } + + DXUser::sync; + + eval { + Local::finish(); # end local processing + }; + dbg("Local::finish error $@") if $@; + + # disconnect nodes + foreach $dxchan (grep {$_->is_node || $_->is_aranea} DXChannel::get_all()) { + $dxchan->disconnect(2) unless $dxchan == $main::me; + } + Msg->event_loop(100, 0.01); + + # disconnect users + foreach $dxchan (DXChannel::get_all_users) { + $dxchan->disconnect; + } + + # disconnect AGW + AGWMsg::finish(); + + # disconnect UDP customers + UDPMsg::finish(); + + # end everything else + Msg->event_loop(100, 0.01); + DXUser::finish(); + DXDupe::finish(); + + # close all databases + DXDb::closeall; + + # close all listeners + foreach my $l (@listeners) { + $l->close_server; + } + + dbg("DXSpider version $version, build $build ended") if isdbg('chan'); + Log('cluster', "DXSpider V$version, build $build ended"); + dbgclose(); + Logclose(); + unlink $lockfn; +# $SIG{__WARN__} = $SIG{__DIE__} = sub {my $a = shift; cluck($a); }; + exit(0); +} + +# the reaper of children +sub reap +{ + my $cpid; + while (($cpid = waitpid(-1, WNOHANG)) > 0) { + dbg("cpid: $cpid") if isdbg('reap'); +# Msg->pid_gone($cpid); + $zombies-- if $zombies > 0; + } + dbg("cpid: $cpid") if isdbg('reap'); +} + +# this is where the input queue is dealt with and things are dispatched off to other parts of +# the cluster + +sub uptime +{ + my $t = $systime - $starttime; + my $days = int $t / 86400; + $t -= $days * 86400; + my $hours = int $t / 3600; + $t -= $hours * 3600; + my $mins = int $t / 60; + return sprintf "%d %02d:%02d", $days, $hours, $mins; +} + +sub AGWrestart +{ + AGWMsg::init(\&new_channel); +}