blib/lib/Mobile/Location.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 4 | 6 | 66.6 |
branch | n/a | ||
condition | n/a | ||
subroutine | 2 | 2 | 100.0 |
pod | n/a | ||
total | 6 | 8 | 75.0 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package Mobile::Location; | ||||||
2 | |||||||
3 | # Location.pm - the mobile agent environment location class. | ||||||
4 | # | ||||||
5 | # Author: Paul Barry, paul.barry@itcarlow.ie | ||||||
6 | # Create: March 2003. | ||||||
7 | # Update: April 2003 - changed to IO::Socket for agent receipt and processing | ||||||
8 | # due to "fork" strangeness on regular sockets. | ||||||
9 | # May 2003 - added support for authentication and encryption. | ||||||
10 | # - added the web-based monitoring service. | ||||||
11 | # | ||||||
12 | # Notes: Version 1.x - unsafe, totally trusting Locations (never released). | ||||||
13 | # Version 2.x - added support to the Location for executing mobile | ||||||
14 | # agents within a restricted Opcode environment. | ||||||
15 | # Version 3.x - adds support for authentication and encryption. This | ||||||
16 | # code assumes that a functioning keyserver is running. | ||||||
17 | # Version 4.x - embeds a web-server to allow for remote monitoring via | ||||||
18 | # the world-wide-web. | ||||||
19 | |||||||
20 | 1 | 1 | 9915 | use strict; | |||
1 | 3 | ||||||
1 | 47 | ||||||
21 | |||||||
22 | 1 | 1 | 2320 | use Crypt::RSA; # Provides authentication and encryption services. | |||
0 | |||||||
0 | |||||||
23 | use IO::Socket; # OO interface to Socket API. | ||||||
24 | use Socket; # Procedural interface to Socket API. | ||||||
25 | use Sys::Hostname; # Provides a means of determine name of current machine. | ||||||
26 | use HTTP::Daemon; # Provides a basic HTTP server. | ||||||
27 | use HTTP::Status; # Provides support for HTTP status messages. | ||||||
28 | use POSIX 'WNOHANG'; # Provides support for POSIX signals. | ||||||
29 | |||||||
30 | # Add a signal handler to process and deal with "zombies". | ||||||
31 | |||||||
32 | $SIG{CHLD} = sub { while ( waitpid( -1, WNOHANG ) > 0 ) { }; }; | ||||||
33 | |||||||
34 | our $VERSION = 4.02; | ||||||
35 | |||||||
36 | use constant TRUE => 1; | ||||||
37 | use constant FALSE => 0; | ||||||
38 | |||||||
39 | use constant RUN_LOCATION_DIR => "Location"; | ||||||
40 | use constant KEY_SIZE => 1024; | ||||||
41 | |||||||
42 | use constant RESPONDER_PPORT => '30001'; | ||||||
43 | use constant REGISTRATION_PPORT => '30002'; | ||||||
44 | |||||||
45 | use constant SCOOBY_CONFIG_FILE => "$ENV{'HOME'}/.scoobyrc"; | ||||||
46 | |||||||
47 | use constant HTML_DEFAULT_PAGE => "index.html"; | ||||||
48 | use constant HTTP_PORT => 8080; | ||||||
49 | |||||||
50 | use constant LOGFILE => 'location.log'; | ||||||
51 | |||||||
52 | use constant VISIT_SCOOBY => 'Visit the Scooby Website at IT Carlow. '; |
||||||
53 | |||||||
54 | our $_PWD = ''; # This 'global' contains the current working directory | ||||||
55 | # for the Location instance determined during construction. | ||||||
56 | |||||||
57 | ########################################################################## | ||||||
58 | # The class constructor is in "new". | ||||||
59 | ########################################################################## | ||||||
60 | |||||||
61 | sub new { | ||||||
62 | |||||||
63 | # The Mobile::Location constructor. | ||||||
64 | # | ||||||
65 | # IN: Receives a series of optional name/value pairings. | ||||||
66 | # Port - Protocol port value to accept connections from. | ||||||
67 | # Default value for Port is '2001'. | ||||||
68 | # Debug - set to 1 for STDERR status messages. | ||||||
69 | # Default value for Debug is 0 (off). | ||||||
70 | # Log - set to 1 to enable logging of agents to disk. | ||||||
71 | # Default value for Log is 0 (off). | ||||||
72 | # Ops - a set of Opcodes or Opcode tags, which are | ||||||
73 | # added to Scooby's ALLOWED ops when executing | ||||||
74 | # mobile agents. | ||||||
75 | # Web - set to 1 to enable the logging mechanism and the | ||||||
76 | # creation of a HTTP-based Monitoring Service. The | ||||||
77 | # default is 1 (i.e., ON). | ||||||
78 | # | ||||||
79 | # OUT: Returns a blessed reference to a Mobile::Location object. | ||||||
80 | |||||||
81 | my ( $class, %arguments ) = @_; | ||||||
82 | |||||||
83 | my $self = bless {}, $class; | ||||||
84 | |||||||
85 | $self->{ Port } = $arguments{ Port } || 2001; | ||||||
86 | $self->{ Debug } = $arguments{ Debug } || FALSE; | ||||||
87 | $self->{ Log } = $arguments{ Log } || FALSE; | ||||||
88 | $self->{ Ops } = $arguments{ Ops } || ''; | ||||||
89 | $self->{ Web } = $arguments{ Web } || TRUE; | ||||||
90 | |||||||
91 | # Untaint the PATH by setting it to something really limited. | ||||||
92 | |||||||
93 | $ENV{'PATH'} = "/bin:/usr/bin"; | ||||||
94 | |||||||
95 | # This next line is part of the standard Perl technique. See 'perlsec'. | ||||||
96 | |||||||
97 | delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' }; | ||||||
98 | |||||||
99 | $_PWD = `pwd`; # XXXXXX: Writing to global! This is tainted. | ||||||
100 | $_PWD =~ /^([-\@\/\w_.]+)$/; # So, we untaint it, using a regex. | ||||||
101 | $_PWD = $1; | ||||||
102 | |||||||
103 | # Disallow if running this Location as 'root'. | ||||||
104 | |||||||
105 | die "Location running as ROOT. This is NOT secure (nor allowed)!" | ||||||
106 | unless $> and $^O ne 'VMS'; | ||||||
107 | |||||||
108 | # Work out and remember the IP address of the computer running this Location. | ||||||
109 | |||||||
110 | my $host = gethostbyname( hostname ) or inet_aton( hostname ); | ||||||
111 | $self->{ Host } = inet_ntoa( $host ); | ||||||
112 | |||||||
113 | # Generate and remember a password to use with the PK- and PK+. | ||||||
114 | |||||||
115 | $self->{ Password } = $0 . $$ . '_Location'; | ||||||
116 | |||||||
117 | # NOTE: A second server is spawned at this stage to handle any | ||||||
118 | # requests from an agent re: the availability of any | ||||||
119 | # required modules within the Perl system running this Location. | ||||||
120 | # See the _check_modules_on_remote subroutine from Devel::Scooby, | ||||||
121 | # as well as the _spawn_network_service and _check_for_modules | ||||||
122 | # subroutines, below. | ||||||
123 | |||||||
124 | _spawn_network_service( $self->{ Port }+1 ); | ||||||
125 | |||||||
126 | # Create the HTTP-based Monitoring Service. | ||||||
127 | |||||||
128 | $self->_spawn_web_monitoring_service; | ||||||
129 | |||||||
130 | return $self; | ||||||
131 | } | ||||||
132 | |||||||
133 | |||||||
134 | ########################################################################## | ||||||
135 | # Methods and support subroutines. | ||||||
136 | ########################################################################## | ||||||
137 | |||||||
138 | sub _logger { | ||||||
139 | |||||||
140 | # This small routine quickly writes a message to the LOGFILE. Note | ||||||
141 | # that every line written to the LOGFILE is timestamped. | ||||||
142 | # | ||||||
143 | # IN: a message to log. | ||||||
144 | # | ||||||
145 | # OUT: nothing. | ||||||
146 | |||||||
147 | my $self = shift; | ||||||
148 | |||||||
149 | # Open the LOGFILE for append >>. | ||||||
150 | |||||||
151 | open ML_LOGFILE, ">>" . LOGFILE | ||||||
152 | or die "Mobile::Location: unable to append to LOGFILE.\n"; | ||||||
153 | |||||||
154 | print ML_LOGFILE scalar localtime, ": @_\n"; | ||||||
155 | |||||||
156 | close ML_LOGFILE; | ||||||
157 | } | ||||||
158 | |||||||
159 | sub _logger2 { | ||||||
160 | |||||||
161 | # This small routine quickly writes a message to the LOGFILE. Note | ||||||
162 | # that every line written to the LOGFILE is timestamped. This code is | ||||||
163 | # the same as "_logger", but for the fact that the location of the | ||||||
164 | # LOGFILE is one-level-up in the directory hierarchy. | ||||||
165 | # | ||||||
166 | # IN: a message to log. | ||||||
167 | # | ||||||
168 | # OUT: nothing. | ||||||
169 | |||||||
170 | my $self = shift; | ||||||
171 | |||||||
172 | # Open the LOGFILE (which is one-level-up) for append >>. | ||||||
173 | |||||||
174 | open ML_LOGFILE, ">>../" . LOGFILE | ||||||
175 | or die "Mobile::Location: unable to append to LOGFILE.\n"; | ||||||
176 | |||||||
177 | print ML_LOGFILE scalar localtime, ": @_\n"; | ||||||
178 | |||||||
179 | close ML_LOGFILE; | ||||||
180 | } | ||||||
181 | |||||||
182 | sub _build_index_dot_html { | ||||||
183 | |||||||
184 | # Builds the INDEX.HTML file (used by _start_web_service). | ||||||
185 | # | ||||||
186 | # IN: nothing. | ||||||
187 | # | ||||||
188 | # OUT: nothing (although "index.html" is created). | ||||||
189 | |||||||
190 | my $self = shift; | ||||||
191 | |||||||
192 | open HTMLFILE, ">index.html" | ||||||
193 | or die "Mobile::Executive: index.html cannot be written to: $!.\n"; | ||||||
194 | |||||||
195 | print HTMLFILE< | ||||||
196 | |||||||
197 | |||||||
198 | |||||||
199 | |
||||||
200 | |||||||
201 | |||||||
202 | Welcome to the Location Web-Based Monitoring Service |
||||||
203 | end_html | ||||||
204 | |||||||
205 | print HTMLFILE "Location executing on: " . hostname . ". "; |
||||||
206 | print HTMLFILE "Location date/time: " . localtime() . | ||||||
207 | ". Running on port: " . | ||||||
208 | $self->{ Port } . ". "; |
||||||
209 | |||||||
210 | print HTMLFILE< | ||||||
211 | |||||||
212 | Click here to reset the log. | ||||||
213 | Logging Details |
||||||
214 | |
||||||
215 | end_html | ||||||
216 | |||||||
217 | open HTTP_LOGFILE, LOGFILE | ||||||
218 | or die "Mobile::Location: the LOGFILE is missing - aborting.\n"; | ||||||
219 | |||||||
220 | while ( my $logline = |
||||||
221 | { | ||||||
222 | print HTMLFILE "$logline"; | ||||||
223 | } | ||||||
224 | |||||||
225 | close HTTP_LOGFILE; | ||||||
226 | |||||||
227 | print HTMLFILE< | ||||||
228 | |||||||
229 | |||||||
230 | end_html | ||||||
231 | |||||||
232 | print HTMLFILE VISIT_SCOOBY; | ||||||
233 | |||||||
234 | print HTMLFILE< | ||||||
235 | |||||||
236 | |||||||
237 | |||||||
238 | end_html | ||||||
239 | |||||||
240 | close HTMLFILE; | ||||||
241 | } | ||||||
242 | |||||||
243 | sub _build_clearlog_dot_html { | ||||||
244 | |||||||
245 | # Builds the CLEARLOG.HTML file (used by _start_web_service). | ||||||
246 | # | ||||||
247 | # IN: the name of the just-created backup file. | ||||||
248 | # | ||||||
249 | # OUT: nothing (although "clearlog.html" is created). | ||||||
250 | |||||||
251 | my $self = shift; | ||||||
252 | |||||||
253 | my $backup_log = shift; | ||||||
254 | |||||||
255 | open CLEARLOG_HTML, ">clearlog.html" | ||||||
256 | or die "Mobile::Executive: clearlog.html cannot be written to: $!.\n"; | ||||||
257 | |||||||
258 | print CLEARLOG_HTML< | ||||||
259 | |||||||
260 | |||||||
261 | |||||||
262 | |
||||||
263 | |||||||
264 | |||||||
265 | Location Logfile Reset |
||||||
266 | The previous logfile has been archived as: $backup_log
|
||||||
267 | Return to this Location's main page.
|
||||||
268 | end_html | ||||||
269 | |||||||
270 | print CLEARLOG_HTML VISIT_SCOOBY; | ||||||
271 | |||||||
272 | print CLEARLOG_HTML< | ||||||
273 | |||||||
274 | |||||||
275 | |||||||
276 | end_html | ||||||
277 | |||||||
278 | close CLEARLOG_HTML; | ||||||
279 | } | ||||||
280 | |||||||
281 | sub _start_web_service { | ||||||
282 | |||||||
283 | # Starts a small web server running on port HTTP_PORT. Provides for some | ||||||
284 | # simple monitoring of the Location. | ||||||
285 | # | ||||||
286 | # IN: nothing. | ||||||
287 | # | ||||||
288 | # OUT: nothing. | ||||||
289 | |||||||
290 | my $self = shift; | ||||||
291 | |||||||
292 | my $httpd = HTTP::Daemon->new( LocalPort => HTTP_PORT, | ||||||
293 | Reuse => 1 ) | ||||||
294 | or die "Mobile::Location: could not create HTTP daemon on " . | ||||||
295 | HTTP_PORT . ".\n"; | ||||||
296 | |||||||
297 | $self->_logger( "Starting web service on port:", HTTP_PORT ) if $self->{ Web }; | ||||||
298 | |||||||
299 | while ( my $http_client = $httpd->accept ) | ||||||
300 | { | ||||||
301 | if ( my $service = $http_client->get_request ) | ||||||
302 | { | ||||||
303 | my $request = $service->uri->path; | ||||||
304 | |||||||
305 | if ( $service->method eq 'GET' ) | ||||||
306 | { | ||||||
307 | my $resource; | ||||||
308 | |||||||
309 | if ( $request eq "/" || $request eq "/index.html" ) | ||||||
310 | { | ||||||
311 | $resource = HTML_DEFAULT_PAGE; | ||||||
312 | |||||||
313 | $self->_build_index_dot_html; | ||||||
314 | |||||||
315 | $http_client->send_file_response( $resource ); | ||||||
316 | } | ||||||
317 | elsif ( $request eq "/clearlog.html" ) | ||||||
318 | { | ||||||
319 | # Create a name for the backup log. | ||||||
320 | |||||||
321 | my $backup_log = "Mobile::Location." . localtime() . | ||||||
322 | "." . $$ . ".log"; | ||||||
323 | |||||||
324 | # Make the backup, delete the LOGFILE, then recreate it. | ||||||
325 | |||||||
326 | system( "cp", LOGFILE, $backup_log ); | ||||||
327 | unlink LOGFILE; | ||||||
328 | |||||||
329 | $self->_logger( "Mobile::Location: log reset." ) if $self->{ Web }; | ||||||
330 | |||||||
331 | $self->_build_clearlog_dot_html( $backup_log ); | ||||||
332 | |||||||
333 | $http_client->send_file_response( "clearlog.html" ); | ||||||
334 | } | ||||||
335 | else | ||||||
336 | { | ||||||
337 | $http_client->send_error( RC_NOT_FOUND ); | ||||||
338 | } | ||||||
339 | } | ||||||
340 | else | ||||||
341 | { | ||||||
342 | $http_client->send_error( RC_METHOD_NOT_ALLOWED ); | ||||||
343 | } | ||||||
344 | } | ||||||
345 | } | ||||||
346 | continue | ||||||
347 | { | ||||||
348 | $http_client->close; | ||||||
349 | undef( $http_client ); | ||||||
350 | } | ||||||
351 | } | ||||||
352 | |||||||
353 | sub _register_with_keyserver { | ||||||
354 | |||||||
355 | # Create a PK+ and PK- for this server, storing the PK+ in the | ||||||
356 | # keyserver, and retaining the PK- in memory (as part of the objects | ||||||
357 | # state). Note: a new key-pair is generated with each invocation. | ||||||
358 | # | ||||||
359 | # IN: nothing. (Other than the object reference, of course). | ||||||
360 | # | ||||||
361 | # OUT: nothing. | ||||||
362 | |||||||
363 | my $self = shift; | ||||||
364 | |||||||
365 | # Generate the PK+ and PK-. Store the PK- in the object's state. | ||||||
366 | |||||||
367 | my $rsa = new Crypt::RSA; | ||||||
368 | |||||||
369 | my $id = $self->{ Host } . ":" . $self->{ Port } . " Location"; | ||||||
370 | |||||||
371 | warn "This location is generating a PK+/PK- pairing.\n" if $self->{ Debug }; | ||||||
372 | |||||||
373 | my ( $public, $private ) = | ||||||
374 | $rsa->keygen( | ||||||
375 | Identity => $id, | ||||||
376 | Size => KEY_SIZE, | ||||||
377 | Password => $self->{ Password }, | ||||||
378 | Verbosity => FALSE | ||||||
379 | ) or die $rsa->errstr, "\n"; | ||||||
380 | |||||||
381 | warn "Pairing Generated.\n" if $self->{ Debug }; | ||||||
382 | |||||||
383 | $self->_logger( "Location's PK+/PK- pairing generated." ) if $self->{ Web }; | ||||||
384 | |||||||
385 | # Remember the PK- in the object's state. | ||||||
386 | |||||||
387 | $self->{ PrivateKey } = $private; | ||||||
388 | |||||||
389 | # Write the PK+ to an appropriately named disk-file. | ||||||
390 | |||||||
391 | my $pub_fn = $self->{ Host } . "." . $self->{ Port } . ".public"; | ||||||
392 | |||||||
393 | $self->_logger( "Writing PK+ to: $pub_fn." ) if $self->{ Web }; | ||||||
394 | |||||||
395 | $public->write( Filename => $pub_fn ); | ||||||
396 | |||||||
397 | # Determine the KEYSERVER address from the .scoobyrc file. | ||||||
398 | |||||||
399 | open KEYFILE, SCOOBY_CONFIG_FILE | ||||||
400 | or die "Mobile::Location: unable to access ~/.scoobyrc. Does it exist?\n"; | ||||||
401 | |||||||
402 | my $keyline = |
||||||
403 | |||||||
404 | close KEYFILE; | ||||||
405 | |||||||
406 | # Note: format of 'rc' file is very strict. No spaces! | ||||||
407 | $keyline =~ /^KEYSERVER=(.+)/; | ||||||
408 | |||||||
409 | $self->{ KeyServer } = $1; | ||||||
410 | |||||||
411 | # Now that we know the address of the keyserver, we can register the PK+ of this | ||||||
412 | # Location with the keyserver. We read the PK+ from the just-created disk-file. | ||||||
413 | |||||||
414 | $self->_logger( "Determined keyserver address as:", $self->{ KeyServer } ) if $self->{ Web }; | ||||||
415 | |||||||
416 | open KEYFILE, "$pub_fn" | ||||||
417 | or die "Mobile::Location: KEYFILE does not exist: $!.\n"; | ||||||
418 | |||||||
419 | my @entire_keyfile = |
||||||
420 | |||||||
421 | close KEYFILE; | ||||||
422 | |||||||
423 | my $keysock_obj = IO::Socket::INET->new( PeerAddr => $self->{ KeyServer }, | ||||||
424 | PeerPort => REGISTRATION_PPORT, | ||||||
425 | Proto => 'tcp' ); | ||||||
426 | |||||||
427 | if ( !defined( $keysock_obj ) ) | ||||||
428 | { | ||||||
429 | die "Mobile::Location: could not create socket object to key server: $!.\n"; | ||||||
430 | } | ||||||
431 | print $keysock_obj $self->{ Port }, "\n"; | ||||||
432 | print $keysock_obj @entire_keyfile; | ||||||
433 | |||||||
434 | $keysock_obj->close; | ||||||
435 | |||||||
436 | $self->_logger( "Location registered with keyserver." ) if $self->{ Web }; | ||||||
437 | } | ||||||
438 | |||||||
439 | sub start_concurrent { | ||||||
440 | |||||||
441 | # Start a passive server/location that executes concurrently. For | ||||||
442 | # each relocation request, a child process is spawned to process it. | ||||||
443 | # | ||||||
444 | # IN: nothing. | ||||||
445 | # | ||||||
446 | # OUT: nothing. | ||||||
447 | # | ||||||
448 | # This method is never returned from. Remember: servers are PERMANENT. | ||||||
449 | |||||||
450 | my $self = shift; | ||||||
451 | |||||||
452 | my $listening_socket = IO::Socket::INET->new( LocalPort => $self->{ Port }, | ||||||
453 | Listen => SOMAXCONN, | ||||||
454 | Proto => 'tcp', | ||||||
455 | Reuse => TRUE ); | ||||||
456 | |||||||
457 | if ( !defined( $listening_socket ) ) | ||||||
458 | { | ||||||
459 | die "Mobile::Location: unable to bind to listening socket: $!.\n"; | ||||||
460 | } | ||||||
461 | |||||||
462 | $self->_logger( "Location (concurrent) starting on port:", $self->{ Port } ) if $self->{ Web }; | ||||||
463 | |||||||
464 | warn "Location starting up on port: " . $self->{ Port } . ".\n" if $self->{ Debug }; | ||||||
465 | |||||||
466 | $self->_register_with_keyserver; | ||||||
467 | |||||||
468 | while ( TRUE ) # i.e., FOREVER, as servers are permanent. | ||||||
469 | { | ||||||
470 | next unless my $from_socket = $listening_socket->accept; | ||||||
471 | |||||||
472 | next if my $child = fork; | ||||||
473 | |||||||
474 | if ( $child == FALSE ) | ||||||
475 | { | ||||||
476 | $self->_logger( "Servicing client from:", | ||||||
477 | inet_ntoa( $from_socket->peeraddr ) ) if $self->{ Web }; | ||||||
478 | |||||||
479 | $listening_socket->close; | ||||||
480 | $self->_service_client( $from_socket ); | ||||||
481 | exit FALSE; | ||||||
482 | } | ||||||
483 | |||||||
484 | $from_socket->close; | ||||||
485 | } | ||||||
486 | } | ||||||
487 | |||||||
488 | sub start_sequential { | ||||||
489 | |||||||
490 | # Start a passive server/location that executes sequentially. | ||||||
491 | # | ||||||
492 | # IN: nothing. | ||||||
493 | # | ||||||
494 | # OUT: nothing. | ||||||
495 | # | ||||||
496 | # This method is never returned from. Remember: servers are PERMANENT. | ||||||
497 | |||||||
498 | my $self = shift; | ||||||
499 | |||||||
500 | my $listening_socket = IO::Socket::INET->new( LocalPort => $self->{ Port }, | ||||||
501 | Listen => SOMAXCONN, | ||||||
502 | Proto => 'tcp', | ||||||
503 | Reuse => TRUE ); | ||||||
504 | |||||||
505 | if ( !defined( $listening_socket ) ) | ||||||
506 | { | ||||||
507 | die "Mobile::Location: unable to bind to listening socket: $!.\n"; | ||||||
508 | } | ||||||
509 | |||||||
510 | $self->_logger( "Location (sequential) starting on port:", $self->{ Port } ) if $self->{ Web }; | ||||||
511 | |||||||
512 | warn "Location starting up on port: " . $self->{ Port } . ".\n" if $self->{ Debug }; | ||||||
513 | |||||||
514 | $self->_register_with_keyserver; | ||||||
515 | |||||||
516 | # Servers are PERMANENT. | ||||||
517 | |||||||
518 | while ( TRUE ) | ||||||
519 | { | ||||||
520 | next unless my $from_socket = $listening_socket->accept; | ||||||
521 | |||||||
522 | $self->_logger( "Servicing client from:", | ||||||
523 | inet_ntoa( $from_socket->peeraddr ) ) if $self->{ Web }; | ||||||
524 | |||||||
525 | $self->_service_client( $from_socket ); | ||||||
526 | } | ||||||
527 | } | ||||||
528 | |||||||
529 | sub _service_client { | ||||||
530 | |||||||
531 | # Service the receipt (and re-execution) of a mobile agent on | ||||||
532 | # this Location. | ||||||
533 | # | ||||||
534 | # IN: A socket object to communicate with/on. | ||||||
535 | # | ||||||
536 | # OUT: nothing. | ||||||
537 | |||||||
538 | my $self = shift; | ||||||
539 | |||||||
540 | my $socket_object = shift; | ||||||
541 | |||||||
542 | my $tmp_fn = <$socket_object>; # The received filename. | ||||||
543 | chomp( $tmp_fn ); | ||||||
544 | |||||||
545 | # We just want the name-part, so a little regex magic gives it to us. | ||||||
546 | |||||||
547 | $tmp_fn = ( split /\//, $tmp_fn )[-1]; | ||||||
548 | |||||||
549 | my $tmp_linenum = <$socket_object>; # The received line number. | ||||||
550 | chomp( $tmp_linenum ); | ||||||
551 | |||||||
552 | my $data = ''; | ||||||
553 | |||||||
554 | # Receive the signature and mobile agent code. | ||||||
555 | |||||||
556 | while ( my $chunk = <$socket_object> ) | ||||||
557 | { | ||||||
558 | $data = $data . $chunk; | ||||||
559 | } | ||||||
560 | |||||||
561 | # We need to split out the signature from the $data so that we can verify it. | ||||||
562 | |||||||
563 | ( my $agent_signature, $data ) = split /\n--end-sig--\n/, $data; | ||||||
564 | |||||||
565 | # We need to verify the signature. To do this, we need to retrieve | ||||||
566 | # the appropriate PK+ from the keyserver. | ||||||
567 | |||||||
568 | my $key_srv_sock = IO::Socket::INET->new( | ||||||
569 | PeerAddr => $self->{ KeyServer }, | ||||||
570 | PeerPort => RESPONDER_PPORT, | ||||||
571 | Proto => 'tcp' | ||||||
572 | ); | ||||||
573 | |||||||
574 | if ( !defined( $key_srv_sock ) ) | ||||||
575 | { | ||||||
576 | $self->_logger( "Unable to create a verify socket." ) if $self->{ Web }; | ||||||
577 | |||||||
578 | die "Mobile::Location: unable to create a verify socket to keyserver: $!.\n"; | ||||||
579 | } | ||||||
580 | |||||||
581 | my $agent_ip = $socket_object->peerhost; | ||||||
582 | my $agent_port = $socket_object->peerport; | ||||||
583 | |||||||
584 | print $key_srv_sock "$agent_ip\n"; | ||||||
585 | print $key_srv_sock $agent_port; | ||||||
586 | |||||||
587 | $key_srv_sock->shutdown( 1 ); | ||||||
588 | |||||||
589 | my $verify_data = ''; | ||||||
590 | |||||||
591 | while ( my $verify_chunk = <$key_srv_sock> ) | ||||||
592 | { | ||||||
593 | $verify_data = $verify_data . $verify_chunk; | ||||||
594 | } | ||||||
595 | |||||||
596 | $key_srv_sock->close; | ||||||
597 | |||||||
598 | # This splits the signature and data on the SIGNATURE_DELIMITER | ||||||
599 | # pattern as used by the keyserver. | ||||||
600 | |||||||
601 | ( my $verify_signature, $verify_data ) = split /\n--end-sig--\n/, $verify_data; | ||||||
602 | |||||||
603 | if ( $verify_signature eq "NOSIG" ) | ||||||
604 | { | ||||||
605 | $self->_logger( "WARNING: The keyserver returned NOSIG." ) if $self->{ Web }; | ||||||
606 | |||||||
607 | # We need to abort, as the keyserver does not have the requested | ||||||
608 | # signature. This is bad. | ||||||
609 | |||||||
610 | $socket_object->close; | ||||||
611 | |||||||
612 | exit 0; # Short circuit. | ||||||
613 | } | ||||||
614 | |||||||
615 | open VERIFY_FILE, ">$agent_ip.$agent_port.public" | ||||||
616 | or die "Mobile::Location: could not create verify key file: $!\n"; | ||||||
617 | |||||||
618 | print VERIFY_FILE $verify_data; | ||||||
619 | |||||||
620 | close VERIFY_FILE; | ||||||
621 | |||||||
622 | my $agent_pkplus = new Crypt::RSA::Key::Public( | ||||||
623 | Filename => "$agent_ip.$agent_port.public" | ||||||
624 | ); | ||||||
625 | |||||||
626 | my $rsa = new Crypt::RSA; | ||||||
627 | |||||||
628 | my $verify = $rsa->verify( | ||||||
629 | Message => $data, | ||||||
630 | Signature => $agent_signature, | ||||||
631 | Key => $agent_pkplus, | ||||||
632 | Armour => TRUE | ||||||
633 | ); | ||||||
634 | |||||||
635 | if ( !$verify ) | ||||||
636 | { | ||||||
637 | $self->_logger( "WARNING: could not verify signature for:", | ||||||
638 | inet_ntoa( $socket_object->peeraddr ), | ||||||
639 | "using $agent_ip/$agent_port." ) if $self->{ Web }; | ||||||
640 | |||||||
641 | die "Mobile::Location: could not verify signature of received mobile agent. Aborting ... \n"; | ||||||
642 | } | ||||||
643 | |||||||
644 | $self->_logger( "Signature verified for $agent_ip/$agent_port." ) if $self->{ Web }; | ||||||
645 | |||||||
646 | # Remove the agents PK+ keyfile, as we no longer need it. | ||||||
647 | |||||||
648 | unlink "$agent_ip.$agent_port.public"; | ||||||
649 | |||||||
650 | # At this stage, we have a mobile agent that is encrypted using the PK+ | ||||||
651 | # of this Location, and we have verified the signature to be correct. | ||||||
652 | # We use this Location's PK- to decrypt it. | ||||||
653 | |||||||
654 | my $plaintext = $rsa->decrypt( | ||||||
655 | Cyphertext => $data, | ||||||
656 | Key => $self->{ PrivateKey }, | ||||||
657 | Armour => TRUE | ||||||
658 | ); | ||||||
659 | |||||||
660 | if ( !defined( $plaintext ) ) | ||||||
661 | { | ||||||
662 | $self->_logger( "WARNING: unable to decrypt Cyphertext for: $agent_ip/$agent_port." ) if $self->{ Web }; | ||||||
663 | |||||||
664 | die "Mobile::Location: decryption errors - aborting.\n"; | ||||||
665 | } | ||||||
666 | |||||||
667 | # We have a plaintext representation of the mobile agent, which | ||||||
668 | # we turn back into an array of lines. | ||||||
669 | |||||||
670 | my @entire_thing = split /\n/, $plaintext; | ||||||
671 | |||||||
672 | # Add a newline to each of the "lines" in @entire_thing. | ||||||
673 | |||||||
674 | foreach my $line ( @entire_thing ) | ||||||
675 | { | ||||||
676 | $line = $line . "\n"; | ||||||
677 | } | ||||||
678 | |||||||
679 | # Ensure the Location is in the correct STARTUP directory. | ||||||
680 | |||||||
681 | chdir $_PWD; | ||||||
682 | |||||||
683 | # We enter the run-time directory if it exists. | ||||||
684 | |||||||
685 | if ( -e RUN_LOCATION_DIR ) | ||||||
686 | { | ||||||
687 | chdir( RUN_LOCATION_DIR ); | ||||||
688 | } | ||||||
689 | else # Or, if it does NOT exist, we create it then change into it. | ||||||
690 | { | ||||||
691 | mkdir( RUN_LOCATION_DIR ); | ||||||
692 | chdir( RUN_LOCATION_DIR ); | ||||||
693 | } | ||||||
694 | |||||||
695 | # As we are now in the run-time directory, we continue with the relocation. | ||||||
696 | |||||||
697 | if ( $self->{ Log } ) | ||||||
698 | { | ||||||
699 | my $logname = "last_agent_" . $$ . ".log"; # Note use of PID. | ||||||
700 | |||||||
701 | # Put a copy of the mobile agent into the log file. | ||||||
702 | |||||||
703 | my $logOK = open AGENTLOGFILE, ">$logname" | ||||||
704 | or warn "Mobile::Location: could not open log file: $!.\n"; | ||||||
705 | |||||||
706 | print AGENTLOGFILE @entire_thing if defined $logOK; | ||||||
707 | |||||||
708 | close AGENTLOGFILE if defined $logOK; | ||||||
709 | |||||||
710 | $self->_logger2( "Received agent logged to: $logname." ) if $self->{ Web }; | ||||||
711 | } | ||||||
712 | |||||||
713 | # Untaint the filename received from Scooby, using a regex. | ||||||
714 | |||||||
715 | $tmp_fn =~ /^([-\@\w_.]+)$/; | ||||||
716 | $tmp_fn = $1; | ||||||
717 | |||||||
718 | # Create the "mutated" agent on the local storage. | ||||||
719 | |||||||
720 | open FILETOCHECK, ">$tmp_fn" | ||||||
721 | or die "Location::Mobile: could not create agent disk-file: $!:"; | ||||||
722 | |||||||
723 | my $label = _generate_label( $tmp_fn, $tmp_linenum ); | ||||||
724 | |||||||
725 | # Start processing the agent one "line" at a time. | ||||||
726 | |||||||
727 | my $chunk = shift @entire_thing; | ||||||
728 | |||||||
729 | # Print the "magic" first line. | ||||||
730 | |||||||
731 | print FILETOCHECK $chunk; | ||||||
732 | |||||||
733 | # # Add the Opcode mask to the code. | ||||||
734 | # | ||||||
735 | # print FILETOCHECK "\nuse ops qw( " . | ||||||
736 | # | ||||||
737 | # # Basic operation mask - relocating to a single Location. | ||||||
738 | # | ||||||
739 | # 'aassign add aelem av2arylen ' . | ||||||
740 | # 'backtick ' . | ||||||
741 | # 'caller chdir chomp chop closedir concat const ' . | ||||||
742 | # 'defined die ' . | ||||||
743 | # 'enter entereval enteriter entersub eq ' . | ||||||
744 | # 'ftdir fteexec ftewrite ' . | ||||||
745 | # 'gelem goto grepstart gv ' . | ||||||
746 | # 'helem ' . | ||||||
747 | # 'iter ' . | ||||||
748 | # 'join ' . | ||||||
749 | # 'last leaveeval leaveloop leavesub lstat ' . | ||||||
750 | # 'method method_named ' . | ||||||
751 | # 'ne negate next not null ' . | ||||||
752 | # 'open_dir ' . | ||||||
753 | # 'padany pop push pushmark ' . | ||||||
754 | # 'readdir refgen require return rv2av rv2cv rv2gv rv2hv rv2sv ' . | ||||||
755 | # 'sassign scalar seq shift sne split stat stringify stub substr ' . | ||||||
756 | # 'undef unshift unstack ' . | ||||||
757 | # | ||||||
758 | # # Relocating to multiple Locations (requires more operations). | ||||||
759 | # # Most of these are needed by Carp.pm, which is used by IO::Socket | ||||||
760 | # # (among other modules). | ||||||
761 | # | ||||||
762 | # 'anonhash anonlist ' . | ||||||
763 | # 'exists ' . | ||||||
764 | # 'keys ' . | ||||||
765 | # 'gt ' . | ||||||
766 | # 'length lt ' . | ||||||
767 | # 'mapstart ' . | ||||||
768 | # 'ord ' . | ||||||
769 | # 'postinc predec preinc ' . | ||||||
770 | # 'redo ref ' . | ||||||
771 | # 'sprintf subtract ' . | ||||||
772 | # 'wantarray ' . | ||||||
773 | # | ||||||
774 | # # Adding the ops required by Crypt::RSA and its support modules. | ||||||
775 | # | ||||||
776 | # 'anoncode ' . | ||||||
777 | # 'bless bit_and bit_or bit_xor ' . | ||||||
778 | # 'chr close complement ' . | ||||||
779 | # 'divide delete dofile ' . | ||||||
780 | # 'each enterwrite eof ' . | ||||||
781 | # 'fcntl fileno flip flop formline fteread ftfile ftis ftsize ' . | ||||||
782 | # 'ge getc ' . | ||||||
783 | # 'hex ' | ||||||
784 | # 'int index ioctl ' . | ||||||
785 | # 'lc le left_shift lslice ' | ||||||
786 | # 'modulo multiply ' | ||||||
787 | # 'oct open ' | ||||||
788 | # 'pack padsv postdec pow print prtf ' | ||||||
789 | # 'quotemeta ' . | ||||||
790 | # 'rand read readline repeat reverse regcreset ' . | ||||||
791 | # 'select splice srand sysread syswrite ' | ||||||
792 | # 'tell tie trans truncate ' | ||||||
793 | # 'uc unpack ' | ||||||
794 | # 'values vec ' | ||||||
795 | # 'warn ' | ||||||
796 | # 'xor ' | ||||||
797 | # | ||||||
798 | # $self->{ Ops } . " );\n\n"; # Forces safety. | ||||||
799 | # | ||||||
800 | |||||||
801 | # Insert the GOTO label line. | ||||||
802 | |||||||
803 | print FILETOCHECK "goto $label;\n"; | ||||||
804 | |||||||
805 | # We re-initialize the line counter. | ||||||
806 | |||||||
807 | my $line_counter = 2; | ||||||
808 | |||||||
809 | # Process the rest of the agent, one "line" at a time. | ||||||
810 | |||||||
811 | while ( $chunk = shift @entire_thing ) | ||||||
812 | { | ||||||
813 | if ( $line_counter == $tmp_linenum ) # We are at the 'next' line. | ||||||
814 | { | ||||||
815 | # Insert a 'label' statement before the next instruction. | ||||||
816 | |||||||
817 | print FILETOCHECK "$label:\n1;\n"; | ||||||
818 | |||||||
819 | print FILETOCHECK "use Mobile::Executive;\n\n"; | ||||||
820 | } | ||||||
821 | print FILETOCHECK $chunk; | ||||||
822 | $line_counter++; | ||||||
823 | } | ||||||
824 | |||||||
825 | close FILETOCHECK; | ||||||
826 | |||||||
827 | # Note: The agent now exists on the local run-time storage of this Location. | ||||||
828 | |||||||
829 | $self->_logger2( "Received $tmp_fn from", $socket_object->peerhost, | ||||||
830 | " next line: $tmp_linenum." ) if $self->{ Web }; | ||||||
831 | |||||||
832 | warn "Received $tmp_fn from ", | ||||||
833 | $socket_object->peerhost, | ||||||
834 | "; next line: $tmp_linenum.\n" if $self->{ Debug }; | ||||||
835 | |||||||
836 | # Construct the command-line that will continue to execute the agent. | ||||||
837 | |||||||
838 | my $cmd = "perl -d:Scooby " . "$tmp_fn"; | ||||||
839 | |||||||
840 | # Close the socket as we are now finished with it. | ||||||
841 | |||||||
842 | close $socket_object | ||||||
843 | or warn "Mobile::Location: close failed: $!.\n"; | ||||||
844 | |||||||
845 | # Continue to execute the agent at this location. | ||||||
846 | |||||||
847 | warn "Continuing to execute agent: $cmd.\n" if $self->{ Debug }; | ||||||
848 | |||||||
849 | $self->_logger2( "Continuing to execute mobile agent: $cmd." ) if $self->{ Web }; | ||||||
850 | |||||||
851 | my $results = qx( $cmd ); | ||||||
852 | |||||||
853 | print "$results" if $results ne ''; | ||||||
854 | } | ||||||
855 | |||||||
856 | sub _spawn_web_monitoring_service { | ||||||
857 | |||||||
858 | # Creates a subprocess to run the web-based monitoring service. | ||||||
859 | # | ||||||
860 | # IN: nothing. | ||||||
861 | # | ||||||
862 | # OUT: nothing. | ||||||
863 | |||||||
864 | my $self = shift; | ||||||
865 | |||||||
866 | my $child_pid = fork; | ||||||
867 | |||||||
868 | die "No spawned web-based monitoring service: $!.\n" unless defined( $child_pid ); | ||||||
869 | |||||||
870 | if ( $child_pid == FALSE ) | ||||||
871 | { | ||||||
872 | # This is the CHILD code, which creates a server on "Port+1" and | ||||||
873 | |||||||
874 | $self->_start_web_service if $self->{ Web }; | ||||||
875 | |||||||
876 | exit 0; | ||||||
877 | } | ||||||
878 | } | ||||||
879 | |||||||
880 | ########################################################################## | ||||||
881 | # These are not methods, they're support subroutines. | ||||||
882 | ########################################################################## | ||||||
883 | |||||||
884 | sub _generate_label { | ||||||
885 | |||||||
886 | # Generate a unique label string. | ||||||
887 | # | ||||||
888 | # IN: A filename and a line number. | ||||||
889 | # Note: These values are combined with the time to produce a | ||||||
890 | # random (and hopefully unique) label. | ||||||
891 | # | ||||||
892 | # OUT: An appropriately formatted label. | ||||||
893 | |||||||
894 | my $fn = shift; | ||||||
895 | my $ln = shift; | ||||||
896 | |||||||
897 | my $tm = time; | ||||||
898 | |||||||
899 | # Remove any unwanted characters from the filename. | ||||||
900 | |||||||
901 | $fn =~ s/[^a-zA-Z0-9]//; | ||||||
902 | |||||||
903 | return ( 'LABEL_' . $fn . $ln . $tm ); | ||||||
904 | } | ||||||
905 | |||||||
906 | sub _check_for_modules { | ||||||
907 | |||||||
908 | # Given a list of module classes, check to see if they exist within this | ||||||
909 | # Location's Perl environment. | ||||||
910 | # | ||||||
911 | # IN: A list of fully-qualified (one or more) module names. | ||||||
912 | # A "fully-qualified module name" is "Devel::Scooby", as | ||||||
913 | # opposed to just "Scooby". | ||||||
914 | # | ||||||
915 | # OUT: A list of modules NOT found. An empty list signals SUCCESS. | ||||||
916 | |||||||
917 | my @mods_to_check = @_; # Taken from IN. | ||||||
918 | |||||||
919 | my @list_of_not_found = (); # Will be used as OUT. | ||||||
920 | |||||||
921 | foreach my $mod ( @mods_to_check ) | ||||||
922 | { | ||||||
923 | # Untaint the $mod values prior to their use, using a regex. | ||||||
924 | |||||||
925 | $mod =~ /^([\w\d:_]+)$/; | ||||||
926 | $mod = $1; | ||||||
927 | |||||||
928 | eval "require $mod;"; | ||||||
929 | if ( $@ ) | ||||||
930 | { | ||||||
931 | |||||||
932 | # The module does not exist within this Perl!! | ||||||
933 | |||||||
934 | push @list_of_not_found, $mod; | ||||||
935 | } | ||||||
936 | } | ||||||
937 | |||||||
938 | return @list_of_not_found; | ||||||
939 | } | ||||||
940 | |||||||
941 | sub _spawn_network_service { | ||||||
942 | |||||||
943 | # Spawn a sub-process, running at protocol port number "$self->{ Port }+1" | ||||||
944 | # to respond to an agent's query re: required classes. | ||||||
945 | # | ||||||
946 | # IN: The protocol port to start the service on. | ||||||
947 | # | ||||||
948 | # OUT: nothing. | ||||||
949 | |||||||
950 | my $port = shift; | ||||||
951 | |||||||
952 | # Untaint the value for $port, as it can be initialized from | ||||||
953 | # the command-line, and is therefore TAINTED. | ||||||
954 | |||||||
955 | $port =~ /^(\d+)$/; | ||||||
956 | $port = $1; | ||||||
957 | |||||||
958 | my $child_pid = fork; | ||||||
959 | |||||||
960 | die "No spawned network service: $!.\n" unless defined( $child_pid ); | ||||||
961 | |||||||
962 | # This child code never ends, as servers are PERMANENT. | ||||||
963 | |||||||
964 | if ( $child_pid == FALSE ) | ||||||
965 | { | ||||||
966 | # This is the CHILD code, which creates a server on "Port+1" and | ||||||
967 | # listens for requests from a remote mobile agent. | ||||||
968 | |||||||
969 | my $trans_serv = getprotobyname( 'tcp' ); | ||||||
970 | my $local_addr = sockaddr_in( $port, INADDR_ANY ); | ||||||
971 | |||||||
972 | socket( TCP_SOCK, PF_INET, SOCK_STREAM, $trans_serv ) | ||||||
973 | or die "Mobile::Location: socket creation failed: $!.\n"; | ||||||
974 | setsockopt( TCP_SOCK, SOL_SOCKET, SO_REUSEADDR, 1 ) | ||||||
975 | or warn "Mobile::Location: could not set socket option: $!.\n"; | ||||||
976 | bind( TCP_SOCK, $local_addr ) | ||||||
977 | or die "Mobile::Location: bind to address failed: $!.\n"; | ||||||
978 | listen( TCP_SOCK, SOMAXCONN ) | ||||||
979 | or die "Mobile::Location: listen couldn't: $!.\n"; | ||||||
980 | |||||||
981 | my $from_who; | ||||||
982 | |||||||
983 | while ( $from_who = accept( CHECK_MOD_SOCK, TCP_SOCK ) ) | ||||||
984 | { | ||||||
985 | # Switch on AUTO-FLUSHING. | ||||||
986 | |||||||
987 | my $previous = select CHECK_MOD_SOCK; | ||||||
988 | $| = 1; | ||||||
989 | select $previous; | ||||||
990 | |||||||
991 | my $data = ''; | ||||||
992 | |||||||
993 | # Get the list of modules from the other Location. | ||||||
994 | |||||||
995 | while ( my $chunk = |
||||||
996 | { | ||||||
997 | $data = $data . $chunk; | ||||||
998 | } | ||||||
999 | |||||||
1000 | my @modules = split / /, $data; | ||||||
1001 | |||||||
1002 | my @list = _check_for_modules( @modules ); | ||||||
1003 | |||||||
1004 | if ( @list ) | ||||||
1005 | { | ||||||
1006 | print CHECK_MOD_SOCK "NOK: @list"; | ||||||
1007 | } | ||||||
1008 | else | ||||||
1009 | { | ||||||
1010 | print CHECK_MOD_SOCK "OK"; | ||||||
1011 | } | ||||||
1012 | |||||||
1013 | close CHECK_MOD_SOCK | ||||||
1014 | or warn "Mobile::Location: close failed: $!.\n"; | ||||||
1015 | } | ||||||
1016 | |||||||
1017 | close TCP_SOCK; # This code may never be reached. It only | ||||||
1018 | # executes if the call to "accept" fails. | ||||||
1019 | } | ||||||
1020 | |||||||
1021 | # This is the parent process code. That is, the value of | ||||||
1022 | # $child_pid is defined and is greater than 0. | ||||||
1023 | } | ||||||
1024 | |||||||
1025 | 1; # As it is required by Perl. | ||||||
1026 | |||||||
1027 | ########################################################################## | ||||||
1028 | # Documentation starts here. | ||||||
1029 | ########################################################################## | ||||||
1030 | |||||||
1031 | =pod | ||||||
1032 | |||||||
1033 | =head1 NAME | ||||||
1034 | |||||||
1035 | "Mobile::Location" - a class that provides for the creation of Scooby mobile agent environments (aka Location, Site or Place). | ||||||
1036 | |||||||
1037 | =head1 VERSION | ||||||
1038 | |||||||
1039 | 4.0x (the v1.0x, v2.0x and v3.0x series were never released). | ||||||
1040 | |||||||
1041 | =head1 SYNOPSIS | ||||||
1042 | |||||||
1043 | use Mobile::Location; | ||||||
1044 | |||||||
1045 | my $location = Mobile::Location->new; | ||||||
1046 | |||||||
1047 | $location->start_sequential; | ||||||
1048 | |||||||
1049 | or | ||||||
1050 | |||||||
1051 | $location->start_concurrent; | ||||||
1052 | |||||||
1053 | |||||||
1054 | =head1 SOME IMPORTANT NOTES FOR LOCATION WRITERS | ||||||
1055 | |||||||
1056 | 1. Never, ever run a Location as 'root'. If you do, this module will die. Running as 'root' is a serious security risk, as a mobile agent is foreign code that you are trusting to execute in a non-threatening way on your computer. (Can you spell the word 'v', 'i', 'r', 'u', 's'?!?) | ||||||
1057 | |||||||
1058 | 2. The B |
||||||
1059 | |||||||
1060 | 3. Never, ever run a Location on the same machine that is acting as your keyserver (it's a really bad idea, so don't even think about it). | ||||||
1061 | |||||||
1062 | =head1 DESCRIPTION | ||||||
1063 | |||||||
1064 | Part of the Scooby mobile agent machinery, the B |
||||||
1065 | |||||||
1066 | =head1 Overview | ||||||
1067 | |||||||
1068 | Simply create an object of type B |
||||||
1069 | |||||||
1070 | =head1 Construction and initialization | ||||||
1071 | |||||||
1072 | Create a new instance of the B |
||||||
1073 | |||||||
1074 | =over 4 | ||||||
1075 | |||||||
1076 | my $location = Mobile::Location->new; | ||||||
1077 | |||||||
1078 | =back | ||||||
1079 | |||||||
1080 | Optional named parameters (with default values) are: | ||||||
1081 | |||||||
1082 | =over 4 | ||||||
1083 | |||||||
1084 | B |
||||||
1085 | |||||||
1086 | B |
||||||
1087 | |||||||
1088 | B |
||||||
1089 | |||||||
1090 | B |
||||||
1091 | |||||||
1092 | B |
||||||
1093 | |||||||
1094 | =back | ||||||
1095 | |||||||
1096 | Note that any received mobile agent executes in a directory called "Location", which will be created (if needs be) in the directory that houses this Location. Any "logs" are also created in the "Location" directory. | ||||||
1097 | |||||||
1098 | A constructor example is: | ||||||
1099 | |||||||
1100 | =over 4 | ||||||
1101 | |||||||
1102 | my $place = Mobile::Location->new( Port => 5555, Debug => 1 ); | ||||||
1103 | |||||||
1104 | =back | ||||||
1105 | |||||||
1106 | creates an object that will display all STDERR status messages, and use protocol port number 5555 for connections. Logging of received agents to disk is off. The standard Opcode mask is in effect. And logging to disk is on, as is the HTTP server. | ||||||
1107 | |||||||
1108 | When the Location is constructed with B |
||||||
1109 | |||||||
1110 | Note: If a Location crashes (or is killed), the second network service can sometimes keeps running. After all, it is a separate process (albeit a child of the original). Trying to restart the Location results in an "bind to address failed" error message. Use the B |
||||||
1111 | |||||||
1112 | =head1 Class and object methods | ||||||
1113 | |||||||
1114 | =over 4 | ||||||
1115 | |||||||
1116 | =item B |
||||||
1117 | |||||||
1118 | Start the location as a passive server, which operates concurrently. Once connected to a client, the server forks another process to receive and continue executing a mobile agent. This is the preferred method to use when there exists the potential to have an agent execute for a long period of time. | ||||||
1119 | |||||||
1120 | =item B |
||||||
1121 | |||||||
1122 | Start the location as a passive server, which operates sequentially. Once connected to a client, the server sequentially processes the receipt and continued executing of a mobile agent. This is OK if the agent is quick and not processor intensive. If the agent has the potential to execute for a long period of time, use the B |
||||||
1123 | |||||||
1124 | =back | ||||||
1125 | |||||||
1126 | =head1 Internal methods/subroutines | ||||||
1127 | |||||||
1128 | The following list of subroutines are used within the class to provide support services to the class methods. These subroutines should not be invoked through the object (and in some cases, cannot be invoked through the object). | ||||||
1129 | |||||||
1130 | =over 4 | ||||||
1131 | |||||||
1132 | =item B<_generate_label> | ||||||
1133 | |||||||
1134 | Takes a filename and line number, then combines them with the current time to produce a random, unique label. | ||||||
1135 | |||||||
1136 | =item B<_check_for_modules> | ||||||
1137 | |||||||
1138 | Given a list of module names, checks to see if the Location's Perl system has the module installed or not. | ||||||
1139 | |||||||
1140 | =item B<_spawn_network_service> | ||||||
1141 | |||||||
1142 | Used by the B |
||||||
1143 | |||||||
1144 | =item B<_service_client> | ||||||
1145 | |||||||
1146 | Given a socket object (and the instances init data), service the relocation of a Scooby mobile agent. | ||||||
1147 | |||||||
1148 | =item B<_register_with_keyserver> | ||||||
1149 | |||||||
1150 | Creates a PK+ and PK- value for the server, storing the PK+ in the keyserver, and the PK- in the object's state. | ||||||
1151 | |||||||
1152 | =item B<_logger> and B<_logger2> | ||||||
1153 | |||||||
1154 | Logs a message to the LOGFILE. | ||||||
1155 | |||||||
1156 | =item B<_build_index_dot_html> | ||||||
1157 | |||||||
1158 | Builds the INDEX.HTML page for use by the HTTP-based Monitoring Service. | ||||||
1159 | |||||||
1160 | =item B<_build_clearlog_dot_html> | ||||||
1161 | |||||||
1162 | Builds the CLEARLOG.HTML page for use by the HTTP-based Monitoring Service. | ||||||
1163 | |||||||
1164 | =item B<_start_web_service> | ||||||
1165 | |||||||
1166 | Starts a small web server running at port 8080 (HTTP_PORT), and uses the two "_build_*" routines just described. | ||||||
1167 | |||||||
1168 | =item B<_spawn_web_monitoring_service> | ||||||
1169 | |||||||
1170 | Creates a subprocess and starts the web server. | ||||||
1171 | |||||||
1172 | =back | ||||||
1173 | |||||||
1174 | =head1 SEE ALSO | ||||||
1175 | |||||||
1176 | The B |
||||||
1177 | |||||||
1178 | The Scooby Website: B |
||||||
1179 | |||||||
1180 | =head1 AUTHOR | ||||||
1181 | |||||||
1182 | Paul Barry, Institute of Technology, Carlow in Ireland, B |
||||||
1183 | |||||||
1184 | =head1 COPYRIGHT | ||||||
1185 | |||||||
1186 | Copyright (c) 2003, Paul Barry. All Rights Reserved. | ||||||
1187 | |||||||
1188 | This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself. | ||||||
1189 |