8b3f3ccef0d6bc6ad75a6f512b8e4101115557c8
[spider.git] / perl / Thingy.pm
1 #
2 # Thingy handling
3 #
4 # This is the new fundamental protocol engine handler
5
6 # This is where all the new things (and eventually all the old things
7 # as well) happen.
8 #
9 # $Id$
10 #
11 # Copyright (c) 2004 Dirk Koopman G1TLH
12 #
13
14 use strict;
15
16 package Thingy;
17
18 use vars qw($VERSION $BRANCH @queue @permin @persec);
19
20 main::mkver($VERSION = q$Revision$);
21
22 @queue = ();                                    # the input / processing queue
23
24 #
25 # these are set up using the Thingy->add_second_process($addr, $name)
26 # and Thingy->add_minute_process($addr, $name)
27 #
28 # They replace the old cycle in cluster.pl
29 #
30
31 @persec = ();                                   # this replaces the cycle in cluster.pl
32 @permin = ();                                   # this is an extra per minute cycle
33
34 my $lastsec = time;
35 my $lastmin = time;
36
37 use DXChannel;
38 use DXDebug;
39
40 # we expect all thingies to be subclassed
41 sub new
42 {
43         my $class = shift;
44         my $thing = {@_};
45
46         $thing->{origin} ||= $main::mycall;
47         
48         bless $thing, $class;
49         return $thing;
50 }
51
52 # send it out in the format asked for, if available
53 sub send
54 {
55         my $thing = shift;
56         my $dxchan = shift;
57         my $class;
58         if (@_) {
59                 $class = shift;
60         } elsif ($dxchan->isa('DXChannel')) {
61                 $class = ref $dxchan;
62         }
63
64         # do output filtering
65         if ($thing->can('out_filter')) {
66                 return unless $thing->out_filter($dxchan);
67         }
68
69         # generate the line which may (or not) be cached
70         my $ref;
71         unless ($ref = $thing->{class}) {
72                 no strict 'refs';
73                 my $sub = "gen_$class";
74                 $ref = $thing->$sub($dxchan) if $thing->can($sub);
75         }
76         $dxchan->send(ref $ref ? @$ref : $ref) if $ref;
77 }
78
79 # broadcast to all except @_
80 sub broadcast
81 {
82         my $thing = shift;
83         dbg("Thingy::broadcast: " . $thing->ascii) if isdbg('thing'); 
84
85         foreach my $dxchan (DXChannel::get_all()) {
86                 next if $dxchan == $main::me;
87                 next if grep $dxchan == $_, @_;
88                 $thing->send($dxchan); 
89         }
90 }
91
92 # queue this thing for processing
93 sub queue
94 {
95         my $thing = shift;
96         my $dxchan = shift;
97         $thing->{dxchan} = $dxchan->call;
98         push @queue, $thing;
99 }
100
101 #
102 # this is the main commutator loop. In due course it will
103 # become the *only* commutator loop, This can be called in one
104 # of two ways: either with 2 args or with none.
105 #
106 # The two arg form is an immediate "queue and handle" and does
107 # a full cycle, immediately
108 #
109 sub process
110 {
111         my $thing;
112         if (@_ == 2) {
113                 $thing = shift;
114                 $thing->queue(shift);
115         }
116         while (@queue) {
117                 $thing = shift @queue;
118                 my $dxchan = DXChannel->get($thing->{dxchan});
119                 if ($dxchan) {
120                         if ($thing->can('in_filter')) {
121                                 next unless $thing->in_filter($dxchan);
122                         }
123
124                         # remember any useful routes
125                         RouteDB::update($thing->{origin}, $dxchan->{call}, $thing->{hopsaway});
126                         RouteDB::update($thing->{user}, $dxchan->{call}, $thing->{hopsaway}) if exists $thing->{user};
127                 
128                         $thing->handle($dxchan);
129                 }
130         }
131
132         # per second and per minute processing
133         if ($main::systime != $lastsec) {
134                 if ($main::systime >= $lastmin+60) {
135                         foreach my $r (@permin) {
136                                 &{$r->[0]}();
137                         }
138                         $lastmin = $main::systime;
139                 }
140                 foreach my $r (@persec) {
141                         &{$r->[0]}();
142                 }
143                 $lastsec = $main::systime;
144         }
145 }
146
147 sub add_minute_process
148 {
149         my $pkg = shift;
150         my $addr = shift;
151         my $name = shift;
152         dbg('Adding $name to Thingy per minute queue');
153         push @permin, [$addr, $name];
154 }
155
156 sub add_second_process
157 {
158         my $pkg = shift;
159         my $addr = shift;
160         my $name = shift;
161         dbg('Adding $name to Thingy per second queue');
162         push @persec, [$addr, $name];
163 }
164
165
166 sub ascii
167 {
168         my $thing = shift;
169         my $dd = new Data::Dumper([$thing]);
170         $dd->Indent(0);
171         $dd->Terse(1);
172         $dd->Sortkeys(1);
173     $dd->Quotekeys($] < 5.005 ? 1 : 0);
174         return $dd->Dumpxs;
175 }
176
177 sub add_auth
178 {
179         my $thing = shift;
180         my $s = $thing->{'s'} = sprintf "%X", int(rand() * 100000000);
181         my $auth = Verify->new("DXSp,$main::mycall,$s,$main::version,$main::build");
182         $thing->{auth} = $auth->challenge($main::me->user->passphrase);
183 }
184
185 1;
186