git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@10316 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
		
			
				
	
	
		
			1032 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			1032 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| #!/usr/bin/env perl
 | |
| # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
 | |
| package xCAT::Client;
 | |
| BEGIN
 | |
| {
 | |
|   $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr';
 | |
| }
 | |
| 
 | |
| # if AIX - make sure we include perl 5.8.2 in INC path.
 | |
| #       Needed to find perl dependencies shipped in deps tarball.
 | |
| if ($^O =~ /^aix/i) {
 | |
|         use lib "/usr/opt/perl5/lib/5.8.2/aix-thread-multi";
 | |
|         use lib "/usr/opt/perl5/lib/5.8.2";
 | |
|         use lib "/usr/opt/perl5/lib/site_perl/5.8.2/aix-thread-multi";
 | |
|         use lib "/usr/opt/perl5/lib/site_perl/5.8.2";
 | |
| }
 | |
| 
 | |
| my $inet6support;
 | |
| if ($^O =~ /^aix/i) {  # disable AIX IPV6  TODO fix
 | |
|  $inet6support = 0;
 | |
| } else {
 | |
|   $inet6support=eval { require Socket6; 1; };
 | |
| }
 | |
| if ($inet6support) {
 | |
|    $inet6support = eval { require IO::Socket::INET6; 1; };
 | |
| }
 | |
| if ($inet6support) {
 | |
|    $inet6support = eval { require IO::Socket::SSL; IO::Socket::SSL->import('inet6'); 1;};
 | |
| }
 | |
| unless ($inet6support) {
 | |
|   eval { require Socket };
 | |
|   eval { require IO::Socket::INET };
 | |
|   eval { require IO::Socket::SSL; IO::Socket::SSL->import('inet4') };
 | |
| }
 | |
| 
 | |
| 
 | |
| use XML::Simple; #smaller than libxml....
 | |
| $XML::Simple::PREFERRED_PARSER='XML::Parser';
 | |
| #require Data::Dumper;
 | |
| my $xcathost='localhost:3001';
 | |
| my $plugins_dir;
 | |
| my %resps;
 | |
| my $EXITCODE;     # save the bitmask of all exit codes returned by calls to handle_response()
 | |
| 1;
 | |
| 
 | |
| 
 | |
| #################################
 | |
| # submit_request will take an xCAT command and pass it to the xCAT
 | |
| #   server for execution.
 | |
| #  Note must not put a require or use for Utils.pm in the non-bypass path
 | |
| #
 | |
| # If the XCATBYPASS env var is set, the connection to the server/daemon
 | |
| #   will be bypassed and the plugin will be called directly.  If it is
 | |
| #   set to one or more directories (separated by ":"), all perl modules
 | |
| #   in those directories will be loaded in as plugins (for duplicate
 | |
| #   commands, last one in wins). If it is set to any other value
 | |
| #   (e.g. "yes", "default", whatever string you want) the default plugin
 | |
| #   directory /opt/xcat/lib/perl/xCAT_plugin will be used.
 | |
| #
 | |
| # Input:
 | |
| #    Request hash - A hash ref containing the input command and args to be
 | |
| #       passed to the plugin.  The the xcatd daemon (or this routine when
 | |
| #       XCATBYPASS) reads the {noderange} entry and builds a flattened array
 | |
| #       of nodes that gets added as request->{node}
 | |
| #       The format for the request hash is:
 | |
| #          { command => [ 'xcatcmd' ],
 | |
| #            noderange => [ 'noderange_string' ],
 | |
| #            arg => [ 'arg1', 'arg2', '...', 'argn' ]
 | |
| #          }
 | |
| #    Callback - A subroutine ref that will be called to process the output
 | |
| #       from the plugin.
 | |
| #
 | |
| # NOTE:  The request hash will get converted to XML when passed to the
 | |
| #        xcatd daemon, and will get converted back to a hash before being
 | |
| #        passed to the plugin.  The XMLin ForceArray option is used to
 | |
| #        force all XML constructs to be arrays so that the plugin code
 | |
| #        and callback routines can access the data consistently.
 | |
| #        The input request and the response hash created by the plugin should
 | |
| #        always create hashes with array values.
 | |
| #################################
 | |
