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.37
Committed: Mon Nov 19 14:15:14 2001 UTC (22 years, 10 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.36: +18 -12 lines
Log Message:
Feature fullfilling some of the following tracker ID on sourceforge:
  [ #479690 ] make ihost/statgrab more modular

This commit adds support for a plugins directory. Any executable file in
the directory will be run, making it easier to add more input sources. The
next stage will be to move statgrab.pl into the plugins, either in whole
or in smaller chunks.

Note: is this a possibly security issue? Running all scripts found in a
directory might be a bad idea... but assuming the directory is set up ok,
I guess it ill be ok.

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