X-Git-Url: http://dxcluster.net/gitweb/gitweb.cgi?a=blobdiff_plain;f=perl%2FMsg.pm;h=13833962d3d316d26f997e8861a431d143bda478;hb=309eea6bcd4a0eaf25f810168bc38dc348c2a982;hp=615feb1987e4a102def727f208d093107f63d85b;hpb=407d9a80a8af1fa6c1ae2c8fbca833e49da6e816;p=spider.git diff --git a/perl/Msg.pm b/perl/Msg.pm index 615feb19..13833962 100644 --- a/perl/Msg.pm +++ b/perl/Msg.pm @@ -5,63 +5,33 @@ # # I have modified it to suit my devious purposes (Dirk Koopman G1TLH) # -# $Id$ +# # package Msg; use strict; -use vars qw($VERSION $BRANCH); -$VERSION = sprintf( "%d.%03d", q$Revision$ =~ /(\d+)\.(\d+)/ ); -$BRANCH = sprintf( "%d.%03d", q$Revision$ =~ /\d+\.\d+\.(\d+)\.(\d+)/ ) || 0; -$main::build += $VERSION; -$main::branch += $BRANCH; +use DXUtil; + +use Mojo::IOLoop; +use Mojo::IOLoop::Stream; -use IO::Select; -use IO::Socket; use DXDebug; use Timer; -use vars qw(%rd_callbacks %wt_callbacks %er_callbacks $rd_handles $wt_handles $er_handles $now %conns $noconns $blocking_supported $cnum); +use vars qw($now %conns $noconns $cnum $total_in $total_out $total_lines_in $total_lines_out $connect_timeout $disc_waittime); -%rd_callbacks = (); -%wt_callbacks = (); -%er_callbacks = (); -$rd_handles = IO::Select->new(); -$wt_handles = IO::Select->new(); -$er_handles = IO::Select->new(); +$total_in = $total_out = 0; +$total_lines_in = $total_lines_out = 0; $now = time; -BEGIN { - # Checks if blocking is supported - eval { - require POSIX; POSIX->import(qw(O_NONBLOCK F_SETFL F_GETFL)) - }; - if ($@ || $main::is_win) { -# print STDERR "POSIX Blocking *** NOT *** supported $@\n"; - $blocking_supported = 0; - } else { - $blocking_supported = 1; -# print STDERR "POSIX Blocking enabled\n"; - } - - - # import as many of these errno values as are available - eval { - require Errno; Errno->import(qw(EAGAIN EINPROGRESS EWOULDBLOCK)); - }; -} - -my $w = $^W; -$^W = 0; -my $eagain = eval {EAGAIN()}; -my $einprogress = eval {EINPROGRESS()}; -my $ewouldblock = eval {EWOULDBLOCK()}; -$^W = $w; $cnum = 0; +$connect_timeout = 5; +$disc_waittime = 1.5; +our %delqueue; # #----------------------------------------------------------------- @@ -74,20 +44,24 @@ sub new my $class = $obj || $pkg; my $conn = { - rproc => $rproc, - inqueue => [], - outqueue => [], - state => 0, - lineend => "\r\n", - csort => 'telnet', - timeval => 60, - blocking => 0, - cnum => (($cnum < 999) ? (++$cnum) : ($cnum = 1)), + rproc => $rproc, + inqueue => [], + outqueue => [], + state => 0, + lineend => "\r\n", + csort => 'telnet', + timeval => 60, + blocking => 0, + cnum => (($cnum < 999) ? (++$cnum) : ($cnum = 1)), + linesin => 0, + linesout => 0, + datain => 0, + dataout => 0, }; $noconns++; - dbg("Connection created ($noconns)") if isdbg('connll'); + dbg("$class Connection created (total $noconns)") if isdbg('connll'); return bless $conn, $class; } @@ -95,28 +69,21 @@ sub set_error { my $conn = shift; my $callback = shift; - $conn->{eproc} = $callback; - set_event_handler($conn->{sock}, error => $callback) if exists $conn->{sock}; + $conn->{sock}->on(error => sub {$callback->($_[1]);}); } -sub set_rproc +sub set_on_eof { my $conn = shift; my $callback = shift; - $conn->{rproc} = $callback; + $conn->{sock}->on(close => sub {$callback->()}); } -sub blocking +sub set_rproc { - return unless $blocking_supported; - - my $flags = fcntl ($_[0], F_GETFL, 0); - if ($_[1]) { - $flags &= ~O_NONBLOCK; - } else { - $flags |= O_NONBLOCK; - } - fcntl ($_[0], F_SETFL, $flags); + my $conn = shift; + my $callback = shift; + $conn->{rproc} = $callback; } # save it @@ -129,11 +96,11 @@ sub conns if (ref $pkg) { $call = $pkg->{call} unless $call; return undef unless $call; - dbg("changing $pkg->{call} to $call") if isdbg('connll') && exists $pkg->{call} && $call ne $pkg->{call}; + dbg((ref $pkg) . " changing $pkg->{call} to $call") if isdbg('connll') && exists $pkg->{call} && $call ne $pkg->{call}; delete $conns{$pkg->{call}} if exists $pkg->{call} && exists $conns{$pkg->{call}} && $pkg->{call} ne $call; $pkg->{call} = $call; $ref = $conns{$call} = $pkg; - dbg("Connection $pkg->{cnum} $call stored") if isdbg('connll'); + dbg((ref $pkg) . " Connection $pkg->{cnum} $call stored") if isdbg('connll'); } else { $ref = $conns{$call}; } @@ -152,53 +119,157 @@ sub pid_gone } } +sub ax25 +{ + my $conn = shift; + return $conn->{csort} eq 'ax25'; +} + +sub peerhost +{ + my $conn = shift; + unless ($conn->{peerhost}) { + $conn->{peerhost} ||= 'ax25' if $conn->ax25; + $conn->{peerhost} ||= $conn->{sock}->handle->peerhost if $conn->{sock}; + $conn->{peerhost} ||= 'UNKNOWN'; + } + return $conn->{peerhost}; +} + #----------------------------------------------------------------- # Send side routines -sub connect { - my ($pkg, $to_host, $to_port, $rproc) = @_; +sub _on_connect +{ + my $conn = shift; + my $handle = shift; + undef $conn->{sock}; + my $sock = $conn->{sock} = Mojo::IOLoop::Stream->new($handle); + $sock->on(read => sub {$conn->_rcv($_[1]);} ); + $sock->on(error => sub {delete $conn->{sock}; $conn->disconnect;}); + $sock->on(close => sub {delete $conn->{sock}; $conn->disconnect;}); + $sock->timeout(0); + $sock->start; + $conn->{peerhost} = eval { $handle->peerhost; }; + dbg((ref $conn) . " connected $conn->{cnum} to $conn->{peerhost}:$conn->{peerport}") if isdbg('conn') || isdbg ('connect'); + if ($conn->{on_connect}) { + &{$conn->{on_connect}}($conn, $handle); + } +} + +sub is_connected +{ + my $conn = shift; + my $sock = $conn->{sock}; + return ref $sock && $sock->isa('Mojo::IOLoop::Stream'); +} + +sub connect { + my ($pkg, $to_host, $to_port, %args) = @_; + my $timeout = delete $args{timeout} || $connect_timeout; + # Create a connection end-point object my $conn = $pkg; unless (ref $pkg) { + my $rproc = delete $args{rproc}; $conn = $pkg->new($rproc); } $conn->{peerhost} = $to_host; $conn->{peerport} = $to_port; $conn->{sort} = 'Outgoing'; + + dbg((ref $conn) . " connecting $conn->{cnum} to $to_host:$to_port") if isdbg('connll'); - # Create a new internet socket - my $sock = IO::Socket::INET->new(); - return undef unless $sock; - - my $proto = getprotobyname('tcp'); - $sock->socket(AF_INET, SOCK_STREAM, $proto) or return undef; + my $sock; + $conn->{sock} = $sock = Mojo::IOLoop::Client->new; + $sock->on(connect => sub { + $conn->_on_connect($_[1]) + } ); + $sock->on(error => sub { + &{$conn->{eproc}}($conn, $_[1]) if exists $conn->{eproc}; + delete $conn->{sock}; + $conn->disconnect + }); + $sock->on(close => sub { + delete $conn->{sock}; + $conn->disconnect} + ); + + # copy any args like on_connect, on_disconnect etc + while (my ($k, $v) = each %args) { + $conn->{$k} = $v; + } - blocking($sock, 0); - $conn->{blocking} = 0; - - my $ip = gethostbyname($to_host); -# my $r = $sock->connect($to_port, $ip); - my $r = connect($sock, pack_sockaddr_in($to_port, $ip)); - return undef unless $r || _err_will_block($!); + $sock->connect(address => $to_host, port => $to_port, timeout => $timeout); - $conn->{sock} = $sock; - - if ($conn->{rproc}) { - my $callback = sub {$conn->_rcv}; - set_event_handler ($sock, read => $callback); - } return $conn; } -sub disconnect { - my $conn = shift; - return if exists $conn->{disconnecting}; +sub start_program +{ + my ($conn, $line, $sort) = @_; + my $pid; + +# local $^F = 10000; # make sure it ain't closed on exec +# my ($a, $b) = $io_socket->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC); +# if ($a && $b) { +# $a->autoflush(1); +# $b->autoflush(1); +# $pid = fork; +# if (defined $pid) { +# if ($pid) { +# close $b; +# $conn->{sock} = $a; +# $conn->{csort} = $sort; +# $conn->{lineend} = "\cM" if $sort eq 'ax25'; +# $conn->{pid} = $pid; +# if ($conn->{rproc}) { +# my $callback = sub {$conn->_rcv}; +# Msg::set_event_handler ($a, read => $callback); +# } +# dbg("connect $conn->{cnum}: started pid: $conn->{pid} as $line") if isdbg('connect'); +# } else { +# $^W = 0; +# dbgclose(); +# STDIN->close; +# STDOUT->close; +# STDOUT->close; +# *STDIN = IO::File->new_from_fd($b, 'r') or die; +# *STDOUT = IO::File->new_from_fd($b, 'w') or die; +# *STDERR = IO::File->new_from_fd($b, 'w') or die; +# close $a; +# unless ($main::is_win) { +# # $SIG{HUP} = 'IGNORE'; +# $SIG{HUP} = $SIG{CHLD} = $SIG{TERM} = $SIG{INT} = 'DEFAULT'; +# alarm(0); +# } +# exec "$line" or dbg("exec '$line' failed $!"); +# } +# } else { +# dbg("cannot fork for $line"); +# } +# } else { +# dbg("no socket pair $! for $line"); +# } + return $pid; +} - $conn->{disconnecting} = 1; - my $sock = delete $conn->{sock}; - $conn->{state} = 'E'; - $conn->{timeout}->del if $conn->{timeout}; +sub disconnect +{ + my $conn = shift; + my $count = $conn->{disconnecting}++; + my $dbg = isdbg('connll'); + my ($pkg, $fn, $line) = caller if $dbg; + + if ($count >= 2) { + dbgtrace((ref $conn) . "::disconnect on call $conn->{call} attempt $conn->{disconnecting} called from ${pkg}::${fn} line $line FORCING CLOSE ") if $dbg; + _close_it($conn); + return; + } + dbg((ref $conn) . "::disconnect on call $conn->{call} attempt $conn->{disconnecting} called from ${pkg}::${fn} line $line ") if $dbg; + return if $count; + # remove this conn from the active queue # be careful to delete the correct one my $call; if ($call = $conn->{call}) { @@ -206,12 +277,52 @@ sub disconnect { delete $conns{$call} if $ref && $ref == $conn; } $call ||= 'unallocated'; - dbg("Connection $conn->{cnum} $call disconnected") if isdbg('connll'); + + $delqueue{$conn} = $conn; # save this connection until everything is finished + my $sock = $conn->{sock}; + if ($sock) { + if ($sock->{buffer}) { + my $lth = length $sock->{buffer}; + Mojo::IOLoop->timer($disc_waittime, sub { + dbg("Buffer contained $lth characters, coordinated for $disc_waittime secs, now disconnecting $call") if $dbg; + _close_it($conn); + }); + } else { + dbg("Buffer empty, just close $call") if $dbg; + _close_it($conn); + } + } else { + dbg((ref $conn) . " socket missing on $conn->{call}") if $dbg; + _close_it($conn); + } +} + +sub _close_it +{ + my $conn = shift; + my $sock = delete $conn->{sock}; + $conn->{state} = 'E'; + $conn->{timeout}->del if $conn->{timeout}; + + my $call = $conn->{call}; + + if (isdbg('connll')) { + my ($pkg, $fn, $line) = caller; + dbg((ref $conn) . "::_close_it on call $conn->{call} attempt $conn->{disconnecting} called from ${pkg}::${fn} line $line "); + } + + + dbg((ref $conn) . " Connection $conn->{cnum} $call starting to close") if isdbg('connll'); - unless ($main::is_win) { - kill 'TERM', $conn->{pid} if exists $conn->{pid}; + if ($conn->{on_disconnect}) { + &{$conn->{on_disconnect}}($conn); } + if ($sock) { + dbg((ref $conn) . " Connection $conn->{cnum} $call closing gracefully") if isdbg('connll'); + $sock->close_gracefully if $sock->can('close_gracefully'); + } + # get rid of any references for (keys %$conn) { if (ref($conn->{$_})) { @@ -219,153 +330,113 @@ sub disconnect { } } - return unless defined($sock); - set_event_handler ($sock, read => undef, write => undef, error => undef); - shutdown($sock, 3); - close($sock); + delete $delqueue{$conn}; # finally remove the $conn + + unless ($main::is_win) { + kill 'TERM', $conn->{pid} if exists $conn->{pid}; + } +} + +sub _send_stuff +{ + my $conn = shift; + my $rq = $conn->{outqueue}; + my $sock = $conn->{sock}; + return unless defined $sock; + return if $conn->{disconnecting}; + + while (@$rq) { + my $data = shift @$rq; + my $lth = length $data; + my $call = $conn->{call} || 'none'; + if (isdbg('raw')) { + dbgdump('raw', "$call send $lth:", $data); + } + if (defined $sock) { + $sock->write($data); + $total_out += $lth; + $conn->{dataout} += $lth; + ++$conn->{linesout}; + ++$total_lines_out; + } else { + dbg("_send_stuff $call ending data ignored: $data"); + } + } } sub send_now { my ($conn, $msg) = @_; $conn->enqueue($msg); - $conn->_send (1); # 1 ==> flush + _send_stuff($conn); } sub send_later { + goto &send_now; +} + +sub send_raw +{ my ($conn, $msg) = @_; - $conn->enqueue($msg); - my $sock = $conn->{sock}; - return unless defined($sock); - set_event_handler ($sock, write => sub {$conn->_send(0)}); + push @{$conn->{outqueue}}, $msg; + _send_stuff($conn); } sub enqueue { my $conn = shift; - push (@{$conn->{outqueue}}, defined $_[0] ? $_[0] : ''); -} - -sub _send { - my ($conn, $flush) = @_; - my $sock = $conn->{sock}; - return unless defined($sock); - my $rq = $conn->{outqueue}; - - # If $flush is set, set the socket to blocking, and send all - # messages in the queue - return only if there's an error - # If $flush is 0 (deferred mode) make the socket non-blocking, and - # return to the event loop only after every message, or if it - # is likely to block in the middle of a message. - - if ($conn->{blocking} != $flush) { - blocking($sock, $flush); - $conn->{blocking} = $flush; - } - my $offset = (exists $conn->{send_offset}) ? $conn->{send_offset} : 0; - - while (@$rq) { - my $msg = $rq->[0]; - my $mlth = length($msg); - my $bytes_to_write = $mlth - $offset; - my $bytes_written = 0; - confess("Negative Length! msg: '$msg' lth: $mlth offset: $offset") if $bytes_to_write < 0; - while ($bytes_to_write > 0) { - $bytes_written = syswrite ($sock, $msg, - $bytes_to_write, $offset); - if (!defined($bytes_written)) { - if (_err_will_block($!)) { - # Should happen only in deferred mode. Record how - # much we have already sent. - $conn->{send_offset} = $offset; - # Event handler should already be set, so we will - # be called back eventually, and will resume sending - return 1; - } else { # Uh, oh - &{$conn->{eproc}}($conn, $!) if exists $conn->{eproc}; - $conn->disconnect; - return 0; # fail. Message remains in queue .. - } - } elsif (isdbg('raw')) { - my $call = $conn->{call} || 'none'; - dbgdump('raw', "$call send $bytes_written: ", $msg); - } - $offset += $bytes_written; - $bytes_to_write -= $bytes_written; - } - delete $conn->{send_offset}; - $offset = 0; - shift @$rq; - #last unless $flush; # Go back to select and wait - # for it to fire again. - } - # Call me back if queue has not been drained. - unless (@$rq) { - set_event_handler ($sock, write => undef); - if (exists $conn->{close_on_empty}) { - &{$conn->{eproc}}($conn, undef) if exists $conn->{eproc}; - $conn->disconnect; - } - } - 1; # Success + push @{$conn->{outqueue}}, defined $_[0] ? $_[0] : ''; } -sub dup_sock +sub _err_will_block { - my $conn = shift; - my $oldsock = $conn->{sock}; - my $rc = $rd_callbacks{$oldsock}; - my $wc = $wt_callbacks{$oldsock}; - my $ec = $er_callbacks{$oldsock}; - my $sock = $oldsock->new_from_fd($oldsock, "w+"); - if ($sock) { - set_event_handler($oldsock, read=>undef, write=>undef, error=>undef); - $conn->{sock} = $sock; - set_event_handler($sock, read=>$rc, write=>$wc, error=>$ec); - $oldsock->close; - } -} - -sub _err_will_block { - return 0 unless $blocking_supported; - return ($_[0] == $eagain || $_[0] == $ewouldblock || $_[0] == $einprogress); + return 0; } sub close_on_empty { my $conn = shift; - $conn->{close_on_empty} = 1; + $conn->{sock}->on(drain => sub {$conn->disconnect;}); } #----------------------------------------------------------------- # Receive side routines -sub new_server { - @_ == 4 || die "Msg->new_server (myhost, myport, login_proc\n"; - my ($pkg, $my_host, $my_port, $login_proc) = @_; - my $self = $pkg->new($login_proc); +sub new_server +{ +# @_ == 4 || die "Msg->new_server (myhost, myport, login_proc)\n"; + my ($pkg, $my_host, $my_port, $login_proc) = @_; + my $conn = $pkg->new($login_proc); - $self->{sock} = IO::Socket::INET->new ( - LocalAddr => $my_host, - LocalPort => $my_port, - Listen => SOMAXCONN, - Proto => 'tcp', - Reuse => 1); - die "Could not create socket: $! \n" unless $self->{sock}; - set_event_handler ($self->{sock}, read => sub { $self->new_client } ); - return $self; + my $sock = $conn->{sock} = Mojo::IOLoop::Server->new; + $sock->on(accept=>sub{$conn->new_client($_[1]);}); + $sock->listen(address=>$my_host, port=>$my_port); + $sock->start; + + die "Could not create socket: $! \n" unless $conn->{sock}; + return $conn; } -sub dequeue + +sub nolinger { my $conn = shift; +} - if ($conn->{msg} =~ /\n/) { - my @lines = split /\r?\n/, $conn->{msg}; - if ($conn->{msg} =~ /\n$/) { +sub dequeue +{ + my $conn = shift; + return if $conn->{disconnecting}; + + if ($conn->{msg} =~ /\cJ/) { + my @lines = split /\cM?\cJ/, $conn->{msg}; + if ($conn->{msg} =~ /\cM?\cJ$/) { delete $conn->{msg}; } else { $conn->{msg} = pop @lines; } + $conn->{linesin} += @lines; + $total_lines_in += @lines; for (@lines) { + last if $conn->{disconnecting}; &{$conn->{rproc}}($conn, defined $_ ? $_ : ''); } } @@ -373,94 +444,74 @@ sub dequeue sub _rcv { # Complement to _send my $conn = shift; # $rcv_now complement of $flush - # Find out how much has already been received, if at all - my ($msg, $offset, $bytes_to_read, $bytes_read); + my $msg = shift; my $sock = $conn->{sock}; return unless defined($sock); + return if $conn->{disconnecting}; + + $total_in += length $msg; + $conn->{datain} += length $msg; - my @lines; - if ($conn->{blocking}) { - blocking($sock, 0); - $conn->{blocking} = 0; + if (isdbg('raw')) { + my $call = $conn->{call} || 'none'; + my $lth = length $msg; + dbgdump('raw', "$call read $lth: ", $msg); } - $bytes_read = sysread ($sock, $msg, 1024, 0); - if (defined ($bytes_read)) { - if ($bytes_read > 0) { - if (isdbg('raw')) { - my $call = $conn->{call} || 'none'; - dbgdump('raw', "$call read $bytes_read: ", $msg); - } - if ($conn->{echo}) { - my @ch = split //, $msg; - my $out; - for (@ch) { - if (/[\cH\x7f]/) { - $out .= "\cH \cH"; - $conn->{msg} =~ s/.$//; - } else { - $out .= $_; - $conn->{msg} .= $_; - } - } - if (defined $out) { - set_event_handler ($sock, write => sub{$conn->_send(0)}); - push @{$conn->{outqueue}}, $out; + if ($conn->{echo}) { + my @ch = split //, $msg; + my $out; + for (@ch) { + if (/[\cH\x7f]/) { + $out .= "\cH \cH"; + $conn->{msg} =~ s/.$//; + } else { + $out .= $_; + $conn->{msg} .= $_; } - } else { - $conn->{msg} .= $msg; } - } + if (defined $out) { + $conn->send_raw($out); + } } else { - if (_err_will_block($!)) { - return ; - } else { - $bytes_read = 0; - } - } + $conn->{msg} .= $msg; + } -FINISH: - if (defined $bytes_read && $bytes_read == 0) { - &{$conn->{eproc}}($conn, $!) if exists $conn->{eproc}; - $conn->disconnect; - } else { - unless ($conn->{disable_read}) { - $conn->dequeue if exists $conn->{msg}; - } + unless ($conn->{disable_read}) { + $conn->dequeue if exists $conn->{msg}; } } sub new_client { my $server_conn = shift; - my $sock = $server_conn->{sock}->accept(); - if ($sock) { - my $conn = $server_conn->new($server_conn->{rproc}); - $conn->{sock} = $sock; - blocking($sock, 0); - $conn->{blocking} = 0; - my ($rproc, $eproc) = &{$server_conn->{rproc}} ($conn, $conn->{peerhost} = $sock->peerhost(), $conn->{peerport} = $sock->peerport()); - $conn->{sort} = 'Incoming'; - if ($eproc) { - $conn->{eproc} = $eproc; - set_event_handler ($sock, error => $eproc); - } - if ($rproc) { - $conn->{rproc} = $rproc; - my $callback = sub {$conn->_rcv}; - set_event_handler ($sock, read => $callback); - } else { # Login failed - &{$conn->{eproc}}($conn, undef) if exists $conn->{eproc}; - $conn->disconnect(); - } - } else { - dbg("Msg: error on accept ($!)") if isdbg('err'); + my $handle = shift; + + my $conn = $server_conn->new($server_conn->{rproc}); + my $sock = $conn->{sock} = Mojo::IOLoop::Stream->new($handle); + $sock->on(read => sub {$conn->_rcv($_[1])}); + $sock->timeout(0); + $sock->start; + $conn->{peerhost} = $handle->peerhost || 'unknown'; + $conn->{peerhost} =~ s|^::ffff:||; # chop off leading pseudo IPV6 stuff on dual stack listeners + $conn->{peerport} = $handle->peerport || 0; + dbg((ref $conn) . " accept $conn->{cnum} from $conn->{peerhost}:$conn->{peerport}") if isdbg('conn') || isdbg('connect'); + my ($rproc, $eproc) = &{$server_conn->{rproc}} ($conn, $conn->{peerhost}, $conn->{peerport}); + $conn->{sort} = 'Incoming'; + if ($eproc) { + $conn->{eproc} = $eproc; } + if ($rproc) { + $conn->{rproc} = $rproc; + } else { # Login failed + &{$conn->{eproc}}($conn, undef) if exists $conn->{eproc}; + $conn->disconnect(); + } + return $conn; } sub close_server { my $conn = shift; - set_event_handler ($conn->{sock}, read => undef, write => undef, error => undef ); - $conn->{sock}->close; + delete $conn->{sock}; } # close all clients (this is for forking really) @@ -474,76 +525,24 @@ sub close_all_clients sub disable_read { my $conn = shift; - set_event_handler ($conn->{sock}, read => undef); - return $_[0] ? $conn->{disable_read} = $_[0] : $_[0]; + return defined $_[0] ? $conn->{disable_read} = $_[0] : $_[0]; } + # #---------------------------------------------------- # Event loop routines used by both client and server sub set_event_handler { - shift unless ref($_[0]); # shift if first arg is package name - my ($handle, %args) = @_; - my $callback; - if (exists $args{'write'}) { - $callback = $args{'write'}; - if ($callback) { - $wt_callbacks{$handle} = $callback; - $wt_handles->add($handle); - } else { - delete $wt_callbacks{$handle}; - $wt_handles->remove($handle); - } - } - if (exists $args{'read'}) { - $callback = $args{'read'}; - if ($callback) { - $rd_callbacks{$handle} = $callback; - $rd_handles->add($handle); - } else { - delete $rd_callbacks{$handle}; - $rd_handles->remove($handle); - } - } - if (exists $args{'error'}) { - $callback = $args{'error'}; - if ($callback) { - $er_callbacks{$handle} = $callback; - $er_handles->add($handle); - } else { - delete $er_callbacks{$handle}; - $er_handles->remove($handle); - } - } -} - -sub event_loop { - my ($pkg, $loop_count, $timeout) = @_; # event_loop(1) to process events once - my ($conn, $r, $w, $e, $rset, $wset, $eset); - while (1) { - - # Quit the loop if no handles left to process - last unless ($rd_handles->count() || $wt_handles->count()); - - ($rset, $wset, $eset) = IO::Select->select($rd_handles, $wt_handles, $er_handles, $timeout); - - foreach $e (@$eset) { - &{$er_callbacks{$e}}($e) if exists $er_callbacks{$e}; - } - foreach $r (@$rset) { - &{$rd_callbacks{$r}}($r) if exists $rd_callbacks{$r}; - } - foreach $w (@$wset) { - &{$wt_callbacks{$w}}($w) if exists $wt_callbacks{$w}; - } - - Timer::handler; - - if (defined($loop_count)) { - last unless --$loop_count; - } - } + my $sock = shift; + my %args = @_; + my ($pkg, $fn, $line) = caller; + my $s; + foreach (my ($k,$v) = each %args) { + $s .= "$k => $v, "; + } + $s =~ s/[\s,]$//; + dbg("Msg::set_event_handler called from ${pkg}::${fn} line $line doing $s"); } sub sleep @@ -551,18 +550,32 @@ sub sleep my ($pkg, $interval) = @_; my $now = time; while (time - $now < $interval) { - $pkg->event_loop(10, 0.01); + sleep 1; } } sub DESTROY { my $conn = shift; + my $call = $conn->{call} || 'unallocated'; + + if (isdbg('connll')) { + my ($pkg, $fn, $line) = caller; + dbgtrace((ref $conn) . "::DESTROY on call $call called from ${pkg}::${fn} line $line "); + } + my $call = $conn->{call} || 'unallocated'; my $host = $conn->{peerhost} || ''; my $port = $conn->{peerport} || ''; - dbg("Connection $conn->{cnum} $call [$host $port] being destroyed") if isdbg('connll'); + my $sock = $conn->{sock}; + + if ($sock) { + $sock->close_gracefully if $sock->can('close_gracefully'); + delete $conn->{sock}; + } + $noconns--; + dbg((ref $conn) . " Connection $conn->{cnum} $call [$host $port] being destroyed (total $noconns)") if isdbg('connll'); } 1;