eval '(exit $?0)' && eval 'PERL=`which perl5`; exec $PERL -wS $0 ${1+"$@"}' & eval 'setenv PERL `which perl5`; exec $PERL -wS $0 $argv:q' if 0; ### Local Variables: *** ### mode:perl *** ### comment-column:0 *** ### comment-start: "### " *** ### comment-end: "***" *** ### End: *** # # ****************DO NOT MOVE OR CHANGE LINES ABOVE THIS********************* # # The first set of lines runs perl from any shell. The second set of lines # identifies the rest of the file as PERL for EMACS autoformatting. # See end of copyright for more information. # # # ------------------------------------------------------------------- # X-BONE # # http://www.isi.edu/xbone # USC Information Sciences Institute (USC/ISI) # Marina del Rey, California 90292, USA # Copyright (c) 1998-2005 # # ------------------------------------------------------------------- # # Copyright (c) 1998-2005 by the University of Southern California. # All rights reserved. # # Permission to use, copy, modify, and distribute this software and # its documentation in source and binary forms for non-commercial # purposes and without fee is hereby granted, provided that the above # copyright notice appear in all copies and that both the copyright # notice and this permission notice appear in supporting # documentation, and that any documentation, advertising materials, # and other materials related to such distribution and use acknowledge # that the software was developed by the University of Southern # California, Information Sciences Institute. The name of the # University may not be used to endorse or promote products derived # from this software without specific prior written permission. # # THE UNIVERSITY OF SOUTHERN CALIFORNIA MAKES NO REPRESENTATIONS ABOUT # THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS # PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. # # Other copyrights might apply to parts of this software and are so # noted when applicable. # # ------------------------------------------------------------------- # # Effort partly sponsored by the Defense Advanced Research Projects # Agency (DARPA) and Air Force Research Laboratory, Air Force Materiel # Command, USAF, under agreement numbers F30602-98-1-0200 (X-Bone) and # F30602-01-2-0529 (DynaBone). The views and conclusions contained # herein are those of the authors and should not be interpreted as # necessarily representing the official policies or endorsements, # either expressed or implied, of the Defense Advanced Research # Projects Agency (DARPA), the Air Force Research Laboratory, or the # U.S. Government. # # This work was partly supported by the NSF STI-XTEND (ANI-0230789) # and NETFS (ANI-0129689) projects. Any opinions, findings, and # conclusions or recommendations expressed in this material are those # of the authors and do not necessarily reflect the views of the # National Science Foundation. # # ------------------------------------------------------------------- # $RCSfile: xb-api-client.pl,v $ # # $Revision: 1.27 $ # $Author: pingali $ # $Date: 2005/04/08 19:19:56 $ # $State: Exp $ # ---------------------------------------------------------------------------- # The first lines start PERL on any system where perl is in the path. # This is a modified version of the "start perl" script provided in # the PERL man pages, which starts perl on the remainder of the file # regardless of whether it is run under sh, csh, or perl. # # This version is modified to dynamically locate the perl path, rather # than requiring it be hard-coded. # set library search path BEGIN { use strict; use sigtrap; use FindBin; use Config; delete $ENV{PATH}; my $version = $Config{'version'}; my $arch = $Config{'archname'}; my $ldir = $FindBin::RealBin; foreach my $p ($ldir, "$ldir/modules", "$ldir/modules/cpan", "$ldir/modules/cpan/lib/perl5/$version", "$ldir/modules/cpan/lib/perl5/$version/$arch", "$ldir/modules/cpan/lib/perl5/site_perl/mach", "$ldir/modules/cpan/lib/perl5/site_perl/mach/$arch", "$ldir/modules/cpan/lib/perl5/site_perl/$version", "$ldir/modules/cpan/lib/perl5/site_perl/$version/$arch", "$ldir/modules/cpan/lib/perl5/site_perl/$version/mach", "$ldir/modules/cpan/lib/perl5/site_perl/$version/mach/$arch", ) { if(-d $p) { unshift @INC, $p; } } # untaint the resulting include path so "use" works foreach my $i (@INC) { if($i =~ /(.*)/) { $i = $1;} } }; ############################################################################## # END OF PREFIX - PUT YOUR PERL CODE BELOW # ############################################################################## # Primary Author: Yu-Shun Wang # Description: This is a simple text-based XBone API client for XBone. use strict; use Data::Dumper; use IO::Select; use IO::Socket::SSL; use Socket; use Socket6; use IO::Socket::SSLv6; use XB_API_parser; use XB_API_SUBS; use XB_Params; use XB_API_GUI; use XB_XML_GUI; use XB_XML_scan; my $use_xml = 1; $Data::Dumper::Indent = 1; my $api_parser = XB_API_parser->new; my %client_params = ( "node_cert" => $XB_Params::node_cert, "node_key" => $XB_Params::node_key, "ca_cert" => $XB_Params::ca_cert, "ca_path" => $XB_Params::ca_path ); my %app_ex1 = ( "program" => 'app-ex1', "script" => 'http://www.isi.edu/~pingali/sample-app-deploy-script.sh', "checksum" => '6e0323f0b00fb9863ef2f7ba9924b4c8', "suid" => 'nobody', "nodes" => 'all', "ifaces" => 'all' ); my %app_ex2 = ( "program" => 'app-ex2', "script" => 'http://www.isi.edu/~pingali/sample-app-deploy-script.sh', "checksum" => '6e0323f0b00fb9863ef2f7ba9924b4c8', "suid" => 'nobody', "nodes" => 'all', "ifaces" => 'all' ); my %app_args; $app_args{$app_ex1{program}} = \%app_ex1; $app_args{$app_ex2{program}} = \%app_ex2; my %cmd_args = ( # auth/credential info "auth_type" => 'X509', "user_name" => 'Yu-Shun Wang', "user_email" => 'yushunwa@isi.edu', # create properties "search_radius" => 5, "manager" => "tnn.isi.edu", "manager_port" => $XB_Params::xbone_api_port, # overlay properties "topology" => 'star', "overlay_name" => 'test.' . $XB_Params::XBONE_NET, "hosts" => 5, "host_os" => 'freebsd', "routers" => 1, "router_os" => 'freebsd', "dns" => 'no', "IPsec_authentication" => 'none', "IPsec_encryption" => 'none', "dynamic_routing" => 'no', "address_type" => 'ipv4', # misc "name_server" => undef, "name_server_port" => undef, "address_server" => undef, "address_server_port" => undef, "application" => \%app_args, "dummynet" => 'no', "dummynet_bandwidth" => 30, "dummynet_bandwidth_unit" => 'Mbit/s', "dummynet_delay" => 25, "dummynet_queue" => "100", "dummynet_queue_unit" => "slots", "dummynet_loss_rate" => 0.02, "custom_netlist" => "able xray baker xray able baker", ); # Missing parameters: # - routing="static"|"dynamic" to replace "dynamic_routing" # - host_os # # Extra parameters # - 'os' => 'FreeBSD Linux', # - 'ip_address' => '10.2.43.122', # - 'local_ip_address' => '10.2.43.122', # - 'remote_ip_address' => '1:2344:ab34:34', # - 'application' => 'program name', # - 'application_script' => 'script for application program', # - 'custom_netlist' => # 'able xray baker xray charlie xray xray zero dog zero easy zero' # == define some recursive overlays for testing =============================== my $r0 = " (xbone 1.5 2.0 (credential (user_name 'Yu-Shun Wang') (user_email yushunwa\@isi.edu) (auth_type x509)) (create_overlay (search_radius 5) (xol 1.1 (class 3Line (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node a (nodeprops ) (interfaces (interface left) (interface right) )) (node c (nodeprops ) (interfaces (interface left) (interface right) ))) (linklist (link link0)) (netlist (a.right link0 c.left)) (exportlist (a.left) (c.right) )) (class 3LineRing (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node X: 3Line (nodeprops )) (node d (nodeprops ) (interfaces (interface left) (interface right)))) (linklist (link link0) (link link1)) (netlist (X.left link0 d.right) (X.right link1 d.left) ) (exportlist )) (root 3LineRing foobar) )))\n"; my $r1 = " (xbone 1.5 2.0 (credential (user_name 'Yu-Shun Wang') (user_email yushunwa\@isi.edu) (auth_type x509)) (create_overlay (search_radius 5) (xol 1.1 (class 3Line (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node a (nodeprops ) (interfaces (interface left) (interface right) )) (node c (nodeprops ) (interfaces (interface left) (interface right) ))) (linklist (link link0)) (netlist (a.right link0 c.left)) (exportlist (a.left) (c.right) )) (class 3LineRing (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node X: 3Line (nodeprops )) (node Y: 3Line (nodeprops ))) (linklist (link link0) (link link1)) (netlist (X.left link0 Y.right) (X.right link1 Y.left) ) (exportlist )) (root 3LineRing foobar) )))\n"; my $r2 = " (xbone 1.5 2.0 (credential (user_name 'Yu-Shun Wang') (user_email yushunwa\@isi.edu) (auth_type x509) ) (create_overlay (search_radius 5) (xol 1.1 (class star (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node host_0 (nodeprops (os freebsd|kame) ) (interfaces (interface if_0) (interface ext0))) (node host_1 (nodeprops (os freebsd|kame) ) (interfaces (interface if_0) (interface ext1))) (node router_0 (nodeprops (os linux|kame) ) (interfaces (interface if_0) (interface if_1)))) (linklist (link link_0) (link link_1)) (netlist (host_0.if_0 link_0 router_0.if_0) (host_1.if_0 link_1 router_0.if_1)) (exportlist (host_0.ext0) (host_1.ext1) )) (class 3Line (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node a: star (nodeprops )) (node b (nodeprops ) (interfaces (interface left) (interface right) )) (node c: star (nodeprops ))) (linklist (link link0) (link link1)) (netlist (a.ext0 link0 b.left) (b.right link1 c.ext1) ) (exportlist (a.ext1) (c.ext0) )) (class 3LineRing (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node X: 3Line (nodeprops )) (node Y: 3Line (nodeprops ))) (linklist (link link0) (link link1)) (netlist (X.ext1 link0 Y.ext0) (X.ext0 link1 Y.ext1) ) (exportlist )) (root 3LineRing foobar) )))\n"; my $r3 = " (xbone 1.5 2.0 (credential (user_name 'Yu-Shun Wang') (user_email yushunwa\@isi.edu) (auth_type x509) ) (create_overlay (search_radius 5) (xol 1.1 (class star (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node host_0 (nodeprops (os freebsd|kame) ) (interfaces (interface if_0) (interface ext0))) (node host_1 (nodeprops (os freebsd|kame) ) (interfaces (interface if_0) (interface ext1))) (node router_0 (nodeprops (os linux|kame) ) (interfaces (interface if_0) (interface if_1)))) (linklist (link link_0) (link link_1)) (netlist (host_0.if_0 link_0 router_0.if_0) (host_1.if_0 link_1 router_0.if_1)) (exportlist (host_0.ext0) (host_1.ext1) )) (class Line (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node a: star (nodeprops )) (node b (nodeprops ) (interfaces (interface left) (interface right) )) (node c (nodeprops ) (interfaces (interface if))) (node d (nodeprops ) (interfaces (interface if))) ) (linklist (link link0) (link link1) (link link2)) (netlist (a.ext0 link0 d.if) (a.ext1 link1 b.left) (b.right link2 c.if) ) (exportlist )) (root Line foobar) )))\n"; my $r4 = " (xbone 1.5 2.0 (credential (user_name 'Yu-Shun Wang') (user_email yushunwa\@isi.edu) (auth_type x509) ) (create_overlay (search_radius 5) (xol 1.1 (class star (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node host_0 (nodeprops (os freebsd|kame) ) (interfaces (interface if_0) (interface ext0))) (node host_1 (nodeprops (os freebsd|kame) ) (interfaces (interface if_0) (interface ext1))) (node router_0 (nodeprops (os linux|kame) ) (interfaces (interface if_0) (interface if_1)))) (linklist (link link_0) (link link_1)) (netlist (host_0.if_0 link_0 router_0.if_0) (host_1.if_0 link_1 router_0.if_1)) (exportlist (host_0.ext0) (host_1.ext1) )) (class Line (netprops (IPsec_authentication none) (IPsec_encryption none) (dns yes) (dynamic_routing no) (addresstype ipv4)) (nodelist (node a: star (nodeprops )) (node b (nodeprops ) (interfaces (interface left))) (node d (nodeprops ) (interfaces (interface if))) ) (linklist (link link0) (link link1)) (netlist (a.ext0 link0 d.if) (a.ext1 link1 b.left) ) (exportlist )) (root Line foobar) )))\n"; my ($cmd, $name, $topo, $topology, $host, $router, $msg_ref, $api_cmd); my ($peer, $sock, $ldap); # == Main ===================================================================== my $noargs = $#ARGV; if(($#ARGV +1)){ $peer = shift @ARGV; }else{ chomp($peer = `hostname`); } print "--> Overlay Manager = $peer\n"; print " CAcert: $client_params{ca_cert}\n"; print " CApath: $client_params{ca_path}\n"; print " Cert: $client_params{node_cert}\n"; print " Key: $client_params{node_key}\n"; $IO::Socket::SSL::DEBUG = 0; foreach my $name (keys %client_params){ unless (-e $client_params{$name}){ print "File not found! Please check \$XB_Params::$name in ". "/usr/local/xbone/programs/XB_Params.pm\n"; exit 1; }; } while(1){ # main menu print "--> Command: [C=Create D=Delete L=List S=Status F=Discover Q=Quit] "; chomp ($cmd = <>); # generate corresponding API command message: if($cmd =~ /c/i){ print " + [Create] Enter overlay name: "; chomp ($name = <>); $cmd_args{overlay_name} = $name. ".$XB_Params::XBONE_NET"; # obtain LDAP while ((not defined $ldap) or ($ldap !~ /(yes|no)/)) { print " + [Create] Use LDAP?: "; chomp ($ldap = <>); $cmd_args{ldap} = $ldap; $cmd_args{attrvals} = ""; $cmd_args{scope} = "local"; } # print " + [Create] Enter overlay topology [s=star; l=linear; r=ring; c=custom\n"; print " m=recursive; i=imported (not yet supported)]: "; chomp ($topo = <>); if($topo =~ /s/i){ print " + [Create] Star - how many hosts: "; chomp($host = <>); unless($host =~ /\d+/){ print " ! [Create] $host is not a number!\n"; next; } $cmd_args{topology} = "star"; $cmd_args{hosts} = $host; $cmd_args{routers} = 1; if($use_xml){ $msg_ref = XB_XML_GUI::XB_build_create_overlay_msg (\%cmd_args); }else{ $msg_ref = XB_API_GUI::XB_build_create_overlay_msg (\%cmd_args); } print "$$msg_ref\n"; }elsif($topo =~ /(l|r)/i){ $cmd_args{topology} = ($1 =~ /l/i)? "linear":"ring"; print " + [Create] Linear - how many routers: "; chomp($router = <>); unless($router =~ /\d+/){ print " ! [Create] $router is not a number!\n"; next; } print " + [Create] Linear - how many hosts: "; chomp($host = <>); unless($host =~ /\d+/){ print " ! [Create] $host is not a number!\n"; next; } $cmd_args{hosts} = $host; $cmd_args{routers} = $router; if($use_xml){ $msg_ref = XB_XML_GUI::XB_build_create_overlay_msg (\%cmd_args); }else{ $msg_ref = XB_API_GUI::XB_build_create_overlay_msg (\%cmd_args); } print "$$msg_ref\n"; } elsif($topo =~ /c/i){ print " + [Create] Custom - (linear) how many hosts: "; chomp($host = <>); unless($host =~ /\d+/){ print " ! [Create] $host is not a number!\n"; next; } $cmd_args{topology} = "custom"; $cmd_args{hosts} = 0; $cmd_args{routers} = $host; $cmd_args{custom_netlist} = ""; foreach my $i (1..($host-1)){ my $next; if ($i == $host){ $next = 1; } else { $next = $i+1; } $cmd_args{custom_netlist} .= "test$i test$next\n"; } if($use_xml){ $msg_ref = XB_XML_GUI::XB_build_create_overlay_msg (\%cmd_args); }else{ $msg_ref = XB_API_GUI::XB_build_create_overlay_msg (\%cmd_args); } print "$$msg_ref\n"; }elsif($topo =~ /m/i){ print " + [Create] Recursive - choose from the following:\n"; print " 0: 3 nodes (1 meta node w/2, 1 simple node)\n"; print " 1: 4 nodes (2 meta nodes w/2 each)\n"; print " 2: 6 nodes (2 meta nodes w/3 each)\n"; print " 3: 7 nodes (1 meta node w/3)\n"; print " 4: 6 nodes (1 meta node w/3): "; chomp($router = <>); $_ = $router; my $realmsg; SWITCH: { /0/ && do { $realmsg = $r0; last SWITCH; }; /1/ && do { $realmsg = $r1; last SWITCH; }; /2/ && do { $realmsg = $r2; last SWTICH; }; /3/ && do { $realmsg = $r3; last SWITCH; }; /4/ && do { $realmsg = $r4; last SWITCH; }; print " ! [Create] $router invalid; only 0, 1, 2, 3\n"; next; } $realmsg =~ s/foobar/$name\.xbone\.net/g; $msg_ref = \$realmsg; print "$$msg_ref\n"; }elsif($topo =~ /i/i){ print " + [Create] Import from file: NOT YET!\n"; next; }else{ print " + [Create] Unknown topology: $topo\n"; next; } }elsif($cmd =~ /d/i){ print " - [Delete] Enter name of the overlay: "; chomp ($name = <>); $cmd_args{overlay_name} = $name. ".$XB_Params::XBONE_NET"; $msg_ref = XB_XML_GUI::XB_build_destroy_overlay_msg (\%cmd_args); print "$$msg_ref\n"; }elsif($cmd =~ /l/i){ print " - [List] List all active overlays:\n"; $msg_ref = XB_XML_GUI::XB_build_list_overlays_msg (\%cmd_args); print "$$msg_ref\n"; }elsif($cmd =~ /f/i){ print " - [Discover] Discover all active nodes:\n"; # obtain LDAP while ((not defined $ldap) or ($ldap !~ /(yes|no)/)) { print " + [Create] Use LDAP?: "; chomp ($ldap = <>); $cmd_args{ldap} = $ldap; $cmd_args{attrvals} = ""; $cmd_args{scope} = "local"; } # construct the xml message to be sent to the backend $msg_ref = XB_XML_GUI::XB_build_discover_daemons_msg (\%cmd_args); print "$$msg_ref\n"; }elsif($cmd =~ /s/i){ print " - [Status] Show the node/link status of an overlay; Name: "; chomp ($name = <>); $cmd_args{overlay_name} = $name. ".$XB_Params::XBONE_NET"; $msg_ref = XB_XML_GUI::XB_build_overlay_status_msg(\%cmd_args); print "$$msg_ref\n"; }elsif($cmd =~ /q/i){ print " o Quit!\n"; last; }elsif($cmd =~ /e/i){ $msg_ref = \" (xbone 1.5 2.5 (credential (user_name 'Yu-Shun Wang') (user_email yushunwa\@isi.edu) (auth_type X509)) (error_reply (function xb_test) (reason haha) (more hahaha))) "; }else{ print " ! Invalid input: $cmd; try again.\n"; next; } # parse the commond if($use_xml){ my $result = XB_XML_scan::XB_XML_parse ($msg_ref); if ($result){ $_ = XB_XML_scan::XB_XML_choose_parse_error ($result); print STDERR "FAILURE!!\n\nCHOSEN ERROR STRING: $_\n\n"; exit (1); }; my $obj_hash = XB_XML_scan::XB_XML_hash ($msg_ref); my $cmdhref = $obj_hash->{'command'}; my $covlhref = $cmdhref->{'create_overlay'}; my $xolhref = $covlhref->{'xol_program'}; XB_XML_scan::XB_XOL_xbone_list_sub ($obj_hash); XB_XML_scan::XB_XOL_synonym_sub ($obj_hash, $xolhref->{'equivto'}); print "XML hash: ", Dumper($obj_hash); }else{ my $api_cmd = $api_parser->API($$msg_ref); unless(defined $api_cmd){ print " ! API Parser failed!\n"; exit(1); }else{ print " o API command parsed ok.\n"; #print " - dump ", Dumper($api_cmd), "====\n"; } } # create TCP/SSL socket & connect to server # try v4 and if that doesnt work, try v6 my $errmsg = ""; eval { $sock = IO::Socket::SSL->new( PeerAddr => $peer, PeerPort => $XB_Params::xbone_api_port, Proto => 'tcp', Reuse => 1, SSL_use_cert => 1, SSL_verify_mode => 0x01, SSL_key_file => "$client_params{node_key}", SSL_cert_file => "$client_params{node_cert}", SSL_ca_file => "$client_params{ca_cert}", SSL_ca_path => "$client_params{ca_path}" ); }; if ($@) { $errmsg = $@; } if (not defined $sock){ eval { # try v6 my ($family, $socktype, $proto, $saddr, $canonname); my @res = getaddrinfo($peer, 'daytime', AF_INET6); while (scalar(@res) >= 5) { # the v6 lookup can return multiple ip addresses some of # which may not work. So, try all of them. $family = -1; # for safety ($family, $socktype, $proto, $saddr, $canonname, @res) = @res; my ($dest, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV); $sock = IO::Socket::SSLv6->new( PeerAddr => $dest, PeerPort => $XB_Params::xbone_api_port, Proto => 'tcp', Reuse => 1, SSL_use_cert => 1, SSL_verify_mode => 0x03, SSL_verify_mode => 0x01, SSL_key_file => "$client_params{node_key}", SSL_cert_file => "$client_params{node_cert}", SSL_ca_file => "$client_params{ca_cert}", SSL_ca_path => "$client_params{ca_path}" ); last if $sock; } # foreach destination ip address }; # eval $errmsg .= $@; }; if (!$sock) { print STDERR " ! create socket failed: $errmsg $!\n"; exit(0); } else { print STDERR "connect ($sock).\n" if ($IO::Socket::SSL::DEBUG); } # send API command print $sock "$$msg_ref XBoneEOC\n"; # get response my $sel = IO::Select->new($sock); my $reply; my @ready; while (@ready = $sel->can_read){ for my $fh (@ready){ if($fh != $sock){ print " ! Unkown socket!\n"; }else{ while (my $line = <$sock>){ if($line =~ /\b$XB_Params::msg_delimiter\b/){ last; } #print "....$line"; $reply .= $line; } $sel->remove($fh); last; } } } # close TCP/SSL socket $sock->close or die "close failed: $!"; # print results print " o Received reply: $reply\n"; $reply =~ s/$reply/$XB_Params::msg_delimiter/g; my $parser_err = XB_XML_scan::XB_XML_parse(\$reply); if ($parser_err){ $_ = XB_XML_scan::XB_XML_choose_parse_error ($parser_err); print "!!! parser error: $_"; } #-- parse the message into hash my $api_cmd = XB_XML_scan::XB_XML_hash (\$reply); XB_XML_scan::XB_XOL_xbone_list_sub ($api_cmd); my $xolhref = $api_cmd->{command}{create_overlay}{xol_program}; XB_XML_scan::XB_XOL_synonym_sub ($api_cmd, $xolhref->{'equivto'}); print "==> API Command Reply: ". Dumper($api_cmd); } 1;