| sub submit_request {
 | |
|   my $request = shift;
 | |
|   my $callback = shift;
 | |
|   my $keyfile = shift;
 | |
|   my $certfile = shift;
 | |
|   my $cafile = shift;
 | |
|   # get home directory
 | |
|   my  @user = getpwuid($>);
 | |
|   my $homedir=$user[7];
 | |
|   unless ($keyfile) { $keyfile = $homedir."/.xcat/client-cred.pem"; }
 | |
|   unless ($certfile) { $certfile = $homedir."/.xcat/client-cred.pem"; }
 | |
|   unless ($cafile) { $cafile  = $homedir."/.xcat/ca.pem"; }
 | |
|   $xCAT::Client::EXITCODE = 0;    # clear out exit code before invoking the plugin
 | |
| $request->{clienttype}->[0] = "cli";   # setup clienttype for auditlog
 | |
| # If XCATBYPASS is set, invoke the plugin process_request method directly
 | |
| # without going through the socket connection to the xcatd daemon
 | |
|   if ($ENV{XCATBYPASS}) {
 | |
|      #add current userid to the request
 | |
|      if (!(defined($request->{username}))) {
 | |
|        $request->{username}->[0] = getpwuid($>);
 | |
|      }
 | |
|    # Load plugins from either specified or default dir
 | |
|     require xCAT::Table;
 | |
|     my %cmd_handlers;
 | |
|     my @plugins_dirs = split('\:',$ENV{XCATBYPASS});
 | |
|     if (-d $plugins_dirs[0]) {
 | |
|        foreach (@plugins_dirs) {
 | |
|           $plugins_dir = $_;
 | |
|           scan_plugins();
 | |
|        }
 | |
|     } else {
 | |
|        # figure out default plugins dir
 | |
|        my $sitetab=xCAT::Table->new('site');
 | |
|        unless ($sitetab) {
 | |
|          print ("ERROR: Unable to open basic site table for configuration\n");
 | |
|        }
 | |
|        $plugins_dir=$::XCATROOT.'/lib/perl/xCAT_plugin';
 | |
|        scan_plugins();
 | |
|     }
 | |
| 
 | |
|   #  don't do XML transformation -- assume request is well-formed
 | |
|   #  my $xmlreq=XMLout($request,RootName=>xcatrequest,NoAttr=>1,KeyAttr=>[]);
 | |
|   #  $request = XMLin($xmlreq,SuppressEmpty=>undef,ForceArray=>1) ;
 | |
| 
 | |
| 
 | |
|     # Call the plugin directly
 | |
|  #   ${"xCAT_plugin::".$modname."::"}{process_request}->($request,$callback);
 | |
|     plugin_command($request,undef,$callback);
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
| # No XCATBYPASS, so establish a socket connection with the xcatd daemon
 | |
| # and submit the request
 | |
|   if ($ENV{XCATHOST}) {
 | |
|     $xcathost=$ENV{XCATHOST};
 | |
|   }
 | |
|   my $client;
 | |
|   if (-r $keyfile and -r $certfile and -r $cafile) {
 | |
|      $client = IO::Socket::SSL->new(
 | |
|     PeerAddr => $xcathost,
 | |
|     SSL_key_file => $keyfile,
 | |
|     SSL_cert_file => $certfile,
 | |
|     SSL_ca_file => $cafile,
 | |
|     SSL_use_cert => 1,
 | |
|     Timeout => 15,
 | |
|     );
 | |
|   } else {
 | |
|      $client = IO::Socket::SSL->new(
 | |
|       PeerAddr => $xcathost,
 | |
|       Timeout => 15,
 | |
|      );
 | |
|    }
 | |
|   unless ($client) {
 | |
|      print "Unable to open socket connection to xcatd daemon on $xcathost.\n";
 | |
|      print "Verify that the xcatd daemon is running and that your SSL setup is correct.\n";
 | |
|      if ($@ =~ /SSL Timeout/) {
 | |
|         die "Connection failure: SSL Timeout or incorrect certificates in ~/.xcat";
 | |
|      } else {
 | |
|         die "Connection failure: $@"
 | |
|      }
 | |
|   }
 | |
|   my $msg=XMLout($request,RootName=>'xcatrequest',NoAttr=>1,KeyAttr=>[]);
 | |
|   if ($ENV{XCATXMLTRACE}) { print $msg; }
 | |
|   if($ENV{XCATXMLWARNING}) {
 | |
|     validateXML($msg);
 | |
|   }
 | |
|   $SIG{TERM} =  $SIG{INT} = sub { print $client XMLout({abortcommand=>1},RootName=>'xcatrequest',NoAttr=>1,KeyAttr=>[]); exit 0; };
 | |
|   print $client $msg;
 | |
|   my $response;
 | |
|   my $rsp;
 | |
|   my $cleanexit=0;
 | |
|   while (<$client>) {
 | |
|     $response .= $_;
 | |
|     if (m/<\/xcatresponse>/) {
 | |
|       #replace ESC with xxxxESCxxx because XMLin cannot handle it
 | |
|       $response =~ s/\e/xxxxESCxxxx/g;
 | |
| 
 | |
|       if ($ENV{XCATXMLTRACE}) { print $response; }
 | |
|       $rsp = XMLin($response,SuppressEmpty=>undef,ForceArray=>1);
 | |
|       if($ENV{XCATXMLWARNING}) {
 | |
|         validateXML($response);
 | |
|       }
 | |
| 
 | |
|       #add ESC back
 | |
|       foreach my $key (keys %$rsp) {
 | |
| 	  if (ref($rsp->{$key}) eq 'ARRAY') { 
 | |
|               foreach my $text (@{$rsp->{$key}}) {
 | |
|                   next unless defined $text;
 | |
|                   $text =~ s/xxxxESCxxxx/\e/g;
 | |
|               }
 | |
|           }
 | |
| 	  else {
 | |
|               $rsp->{$key} =~ s/xxxxESCxxxx/\e/g;
 | |
|           }
 | |
|       }
 | |
| 	  
 | |
|       $response='';
 | |
|       $callback->($rsp);
 | |
|       if ($rsp->{serverdone}) {
 | |
|          $cleanexit=1;
 | |
|         last;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   unless ($cleanexit) {
 | |
|      print STDERR "ERROR/WARNING: communication with the xCAT server seems to have been ended prematurely\n";
 | |
|   }
 | |
| 
 | |
| sub validateXML {
 | |
|   my $xml = shift;
 | |
|   my @lines = split /\n/, $xml;
 | |
|   my $invalidNewline = 0;
 | |
|   my $contentsColon = 0;
 | |
|   my $contentsLine;
 | |
| 
 | |
|   foreach (@lines) {
 | |
|     if(!$invalidNewline) {
 | |
|       if( ($_ =~ /<contents>/ && $_ !~ /<\/contents>/) ||
 | |
|           ($_ =~ /<desc>/ && $_ !~ /<\/desc>/)) {
 | |
|         $invalidNewline = 1;
 | |
|         print "Possible invalid XML using newlines found:  \n$xml\n";
 | |
|       }
 | |
|     }
 | |
|     if($_ =~ /<contents>.+:.+<\/contents>/) {
 | |
|       $contentsColon = 1;
 | |
|       $contentsLine = $_;
 | |
|     }
 | |
|     if($_ =~ /<desc>.+<\/desc>/) {
 | |
|       $contentsColon = 0;
 | |
|     }
 | |
|     if($contentsColon && $_ =~ /<desc><\/desc>/) {
 | |
|       print "Possible invalid XML found(data contents using colon and blank description):  \n$contentsLine\n$_\n";
 | |
|       $contentsColon = 0;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| ###################################
 | |
| # scan_plugins
 | |
| #    will load all plugin perl modules and build a list of supported
 | |
| #    commands
 | |
| #
 | |
| # NOTE:  This is copied from xcatd (last merge 11/23/09).
 | |
| # TODO:  Will eventually move to using common source....
 | |
| ###################################
 | |
| sub scan_plugins {
 | |
|   my @plugins=glob($plugins_dir."/*.pm");
 | |
|   foreach (@plugins) {
 | |
|     /.*\/([^\/]*).pm$/;
 | |
|     my $modname = $1;
 | |
|     unless ( eval { require "$_" }) {
 | |
| #       xCAT::MsgUtils->message("S","Error loading module ".$_."  ...skipping");
 | |
|         print "Error loading module $_  ...skipping\n"; 
 | |
|         next;
 | |
|     }
 | |
|     no strict 'refs';
 | |
|     my $cmd_adds=${"xCAT_plugin::".$modname."::"}{handled_commands}->();
 | |
|     foreach (keys %$cmd_adds) {
 | |
|       my $value = $_;
 | |
|       if (defined($cmd_handlers{$_})) {
 | |
|         push @{$cmd_handlers{$_}},[$modname,$cmd_adds->{$_}]; 
 | |
|       } else {
 | |
|         $cmd_handlers{$_} = [ [$modname,$cmd_adds->{$_}] ];
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   foreach (@plugins) {
 | |
|     no strict 'refs';
 | |
|     /.*\/([^\/]*).pm$/;
 | |
|     my $modname = $1;
 | |
|     unless (defined(${"xCAT_plugin::".$modname."::"}{init_plugin})) {
 | |
|         next;
 | |
|     }
 | |
|     ${"xCAT_plugin::".$modname."::"}{init_plugin}->(\&do_request);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| ###################################
 | |
| # plugin_command
 | |
| #    will invoke the correct plugin
 | |
| #
 | |
| # NOTE:  This is copied from xcatd (last merge 11/23/09).
 | |
| # TODO:  Will eventually move to using common source....
 | |
| ###################################
 | |
| sub plugin_command {
 | |
|   my $req = shift;
 | |
|   my $sock = shift;
 | |
|   my $callback = shift;
 | |
|   my %handler_hash;
 | |
|   my $usesiteglobal = 0;
 | |
| 
 | |
|   # We require these only in bypass mode to reduce start up time for the normal case
 | |
|   #use lib "$::XCATROOT/lib/perl";
 | |
|   #use xCAT::NodeRange;
 | |
|   require lib;
 | |
|   lib->import("$::XCATROOT/lib/perl");
 | |
|   require xCAT::NodeRange;
 | |
|   require xCAT::Table;
 | |
| 
 | |
|   $Main::resps={};
 | |
|   my @nodes;
 | |
|   if ($req->{node}) {
 | |
|     @nodes = @{$req->{node}};
 | |
|   } elsif ($req->{noderange} and $req->{noderange}->[0]) {
 | |
|     @nodes = xCAT::NodeRange::noderange($req->{noderange}->[0]);
 | |
|     if (xCAT::NodeRange::nodesmissed()) {
 | |
| #     my $rsp = {errorcode=>1,error=>"Invalid nodes in noderange:".join(',',xCAT::NodeRange::nodesmissed)};
 | |
| #     my $rsp->{serverdone} = {};
 | |
|       print "Invalid nodes in noderange:".join(',',xCAT::NodeRange::nodesmissed())."\n";
 | |
| #     if ($sock) {
 | |
| #       print $sock XMLout($rsp,RootName=>'xcatresponse' ,NoAttr=>1);
 | |
| #     }
 | |
| #     return ($rsp);
 | |
|       return 1;
 | |
|     }
 | |
|     unless (@nodes) {
 | |
|        $req->{emptynoderange} = [1];
 | |
|     }
 | |
| 
 | |
|     if(@nodes == 0 ) {
 | |
|         print "No nodes or noderanges specified\n";
 | |
|         return 1;
 | |
|      }
 | |
|   }
 | |
|   if (@nodes) { $req->{node} = \@nodes; }
 | |
|   my %unhandled_nodes;
 | |
|   foreach (@nodes) {
 | |
|       $unhandled_nodes{$_}=1;
 | |
|   }
 | |
|   my $useunhandled=0;
 | |
|   if (defined($cmd_handlers{$req->{command}->[0]})) {
 | |
|     my $hdlspec;
 | |
|     my @globalhandlers=();
 | |
|     my $useglobals=1; #If it stays 1, then use globals normally, if 0, use only for 'unhandled_nodes, if -1, don't do at all
 | |
|     foreach (@{$cmd_handlers{$req->{command}->[0]}}) {
 | |
|       $hdlspec =$_->[1];
 | |
|       my $ownmod = $_->[0];
 | |
|       if ($hdlspec =~ /^site:/) { #A site entry specifies a plugin
 | |
|           my $sitekey = $hdlspec;
 | |
|           $sitekey =~ s/^site://;
 | |
|           $sitetab = xCAT::Table->new('site');
 | |
|           my $sent = $sitetab->getAttribs({key=>$sitekey},['value']);
 | |
|           if ($sent and $sent->{value}) { #A site style plugin specification is just like
 | |
|                                           #a static global, it grabs all nodes rather than some
 | |
|             $useglobals = -1; #If they tried to specify anything, don't use the default global handlers at all
 | |
|             unless (@nodes) {
 | |
|               $handler_hash{$sent->{value}} = 1;
 | |
|               $usesiteglobal = 1;
 | |
|             }
 | |
|             foreach (@nodes) { #Specified a specific plugin, not a table lookup
 | |
|               $handler_hash{$sent->{value}}->{$_} = 1;
 | |
|             }
 | |
|           }
 | |
|       } elsif ($hdlspec =~ /:/) { #Specificed a table lookup path for plugin name
 | |
|         if (@nodes) { # only use table lookup plugin if nodelist exists
 | |
|                       # Usage will be handled in common AAAhelp plugin
 | |
| 
 | |
|           $useglobals = 0; #Only contemplate nodes that aren't caught through searching below in the global handler
 | |
|           $useunhandled=1;
 | |
|           my $table;
 | |
|           my $cols;
 | |
|           ($table,$cols) = split(/:/,$hdlspec);
 | |
|           my @colmns=split(/,/,$cols);
 | |
|           my @columns;
 | |
|           my $hdlrtable=xCAT::Table->new($table);
 | |
|           unless ($hdlrtable) {
 | |
|             #TODO: proper error handling
 | |
|           }
 | |
|           my $node;
 | |
|           my $colvals = {};
 | |
|           foreach my $colu (@colmns) {
 | |
|             if ($colu =~ /=/) { #a value redirect to a pattern/specific name
 | |
|               my $coln; my $colv;
 | |
|               ($coln,$colv) = split(/=/,$colu,2);
 | |
|               $colvals->{$coln} = $colv;
 | |
|               push (@columns,$coln);
 | |
|             } else {
 | |
|               push (@columns,$colu);
 | |
|             }
 | |
|           }
 | |
| 
 | |
| 
 | |
|           unless (@nodes) { #register the plugin in the event of usage
 | |
|             $handler_hash{$ownmod} = 1;
 | |
|             $useglobals = 1;
 | |
|           }
 | |
|           my $hdlrcache;
 | |
|           if ($hdlrtable) {
 | |
|                   $hdlrcache = $hdlrtable->getNodesAttribs(\@nodes,\@columns);
 | |
|           }
 | |
|           foreach $node (@nodes) {
 | |
|             unless ($hdlrcache) { next; }
 | |
|             my $attribs = $hdlrcache->{$node}->[0]; #$hdlrtable->getNodeAttribs($node,\@columns);
 | |
|             unless (defined($attribs)) { next; }
 | |
|             foreach (@columns) {
 | |
|               my $col=$_;
 | |
|               if (defined($attribs->{$col})) {
 | |
|                 if ($colvals->{$col}) { #A pattern match style request.
 | |
|                   if ($attribs->{$col} =~ /$colvals->{$col}/) {
 | |
|                     $handler_hash{$ownmod}->{$node} = 1;
 | |
|                     delete $unhandled_nodes{$node};
 | |
|                     last;
 | |
|                   }
 | |
|                 } else {
 | |
|                   # call the plugin that matches the table value for that node
 | |
|                   if ($attribs->{$col} =~ /$ownmod/) {
 | |
|                     $handler_hash{$attribs->{$col}}->{$node} = 1;
 | |
|                     delete $unhandled_nodes{$node};
 | |
|                     last;
 | |
|                   }
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|           $hdlrtable->close;
 | |
|         }  # end if (@nodes)
 | |
| 
 | |
|       } else {
 | |
|           push @globalhandlers,$hdlspec;
 | |
|       }
 | |
|     }
 | |
|       if ($useglobals == 1) {  #Behavior when globals have not been overriden
 | |
|           my $hdlspec;
 | |
|           foreach $hdlspec (@globalhandlers) {
 | |
|             unless (@nodes) {
 | |
|               $handler_hash{$hdlspec} = 1;
 | |
|             }
 | |
|             foreach (@nodes) { #Specified a specific plugin, not a table lookup
 | |
|               $handler_hash{$hdlspec}->{$_} = 1;
 | |
|             }
 | |
|           }
 | |
|       } elsif ($useglobals == 0) {
 | |
|           unless (@nodes or $usesiteglobal) { #if something like 'makedhcp -n',
 | |
|               foreach (keys %handler_hash) {
 | |
|                   if ($handler_hash{$_} == 1) {
 | |
|                       delete ($handler_hash{$_})
 | |
|                   }
 | |
|               }
 | |
|           }
 | |
|           foreach $hdlspec (@globalhandlers) {
 | |
|             unless (@nodes or $usesiteglobal) {
 | |
|               $handler_hash{$hdlspec} = 1;
 | |
|             }
 | |
|             foreach (keys %unhandled_nodes) { #Specified a specific plugin, not a table lookup
 | |
|               $handler_hash{$hdlspec}->{$_} = 1;
 | |
|             }
 | |
|           }
 | |
|       } #Otherwise, global handler is implicitly disabled
 | |
|   } else {
 | |
|     return 1;  #TODO: error back that request has no known plugin for it
 | |
|   }
 | |
|   if ($useunhandled) {
 | |
|    my $queuelist;
 | |
|    foreach (@{$cmd_handlers{$req->{command}->[0]}}) {
 | |
|        my $queueitem = $_->[1];
 | |
|        if (($queueitem =~ /:/) and !($queuelist =~ /($queueitem)/)) {
 | |
|          $queuelist .= "$_->[1];";
 | |
|        } 
 | |
|    }
 | |
|    $queuelist =~ s/;$//;
 | |
|    $queuelist =~ s/:/./g;
 | |
|    foreach (keys %unhandled_nodes) {
 | |
| #      if ($sock) {
 | |
| #         print $sock XMLout({node=>[{name=>[$_],data=>["Unable to identify plugin for this command, check relevant tables: $queuelist"],errorcode=>[1]}]},NoAttr=>1,RootName=>'xcatresponse');
 | |
| #      } else {
 | |
|          my $tabdesc = $queuelist;
 | |
|          $tabdesc =~ s/=.*$//;
 | |
|          $callback->({node=>[{name=>[$_],error=>['Unable to identify plugin for this command, check relevant tables: '.$tabdesc],errorcode=>[1]}]});
 | |
| #      }
 | |
|      }
 | |
|   }
 | |
| 
 | |
| ## FOR NOW, DON'T FORK CHILD PROCESS TO MAKE BYPASS SIMPLER AND EASIER TO DEBUG
 | |
| #  $plugin_numchildren=0;
 | |
| #  %plugin_children=();
 | |
| #  $SIG{CHLD} = \&plugin_reaper; #sub {my $plugpid; while (($plugpid = waitpid(-1, WNOHANG)) > 0) { if ($plugin_children{$plugpid}) { delete $plugin_children{$plugpid}; $plugin_numchildren--; } } };
 | |
| # my $check_fds;
 | |
| # if ($sock) {
 | |
| #   $check_fds = new IO::Select;
 | |
| # }
 | |
|   foreach (keys %handler_hash) {
 | |
|     my $modname = $_;
 | |
| #   my $shouldbealivepid=$$;
 | |
|     if (-r $plugins_dir."/".$modname.".pm") {
 | |
|       require $plugins_dir."/".$modname.".pm";
 | |
| #     $plugin_numchildren++;
 | |
| #     my $pfd; #will be referenced for inter-process messaging.
 | |
| #     my $parfd; #not causing a problem that I discern yet, but theoretically
 | |
| #     my $child;
 | |
| #     if ($sock) { #If $sock not passed in, don't fork..
 | |
| #       socketpair($pfd, $parfd,AF_UNIX,SOCK_STREAM,PF_UNSPEC) or die "socketpair: $!";
 | |
| #       #pipe($pfd,$cfd);
 | |
| #       $parfd->autoflush(1);
 | |
| #       $pfd->autoflush(1);
 | |
| #       $child = xCAT::Utils->xfork;
 | |
| #     } else {
 | |
| #       $child = 0;
 | |
| #     }
 | |
| #     unless (defined $child) { die "Fork failed"; }
 | |
| #     if ($child == 0) {
 | |
| #       if ($parfd) {  #If xCAT is doing multiple requests in same communication PID, things would get unfortunate otherwise
 | |
| #           $parent_fd = $parfd;
 | |
| #       }
 | |
|         my $oldprogname=$$progname;
 | |
|         $$progname=$oldprogname.": $modname instance";
 | |
| #       if ($sock) { close $pfd; }
 | |
|         unless ($handler_hash{$_} == 1) {
 | |
|           my @nodes = sort {($a =~ /(\d+)/)[0] <=> ($b =~ /(\d+)/)[0] || $a cmp $b } (keys %{$handler_hash{$_}});
 | |
|           $req->{node}=\@nodes;
 | |
|         }
 | |
|         no strict  "refs";
 | |
| #       eval { #REMOVEEVALFORDEBUG
 | |
| #       if ($dispatch_requests) {
 | |
|                 dispatch_request($req,$callback,$modname);
 | |
| #       } else {
 | |
| #          $SIG{CHLD}='DEFAULT';
 | |
| #          ${"xCAT_plugin::".$modname."::"}{process_request}->($req,$callback,\&do_request);
 | |
| #       }
 | |
|         $$progname=$oldprogname;
 | |
| #       if ($sock) {
 | |
| #         close($parent_fd);
 | |
| #         xexit(0);
 | |
| #       }
 | |
| #       }; #REMOVEEVALFORDEBUG
 | |
| #       if ($sock or $shouldbealivepid != $$) { #We shouldn't still be alive, try to send as much detail to parent as possible as to why
 | |
| #           my $error= "$modname plugin bug, pid $$, process description: '$$progname'";
 | |
| #           if ($@) {
 | |
| #               $error .= " with error '$@'";
 | |
| #           } else { #Sys::Virt and perhaps Net::SNMP sometimes crashes in a way $@ won't catch..
 | |
| #               $error .= " with missing eval error, probably due to special manipulation of $@ or strange circumstances in an XS library, remove evals in xcatd marked 'REMOVEEVALFORDEBUG and run xcatd -f for more info";
 | |
| #           }
 | |
| #           if (scalar (@nodes)) { #Don't know which of the nodes, so one error message warning about the possibliity..
 | |
| #               $error .= " while trying to fulfill request for the following nodes: ".join(",",@nodes);
 | |
| #           }
 | |
| #           xCAT::MsgUtils->message("S","xcatd: $error");
 | |
| #           $callback->({error=>[$error],errorcode=>[1]});
 | |
| #           xexit(0); #Die like we should have done
 | |
| #       } elsif ($@) { #We are still alive, should be alive, but yet we have an error.  This means we are in the case of 'do_request' or something similar.  Forward up the death since our communication channel is intact..
 | |
| #           die $@;
 | |
| #       }
 | |
| #     } else {
 | |
| #       $plugin_children{$child}=1;
 | |
| #       close $parfd;
 | |
| #       $check_fds->add($pfd);
 | |
| #     }
 | |
|     } else {
 | |
|       my $pm_name = $plugins_dir."/".$modname.".pm";
 | |
|       foreach my $node (keys %{$handler_hash{$_}}) {
 | |
|         if ($sock) {
 | |
|          print $sock XMLout({node=>[{name=>[$node],data=>["Cannot find the perl module to complete the operation: $pm_name"],errorcode=>[1]}]},NoAttr=>1,RootName=>'xcatresponse');
 | |
|         } else {
 | |
|          $callback->({node=>[{name=>[$node],data=>["Cannot find the perl module to complete the operation: $pm_name"],errorcode=>[1]}]});
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   unless ($sock) { return $Main::resps };
 | |
| # while (($plugin_numchildren > 0) and ($check_fds->count > 0)) { #this tracks end of useful data from children much more closely
 | |
| #   relay_fds($check_fds,$sock);
 | |
| # }
 | |
| # #while (relay_fds($check_fds,$sock)) {}
 | |
| # my %done;
 | |
| # $done{serverdone} = {};
 | |
| # if ($req->{transid}) {
 | |
| #   $done{transid}=$req->{transid}->[0];
 | |
| # }
 | |
| # if ($sock) {
 | |
| #     my $clientpresence = new IO::Select; #The client may have gone away without confirmation, don't PIPE over this trivial thing
 | |
| #     $clientpresence->add($sock);
 | |
| #     if ($clientpresence->can_write(5)) {
 | |
| #         print $sock XMLout(\%done,RootName => 'xcatresponse',NoAttr=>1);
 | |
| #     }
 | |
| # }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| ###################################
 | |
| # dispatch_request
 | |
| #    dispatch the requested command
 | |
| #
 | |
| # NOTE:  This is copied from xcatd (last merge 11/23/09).
 | |
| #        All we really need from this subroutine is to call preprocess_request
 | |
| #        and to only run the command for nodes handled by the local server
 | |
| #        Will eventually move to using common source....
 | |
| ###################################
 | |
| sub dispatch_request {
 | |
| #  %dispatched_children=();
 | |
|    require xCAT::Utils;
 | |
|    my $req = shift;
 | |
|    $dispatch_cb = shift;
 | |
| 
 | |
|    my $modname = shift;
 | |
|    my $reqs = [];
 | |
| #  my $child_fdset = new IO::Select;
 | |
|    no strict  "refs";
 | |
| 
 | |
|    #Hierarchy support.  Originally, the default scope for noderange commands was
 | |
|    #going to be the servicenode associated unless overriden.
 | |
|    #However, assume for example that you have blades and a blade is the service node
 | |
|    #rpower being executed by the servicenode for one of its subnodes would have to
 | |
|    #reach it's own management module.  This has the potential to be non-trivial for some quite possible network configurations.
 | |
|    #Since plugins may commonly experience this, a preprocess_request implementation
 | |
|    #will for now be required for a command to be scaled through service nodes
 | |
|    #If the plugin offers a preprocess method, use it to set the request array
 | |
|    if (defined(${"xCAT_plugin::".$modname."::"}{preprocess_request})) {
 | |
|    $SIG{CHLD}='DEFAULT';
 | |
|     $reqs = ${"xCAT_plugin::".$modname."::"}{preprocess_request}->($req,$dispatch_cb,\&do_request);
 | |
|    } else { #otherwise, pass it in without hierarchy support
 | |
|     $reqs = [$req];
 | |
|    }
 | |
| 
 | |
| # $dispatch_children=0;
 | |
| # $SIG{CHLD} = \&dispatch_reaper; #sub {my $cpid; while (($cpid =waitpid(-1, WNOHANG)) > 0) { if ($dispatched_children{$cpid}) { delete $dispatched_children{$cpid}; $dispatch_children--; } } };
 | |
|   my $onlyone=0;
 | |
|   if (defined $reqs and (scalar(@{$reqs}) == 1)) {
 | |
|       $onlyone=1;
 | |
|   }
 | |
| 
 | |
|    foreach (@{$reqs}) {
 | |
| #   my $pfd;
 | |
| #   my $parfd; #use a private variable so it won't trounce itself recursively
 | |
| #   my $child;
 | |
|     delete $_->{noderange};
 | |
| #----- added to Client.pm -----#
 | |
|     if ($_->{node}) {
 | |
|        $_->{noderange}->[0]=join(',',@{$_->{node}});
 | |
|     }
 | |
| #----- end added to Client.pm -----#
 | |
| 
 | |
|     if (ref $_->{'_xcatdest'} and (ref $_->{'_xcatdest'}) eq 'ARRAY') {
 | |
|         _->{'_xcatdest'} =  $_->{'_xcatdest'}->[0];
 | |
|     }
 | |
|     if ($onlyone and not ($_->{'_xcatdest'} and xCAT::Utils->thishostisnot($_->{'_xcatdest'}))) {
 | |
|        $SIG{CHLD}='DEFAULT';
 | |
|        ${"xCAT_plugin::".$modname."::"}{process_request}->($_,$dispatch_cb,\&do_request);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
| #   socketpair($pfd, $parfd,AF_UNIX,SOCK_STREAM,PF_UNSPEC) or die "socketpair: $!";
 | |
| #   $parfd->autoflush(1);
 | |
| #   $pfd->autoflush(1);
 | |
| #   $child = xCAT::Utils->xfork;
 | |
| #   if ($child) {
 | |
| #      $dispatch_children++;
 | |
| #      $dispatched_children{$child}=1;
 | |
| #      $child_fdset->add($pfd);
 | |
| #      next;
 | |
| #   }
 | |
| #   unless (defined $child) {
 | |
| #      $dispatch_cb->({error=>['Fork failure dispatching request'],errorcode=>[1]});
 | |
| #   }
 | |
| #   undef $SIG{CHLD};
 | |
| #     $dispatch_parentfd = $parfd;
 | |
|       my @prexcatdests=();
 | |
|       my @xcatdests=();
 | |
|      if (ref($_->{'_xcatdest'}) eq 'ARRAY') { #If array, consider it an 'anycast' operation, broadcast done through dupe
 | |
|                                               #requests, or an alternative join '&' maybe?
 | |
|          @prexcatdests=@{$_->{'_xcatdest'}};
 | |
|      } else {
 | |
|          @prexcatdests=($_->{'_xcatdest'});
 | |
|      }
 | |
|      foreach (@prexcatdests) {
 | |
|          if ($_ and /,/) {
 | |
|              push @xcatdests,split /,/,$_;
 | |
|          } else {
 | |
|              push @xcatdests,$_;
 | |
|          }
 | |
|      }
 | |
|      my $xcatdest;
 | |
|      my $numdests=scalar(@xcatdests);
 | |
|      my $request_satisfied=0;
 | |
|      foreach $xcatdest (@xcatdests) {
 | |
|         my $dlock;
 | |
|         if ($xcatdest and xCAT::Utils->thishostisnot($xcatdest)) {
 | |
| #----- added to Client.pm -----#
 | |
|        $dispatch_cb->({warning=>['XCATBYPASS is set, skipping hierarchy call to '.$_->{'_xcatdest'}.'']});
 | |
| #----- end added to Client.pm -----#
 | |
| 
 | |
| #           #mkpath("/var/lock/xcat/"); #For now, limit intra-xCAT requests to one at a time, to mitigate DB handle usage
 | |
| #           #open($dlock,">","/var/lock/xcat/dispatchto_$xcatdest");
 | |
| #           #flock($dlock,LOCK_EX);
 | |
| #           $ENV{XCATHOST} =  ($xcatdest =~ /:/ ? $xcatdest : $xcatdest.":3001" );
 | |
| #           $$progname.=": connection to ".$ENV{XCATHOST};
 | |
| #           my $errstr;
 | |
| #           eval {
 | |
| #           undef $_->{'_xcatdest'};
 | |
| #           xCAT::Client::submit_request($_,\&dispatch_callback,$xcatdir."/cert/server-cred.pem",$xcatdir."/cert/server-cred.pem",$xcatdir."/cert/ca.pem");
 | |
| #           };
 | |
| #           if ($@) {
 | |
| #            $errstr=$@;
 | |
| #           }
 | |
| #           #unlink("/var/lock/xcat/dispatchto_$xcatdest");
 | |
| #           #flock($dlock,LOCK_UN);
 | |
| #           if ($errstr) {
 | |
| #                   if ($numdests == 1) {
 | |
| #                   dispatch_callback({error=>["Unable to dispatch command to ".$ENV{XCATHOST}.", command will not make changes to that server ($errstr)"],errorcode=>[1]});
 | |
| #                       xCAT::MsgUtils->message("S","Error dispatching request to ".$ENV{XCATHOST}.": ".$errstr);
 | |
| #               } else {
 | |
| #                       xCAT::MsgUtils->message("S","Error dispatching request to ".$ENV{XCATHOST}.", trying other service nodes: ".$errstr);
 | |
| #               }
 | |
| #               next;
 | |
| #               } else {
 | |
| #               $request_satisfied=1;
 | |
| #               last;
 | |
| #           }
 | |
|          } else {
 | |
|             $$progname.=": locally executing";
 | |
|             $SIG{CHLD}='DEFAULT';
 | |
| #           ${"xCAT_plugin::".$modname."::"}{process_request}->($_,\&dispatch_callback,\&do_request);
 | |
| #----- changed in Client.pm -----#
 | |
|             ${"xCAT_plugin::".$modname."::"}{process_request}->($_,$dispatch_cb,\&do_request);
 | |
| #----- end changed in Client.pm -----#
 | |
|             last;
 | |
|         }
 | |
|      }
 | |
| #    if ($numdests > 1 and not $request_satisfied) {
 | |
| #           xCAT::MsgUtils->message("S","Error dispatching a request to all possible service nodes for request");
 | |
| #       dispatch_callback({error=>["Failed to dispatch command to any of the following service nodes: ".join(",",@xcatdests)],errorcode=>[1]});
 | |
| #    }
 | |
| 
 | |
| #    xexit;
 | |
|   }
 | |
| #while (($dispatch_children > 0) and ($child_fdset->count > 0)) { relay_dispatch($child_fdset) }
 | |
| #while (relay_dispatch($child_fdset)) { } #Potentially useless drain.
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| ###################################
 | |
| # do_request
 | |
| #    called from a plugin to execute another xCAT plugin command internally
 | |
| #
 | |
| # NOTE:  This is copied from xcatd (last merge 11/23/09).
 | |
| #        Will eventually move to using common source....
 | |
| ###################################
 | |
| sub do_request {
 | |
|   my $req = shift;
 | |
|   my $second = shift;
 | |
|   my $rsphandler = \&build_response;
 | |
|   my $sock = undef;
 | |
|   if ($second) {
 | |
|     if (ref($second) eq "CODE") {
 | |
|       $rsphandler = $second;
 | |
|     } elsif (ref($second) eq "GLOB") {
 | |
|       $sock = $second;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   #my $sock = shift; #If no sock, will return a response hash
 | |
|   if ($cmd_handlers{$req->{command}->[0]}) {
 | |
|      return plugin_command($req,$sock,$rsphandler);
 | |
|   } elsif ($req->{command}->[0] eq "noderange" and $req->{noderange}) {
 | |
|      my @nodes = xCAT::NodeRange::noderange($req->{noderange}->[0]);
 | |
|      my %resp;
 | |
|      if (xCAT::NodeRange::nodesmissed()) {
 | |
|        $resp{warning}="Invalid nodes in noderange:".join ',',xCAT::NodeRange::nodesmissed() ."\n";
 | |
|      }
 | |
|      $resp{serverdone} = {};
 | |
|      @{$resp{node}}=@nodes;
 | |
|      if ($req->{transid}) {
 | |
|        $resp{transid}=$req->{transid}->[0];
 | |
|      }
 | |
|      if ($sock) {
 | |
|        print $sock XMLout(\%resp,RootName => 'xcatresponse',NoAttr=>1);
 | |
|      } else {
 | |
|        return (\%resp);
 | |
|      }
 | |
|   } else {
 | |
|      my %resp=(error=>"Unsupported request");
 | |
|      $resp{serverdone} = {};
 | |
|      if ($req->{transid}) {
 | |
|        $resp{transid}=$req->{transid}->[0];
 | |
|      }
 | |
|      if ($sock) {
 | |
|        print $sock XMLout(\%resp,RootName => 'xcatresponse',NoAttr=>1);
 | |
|      } else {
 | |
|        return (\%resp);
 | |
|      }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| ###################################
 | |
| # build_response
 | |
| #   This callback handles responses from nested level plugin calls.
 | |
| #   It builds a merged hash of all responses that gets passed back
 | |
| #   to the calling plugin.
 | |
| #   Note:  Need to create a "deep clone" of this response to add to the
 | |
| #     return, otherwise next time through the referenced data is overwritten
 | |
| #
 | |
| ###################################
 | |
| sub build_response {
 | |
|   my $rsp = shift;
 | |
|   require Storable;
 | |
|   foreach (keys %$rsp) {
 | |
|     my $subresp = Storable::dclone($rsp->{$_});
 | |
|     push (@{$Main::resps->{$_}}, @{$subresp});
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| }    # end of submit_request()
 | |
| 
 | |
| 
 | |
| 
 | |
| ##########################################
 | |
| # handle_response is a default callback that can be passed into submit_response()
 | |
| # It is invoked repeatedly by submit_response() to print out the data returned by
 | |
| # the plugin.
 | |
| #
 | |
| # The normal flow is:
 | |
| #	-> client cmd (e.g. nodels, which is just a link to xcatclient)
 | |
| #		-> xcatclient
 | |
| #			-> submit_request()
 | |
| #				-> send xml request to xcatd
 | |
| #					-> xcatd
 | |
| #						-> process_request() of the plugin
 | |
| #						<- plugin callback
 | |
| #					<- xcatd
 | |
| #				<- xcatd sends xml response to client
 | |
| #			<- submit_request() read response
 | |
| #		<- handle_response() prints responses and saves exit codes
 | |
| #	<- xcatclient gets exit code and exits
 | |
| #
 | |
| # But in XCATBYPASS mode, the flow is:
 | |
| #	-> client cmd (e.g. nodels, which is just a link to xcatclient)
 | |
| #		-> xcatclient
 | |
| #			-> submit_request()
 | |
| #				-> process_request() of the plugin
 | |
| #		<- handle_response() prints responses and saves exit codes
 | |
| #	<- xcatclient gets exit code and exits
 | |
| #
 | |
| # Format of the response hash:
 | |
| #  {data => [ 'data str1', 'data str2', '...' ] }
 | |
| #
 | |
| #    Results are printed as:
 | |
| #       data str1
 | |
| #       data str2
 | |
| #
 | |
| # or:
 | |
| #  {data => [ {desc => [ 'desc1' ],
 | |
| #              contents => [ 'contents1' ] },
 | |
| #             {desc => [ 'desc2 ],
 | |
| #              contents => [ 'contents2' ] }
 | |
| #                :
 | |
| #            ] }
 | |
| #    NOTE:  In this format, only the data array can have more than one
 | |
| #           element. All other arrays are assumed to be a single element.
 | |
| #    Results are printed as:
 | |
| #       desc1: contents1
 | |
| #       desc2: contents2
 | |
| #
 | |
| # or:
 | |
| #  {node => [ {name => ['node1'],
 | |
| #              data => [ {desc => [ 'node1 desc' ],
 | |
| #                         contents => [ 'node1 contents' ] } ] },
 | |
| #             {name => ['node2'],
 | |
| #              data => [ {desc => [ 'node2 desc' ],
 | |
| #                         contents => [ 'node2 contents' ] } ] },
 | |
| #                :
 | |
| #             ] }
 | |
| #    NOTE:  Only the node array can have more than one element.
 | |
| #           All other arrays are assumed to be a single element.
 | |
| #
 | |
| #    This was generated from the corresponding XML:
 | |
| #    <xcatrequest>
 | |
| #      <node>
 | |
| #        <name>node1</name>
 | |
| #        <data>
 | |
| #          <desc>node1 desc</desc>
 | |
| #          <contents>node1 contents</contents>
 | |
| #        </data>
 | |
| #      </node>
 | |
| #      <node>
 | |
| #        <name>node2</name>
 | |
| #        <data>
 | |
| #          <desc>node2 desc</desc>
 | |
| #          <contents>node2 contents</contents>
 | |
| #        </data>
 | |
| #      </node>
 | |
| #    </xcatrequest>
 | |
| #
 | |
| #   Results are printed as:
 | |
| #      node_name: desc: contents
 | |
| ##########################################
 | |
| sub handle_response {
 | |
|   my $rsp = shift;
 | |
| #print "in handle_response\n";
 | |
|   # Handle errors
 | |
|   if ($rsp->{errorcode}) {
 | |
|     if (ref($rsp->{errorcode}) eq 'ARRAY') {
 | |
|       foreach my $ecode (@{$rsp->{errorcode}}) { 
 | |
|         $xCAT::Client::EXITCODE |= $ecode;
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       $xCAT::Client::EXITCODE |= $rsp->{errorcode};
 | |
|     }   # assume it is a non-reference scalar
 | |
|   }
 | |
|   if ($rsp->{error}) {
 | |
| #print "printing error\n";
 | |
|     if (ref($rsp->{error}) eq 'ARRAY') {
 | |
|       foreach my $text (@{$rsp->{error}}) {
 | |
|         print STDERR "Error: $text\n";
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       print ("Error: ".$rsp->{error}."\n");
 | |
|     }
 | |
|   }
 | |
|   if ($rsp->{warning}) {
 | |
| #print "printing warning\n";
 | |
|     if (ref($rsp->{warning}) eq 'ARRAY') {
 | |
|       foreach my $text (@{$rsp->{warning}}) {
 | |
|         print STDERR "Warning: $text\n";
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       print ("Warning: ".$rsp->{warning}."\n");
 | |
|     }
 | |
|   }
 | |
|   if ($rsp->{info}) {
 | |
| #print "printing info\n";
 | |
|     if (ref($rsp->{info}) eq 'ARRAY') {
 | |
|       foreach my $text (@{$rsp->{info}}) {
 | |
|         print "$text\n";
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       print ($rsp->{info}."\n");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if ($rsp->{sinfo}) {
 | |
|     if (ref($rsp->{sinfo}) eq 'ARRAY') {
 | |
|       foreach my $text (@{$rsp->{sinfo}}) {
 | |
|         print "$text\r"; $|++;
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       print ($rsp->{sinfo}."\r"); $|++;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
| 
 | |
|   # Handle {node} structure
 | |
|   my $errflg=0;
 | |
|   if (scalar @{$rsp->{node}}) {
 | |
| #print "printing node\n";
 | |
|     my $nodes=($rsp->{node});
 | |
|     my $node;
 | |
|     foreach $node (@$nodes) {
 | |
|       my $desc=$node->{name}->[0];
 | |
|       if ($node->{errorcode}) {
 | |
|         if (ref($node->{errorcode}) eq 'ARRAY') {
 | |
|           foreach my $ecode (@{$node->{errorcode}}) {
 | |
|             $xCAT::Client::EXITCODE |= $ecode;
 | |
|           }
 | |
|         }
 | |
|     	else {
 | |
|           $xCAT::Client::EXITCODE |= $node->{errorcode};
 | |
|         }   # assume it is a non-reference scalar
 | |
|       }
 | |
|       if ($node->{error}) {
 | |
|          $desc.=": Error: ".$node->{error}->[0];
 | |
| 		 $errflg=1;
 | |
|       }
 | |
|       if ($node->{warning}) {
 | |
|          $desc.=": Warning: ".$node->{warning}->[0];
 | |
| 		 $errflg=1;
 | |
|       }
 | |
|       if ($node->{data}) {
 | |
|          if (ref(\($node->{data}->[0])) eq 'SCALAR') {
 | |
|             $desc=$desc.": ".$node->{data}->[0];
 | |
|          } else {
 | |
|             if ($node->{data}->[0]->{desc}) {
 | |
|               $desc=$desc.": ".$node->{data}->[0]->{desc}->[0];
 | |
|             }
 | |
|             if ($node->{data}->[0]->{contents}) {
 | |
|         $desc="$desc: ".$node->{data}->[0]->{contents}->[0];
 | |
|             }
 | |
|          }
 | |
|       }
 | |
|       if ($desc) {
 | |
| 		if ($errflg == 1) {
 | |
| 		  print STDERR ("$desc\n");
 | |
| 		} else {
 | |
|           print "$desc\n";
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   # Handle {data} structure with no nodes
 | |
|   if ($rsp->{data}) {
 | |
| #print "printing data\n";
 | |
|     my $data=($rsp->{data});
 | |
|     my $data_entry;
 | |
|     foreach $data_entry (@$data) {
 | |
|       my $desc;
 | |
|          if (ref(\($data_entry)) eq 'SCALAR') {
 | |
|             $desc=$data_entry;
 | |
|          } else {
 | |
|             if ($data_entry->{desc}) {
 | |
|               $desc=$data_entry->{desc}->[0];
 | |
|             }
 | |
|             if ($data_entry->{contents}) {
 | |
|                if ($desc) {
 | |
|            $desc="$desc: ".$data_entry->{contents}->[0];
 | |
|                } else {
 | |
|            $desc=$data_entry->{contents}->[0];
 | |
|             }
 | |
|          }
 | |
|       }
 | |
|       if ($desc) { print "$desc\n"; }
 | |
|     }
 | |
|   }
 | |
| }      # end of handle_response
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 |