ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/host/ihost-perl/ihost.pl
Revision: 1.47
Committed: Thu Mar 21 17:57:13 2002 UTC (22 years, 8 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.46: +7 -1 lines
Log Message:
Added key authentication to ihost.

File Contents

# User Rev Content
1 pjm2 1.6 #!/usr/bin/perl -w
2 pjm2 1.1
3     # -----------------------------------------------------------
4     # Perl i-scream Host.
5 pjm2 1.12 # http://www.i-scream.org.uk
6 pjm2 1.1 #
7     # An all-in-one script to act as an i-scream host on
8 tdb 1.36 # a typical Unix/Linux box.
9 pjm2 1.1 #
10 tdb 1.44 # $Author: tdb $
11 tdb 1.47 # $Id: ihost.pl,v 1.46 2002/03/11 13:34:24 tdb Exp $
12 pjm2 1.1 #------------------------------------------------------------
13    
14     $| = 1;
15    
16     use strict;
17     use IO::Socket;
18     use Sys::Hostname;
19    
20     use vars qw (
21     $filter_manager_addr
22     $filter_manager_port
23     $seq_no
24     $udp_update_time
25     $tcp_update_time
26     $last_udp_time
27     $last_tcp_time
28     $last_modified
29     $udp_port
30     $tcp_port
31     $filter_addr
32     $file_list
33 pjm2 1.17 $fqdn
34 tdb 1.25 $pidfile
35 pjm2 1.28 $retry_wait
36 tdb 1.44 $ostype
37 tdb 1.47 $key
38 tdb 1.37 @data
39 pjm2 1.1 );
40    
41     if (@ARGV != 2) {
42     die "Usage: ihost.pl [i-scream filter manager] [TCP port]\n";
43     }
44    
45     $filter_manager_addr = $ARGV[0];
46     $filter_manager_port = $ARGV[1];
47    
48     $seq_no = 1;
49 pjm2 1.33 $retry_wait = 60;
50 pjm2 1.1
51 tdb 1.44 # work out our platform, if we can.
52     $ostype = `uname -s`;
53     chomp $ostype;
54     $ostype = "unknown" if not defined $ostype;
55    
56 tdb 1.25 # write our PID to a file
57 tdb 1.41 # use home dir by default
58 tdb 1.42 #$pidfile = $ENV{"HOME"};
59 tdb 1.41 # or drop it in /var/tmp if we can't find HOME
60     $pidfile = "/var/tmp" if not defined $pidfile;
61     $pidfile .= "/.ihost.pid";
62 tdb 1.25 &write_pid();
63    
64 pjm2 1.1 &tcp_configure();
65 tdb 1.47 &send_tcp_heartbeat();
66 pjm2 1.1 &send_udp_packet();
67    
68     $last_udp_time = time;
69     $last_tcp_time = time;
70     while (1) {
71 pjm2 1.9 my($time) = time;
72     if ($time >= $last_udp_time + $udp_update_time) {
73 pjm2 1.1 &send_udp_packet();
74 pjm2 1.9 $last_udp_time = $time;
75 pjm2 1.1 }
76 pjm2 1.9 if ($time >= $last_tcp_time + $tcp_update_time) {
77 pjm2 1.1 &send_tcp_heartbeat();
78 pjm2 1.9 $last_tcp_time = $time;
79 pjm2 1.1 }
80 pjm2 1.9 my($next_udp) = $udp_update_time - $time + $last_udp_time;
81     my($next_tcp) = $tcp_update_time - $time + $last_tcp_time;
82     my($delay);
83     if ($next_udp < $next_tcp) {
84     $delay = $next_udp
85     }
86     else {
87     $delay = $next_tcp;
88     }
89 tdb 1.35 sleep $delay;
90 pjm2 1.1 }
91    
92 tdb 1.25 # we'll probably never get here...
93     `rm -f $pidfile`;
94 pjm2 1.1 exit(0);
95    
96 pjm2 1.12
97     #-----------------------------------------------------------------------
98 pjm2 1.28 # wait_then_retry
99     # Waits for the period of time specified in $retry_wait, then attempts
100     # to reconfigure with the server.
101     #-----------------------------------------------------------------------
102     sub wait_then_retry() {
103     print "Will retry configuration with filter manager in $retry_wait seconds.\n";
104 tdb 1.35 sleep $retry_wait;
105 pjm2 1.28 }
106    
107    
108     #-----------------------------------------------------------------------
109 pjm2 1.12 # tcp_configure
110     # Establishes a TCP connection to the specified i-scream filter manager.
111     # The host then requests details from the server, such as the intervals
112     # at which to send UDP packets.
113     #-----------------------------------------------------------------------
114 pjm2 1.1 sub tcp_configure() {
115    
116 pjm2 1.31 while (1) {
117 pjm2 1.28 my($sock) = new IO::Socket::INET(
118     PeerAddr => $filter_manager_addr,
119     PeerPort => $filter_manager_port,
120     Proto => 'tcp'
121     ) or die "Cannot connect!";
122     if (!defined $sock) {
123     print "IHOST ERROR: Could not connect to $filter_manager_addr:$filter_manager_port.\n";
124     print "Please check that there is an i-scream server at this address.\n";
125     wait_then_retry();
126     next;
127     }
128    
129     # Now run through the configuration process...
130     my($response);
131    
132     print $sock "STARTCONFIG\n";
133     $response = <$sock>;
134 pjm2 1.32 if ($response && !($response eq "OK\n")) {
135     print "The i-scream server rejected the STARTCONFIG command.\n";
136 pjm2 1.28 close($sock);
137     wait_then_retry();
138     next;
139     }
140    
141     print "Config started okay.\n";
142    
143     print $sock "LASTMODIFIED\n";
144     $response = <$sock>;
145 pjm2 1.32 if (!$response) {
146     print "The i-scream server did not return anything for the LASTMODIFIED command.\n";
147     close($sock);
148     wait_then_retry();
149     next;
150     }
151 pjm2 1.28 chop $response;
152     $last_modified = $response;
153    
154     print "Config last modified: ". (scalar localtime $last_modified/1000) . "\n";
155    
156     print $sock "FILELIST\n";
157     $response = <$sock>;
158 pjm2 1.32 if (!$response) {
159     print "The i-scream server did not provide a configuration file list.\n";
160     close($sock);
161     wait_then_retry();
162     next;
163     }
164 pjm2 1.28 chop $response;
165     $file_list = $response;
166    
167     print "File list obtained: $file_list\n";
168    
169     print $sock "FQDN\n";
170     $response = <$sock>;
171 pjm2 1.32 if (!$response) {
172     print "The i-scream server did not tell us our FQDN.\n";
173     close($sock);
174     wait_then_retry();
175     next;
176     }
177 pjm2 1.28 chop $response;
178     $fqdn = $response;
179    
180     print "FQDN returned: $fqdn\n";
181    
182     print $sock "UDPUpdateTime\n";
183     $response = <$sock>;
184 pjm2 1.32 if (!$response) {
185     print "The i-scream server did not give us a UDPUpdateTime.\n";
186     close($sock);
187     wait_then_retry();
188     next;
189     }
190 pjm2 1.28 chop $response;
191     $udp_update_time = $response;
192    
193     print $sock "TCPUpdateTime\n";
194     $response = <$sock>;
195 pjm2 1.32 if (!$response) {
196     print "The i-scream server did not give us a TCPUpdateTime.\n";
197     close($sock);
198     wait_then_retry();
199     next;
200     }
201 pjm2 1.28 chop $response;
202     $tcp_update_time = $response;
203    
204     print "UDP packet period: $udp_update_time seconds.\nTCP heartbeat period: $tcp_update_time seconds.\n";
205    
206     print $sock "ENDCONFIG\n";
207     $response = <$sock>;
208 pjm2 1.32 if ($response && !($response eq "OK\n")) {
209 pjm2 1.28 print "ENDCONFIG command to server failed. Terminated.\n";
210     close($sock);
211     wait_then_retry();
212     next;
213     }
214    
215     print "Config ended.\n";
216    
217     print $sock "FILTER\n";
218     $response = <$sock>;
219 pjm2 1.32 if (!$response) {
220 pjm2 1.28 print "Failed: Could not get a filter address from the filter manager.\n";
221     close($sock);
222     wait_then_retry();
223     next;
224     }
225     chop $response;
226 pjm2 1.32 $response =~ /^(.*);(.*);(.*)/;
227 pjm2 1.28 if ($response eq "ERROR") {
228     print "There are no active configured filters for your host.\n";
229     close($sock);
230     wait_then_retry();
231     next;
232     }
233     ($filter_addr, $udp_port, $tcp_port) = ($1, $2, $3);
234     unless (defined($filter_addr) && defined($udp_port) && defined($tcp_port)) {
235     print "Failed: Filter address response from server did not make sense: $response\n";
236     close($sock);
237     wait_then_retry();
238     next;
239     }
240    
241     print "Got filter data ($filter_addr, $udp_port, $tcp_port)\n";
242    
243     print $sock "END\n";
244     $response = <$sock>;
245 pjm2 1.32 if ($response && ($response eq "OK\n")) {
246 pjm2 1.28 print "Host successfully configured via TCP.\n"
247     }
248     else {
249 pjm2 1.32 print "The server failed the host configuration on the END command.\n";
250 pjm2 1.28 close($sock);
251     wait_then_retry();
252     next;
253     }
254 pjm2 1.1
255 pjm2 1.28 close($sock);
256 pjm2 1.1
257 pjm2 1.28 print "Configuration finished sucessfully!\n";
258     last;
259 pjm2 1.1 }
260 pjm2 1.12 return;
261 pjm2 1.1 }
262    
263 pjm2 1.12
264     #-----------------------------------------------------------------------
265     # send_udp_packet
266     # Sends a UDP packet to an i-scream filter.
267     # The packet contains XML markup describing some of the machine's state.
268     # Receipt of UDP packets is not guaranteed.
269     #-----------------------------------------------------------------------
270 pjm2 1.1 sub send_udp_packet() {
271    
272 tdb 1.37 my($plugins_dir) = "plugins";
273    
274     opendir PLUGINS, $plugins_dir;
275     my(@plugins) = readdir PLUGINS;
276     foreach my $plugin (@plugins) {
277 tdb 1.44 push @data, `$plugins_dir/$plugin $ostype` if -x "$plugins_dir/$plugin" && -f "$plugins_dir/$plugin";
278 tdb 1.37 }
279 pjm2 1.1
280 tdb 1.36 # get some extra data
281 pjm2 1.1 my($date) = time;
282 pjm2 1.33 my($ip);
283     $ip = inet_ntoa(scalar(gethostbyname(hostname())) || 'localhost') or $ip = 'localhost';
284 tdb 1.36
285     # add some extra data to the array
286 tdb 1.43 push(@data, "packet.attributes.seq_no $seq_no");
287     push(@data, "packet.attributes.machine_name $fqdn");
288     push(@data, "packet.attributes.date $date");
289     push(@data, "packet.attributes.type data");
290     push(@data, "packet.attributes.ip $ip");
291 tdb 1.47 push(@data, "packet.attributes.key $key");
292 tdb 1.39
293     # sort the data
294 tdb 1.43 @data = sort(grep(!/^$/, grep(/^packet\./, @data)));
295    
296 tdb 1.36 # turn the array into some nice XML
297     my($xml) = &make_xml("", "");
298 tdb 1.43
299 pjm2 1.1 my($sock) = new IO::Socket::INET (
300     PeerPort => $udp_port,
301     PeerAddr => $filter_addr,
302     Proto => 'udp'
303 pjm2 1.21 ) or die "Could not send UDP: $!\n";
304 tdb 1.36
305 pjm2 1.1 print $sock $xml or die "Could not send UDP packet: $!\n";
306     close($sock);
307     $seq_no++;
308 tdb 1.38 print "-";
309 pjm2 1.12
310     return;
311 pjm2 1.1 }
312    
313 pjm2 1.12
314     #-----------------------------------------------------------------------
315     # send_tcp_heartbeat
316     # Establishes a TCP connection to an i-scream filter.
317     # The heartbeat is used as a guaranteed "I'm alive" delivery mechanism.
318 pjm2 1.34 # If we need to reconfigure, then we complete the heartbeat before
319     # doing so.
320 pjm2 1.12 #-----------------------------------------------------------------------
321 pjm2 1.1 sub send_tcp_heartbeat() {
322    
323 pjm2 1.34 my ($doReconfigure) = 0;
324    
325 pjm2 1.1 my($sock) = new IO::Socket::INET(
326     PeerAddr => $filter_addr,
327     PeerPort => $tcp_port,
328     Proto => 'tcp'
329 pjm2 1.21 ) or return;
330 pjm2 1.11 if (!defined $sock) {
331     print "IHOST WARNING: Failed to deliver a heartbeat to the i-scream filter.\n";
332 pjm2 1.27 &tcp_configure();
333 pjm2 1.11 return;
334     }
335 pjm2 1.1
336     # Now run through the configuration process.
337     my($response);
338    
339     print $sock "HEARTBEAT\n";
340     $response = <$sock>;
341 pjm2 1.32 if (!$response eq "OK\n") {
342 pjm2 1.1 close($sock);
343     print "Server gave wrong response to HEARTBEAT: $response\n";
344 pjm2 1.27 &tcp_configure();
345 pjm2 1.1 return;
346     }
347    
348     print $sock "CONFIG\n";
349     $response = <$sock>;
350 pjm2 1.32 if (!$response eq "OK\n") {
351 pjm2 1.1 close($sock);
352     print "Server gave wrong response to CONFIG: $response\n";
353 pjm2 1.27 &tcp_configure();
354 pjm2 1.1 return;
355     }
356    
357     print $sock "$file_list\n";
358     $response = <$sock>;
359 pjm2 1.32 if (!$response eq "OK\n") {
360 pjm2 1.1 close($sock);
361     print "Server gave wrong response to file list: $response\n";
362 pjm2 1.27 &tcp_configure();
363 pjm2 1.1 return;
364     }
365    
366     print $sock "$last_modified\n";
367     $response = <$sock>;
368 pjm2 1.32 if ($response eq "ERROR\n") {
369 pjm2 1.1 close($sock);
370 pjm2 1.27 print "Server configuration changed. Reconfiguring with filter manager.\n";
371 pjm2 1.34 $doReconfigure = 1;
372 pjm2 1.1 }
373 pjm2 1.32 if (!$response eq "OK\n") {
374 pjm2 1.1 close($sock);
375     print "Server gave wrong response to HEARTBEAT: $response\n";
376 pjm2 1.27 &tcp_configure();
377 pjm2 1.1 return;
378     }
379 tdb 1.47
380     print $sock "KEY\n";
381     $key = <$sock>;
382 pjm2 1.1
383     print $sock "ENDHEARTBEAT\n";
384 pjm2 1.23 $response = <$sock>;
385 pjm2 1.32 if (!$response eq "OK\n") {
386 pjm2 1.1 close($sock);
387     print "Server gave wrong response to ENDHEARTBEAT: $response\n";
388 pjm2 1.27 &tcp_configure();
389 pjm2 1.1 return;
390     }
391    
392     close($sock);
393     print "^";
394 pjm2 1.34
395     &tcp_configure() if $doReconfigure;
396 tdb 1.25
397     return;
398     }
399    
400 tdb 1.36
401 tdb 1.25 #-----------------------------------------------------------------------
402     # write_pid
403     # Writes the PID (process ID) of this instance to $pidfile.
404     # This is then used by a seperate script to check (and restart) ihost.
405     #-----------------------------------------------------------------------
406     sub write_pid() {
407     open PID, ">$pidfile";
408     print PID $$;
409     close PID;
410 pjm2 1.12
411     return;
412 tdb 1.36 }
413    
414     #-----------------------------------------------------------------------
415     # make_xml
416 tdb 1.37 # Turns an array of plugins data into an XML string.
417 tdb 1.36 #-----------------------------------------------------------------------
418     sub make_xml() {
419     my($curlevel, $curline) = @_;
420     my($xmltemp) = ""; my($curtag) = ""; my($attributes) = "";
421 tdb 1.40 while(1) {
422 tdb 1.43 $curline = shift(@data) if $curline eq "";
423     return $xmltemp if not defined $curline;
424     chomp $curline;
425     # dealing with nest (or attributes)
426 tdb 1.36 if($curline =~ /^$curlevel([^\.\s]+\.)/) {
427     $curtag=$1;
428 tdb 1.43 if($curline =~ /^$curlevel$curtag([^\.\s]+)\s+(.*)$/) {
429     $xmltemp .= &make_xml("$curlevel$curtag", $curline);
430     }
431     elsif($curline =~ /^$curlevel$curtag(attributes)\.([^\.\s]+)\s+(.*)$/) {
432     $attributes .= " $2=\"$3\"";
433     }
434     else {
435     $xmltemp .= &make_xml("$curlevel$curtag", $curline);
436     }
437     my($nextline) = $data[0]; chomp $nextline if defined $nextline;
438 tdb 1.45 $curtag =~ s/(.*)\./$1/;
439     if((defined $nextline) && ($nextline =~ /^$curlevel$curtag\./)) {
440 tdb 1.43 $curline = "";
441     }
442     else {
443     $xmltemp = "<$curtag$attributes>$xmltemp</$curtag>" unless $curtag eq "";
444     return $xmltemp;
445     }
446 tdb 1.36 }
447 tdb 1.43 # dealing with value
448     elsif($curline =~ /^$curlevel([^\.\s]+)\s+(.*)$/) {
449     $curtag=$1;
450     $xmltemp=$2;
451     my($nextline) = $data[0]; chomp $nextline if defined $nextline;
452     if(defined $nextline && ($nextline =~ /^$curlevel$curtag\./ || $nextline =~ /^$curlevel$curtag\s+/)) {
453     $curline = "";
454     }
455     else {
456     $xmltemp = "<$curtag$attributes>$xmltemp</$curtag>" unless $curtag eq "";
457     return $xmltemp;
458     }
459 tdb 1.36 }
460 tdb 1.46 # dealing with a null value
461     elsif($curline =~ /^$curlevel([^\.\s]+)$/) {
462     # simply adding a space makes the above elsif deal with it :)
463     # just level with an empty tag in the XML
464     $curline .= " ";
465     }
466     # failing all that, skip the line
467     else {
468     $curline = "";
469     }
470 tdb 1.36 }
471 pjm2 1.1 }