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.44
Committed: Tue Dec 18 04:07:17 2001 UTC (22 years, 10 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.43: +9 -3 lines
Log Message:
Solaris process accounting made me notice the number of times `uname` was
being called. It was way over the top, especially as it's unlikely to
change during an invocation of ihost :) Before it was run by each plugin
every time they were run - in total 50 times a minute :/ Now, it's run once
by ihost at startup, and that's it. The value read at startup is passed as
a command line argument to each plugin. The plugins can fallback on working
out the ostype using `uname` if they don't get given one on the command
line.

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