#!/usr/bin/perl # The LearningOnline Network with CAPA # # lonManage supports remote management of nodes in a LonCAPA cluster. # # $Id: lonManage,v 1.13 2003/08/19 10:26:24 foxr Exp $ # # $Id: lonManage,v 1.13 2003/08/19 10:26:24 foxr Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). ## LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # # # lonManage supports management of remot nodes in a lonCAPA cluster. # it is a command line tool. The following command line syntax (usage) # is supported: # # lonManage -push newfile host # Push to the lonTabs directory. Note that # must be one of: # hosts (hosts.tab) # domain (domain.tab) # # lonManage -reinit lonc host # Sends a HUP signal to the remote systems's lond. # # lonmanage -reinit lond host # Requests the remote system's lond perform the same action as if # it had received a HUP signal. # # In the above syntax, the host above is the hosts.tab name of a host, # not the IP address of the host. # # $Log: lonManage,v $ # Revision 1.13 2003/08/19 10:26:24 foxr # Initial working version... tested against an unmodified lond this # produces an unknown_cmd response which is about what I'd expect. # # Revision 1.12 2003/08/18 11:08:07 foxr # Debug request building in Transact. # # Revision 1.11 2003/08/18 10:45:32 foxr # Felt strongly enough about hoisting ReadConfiguration into a separate sub # that I did it now before I forgot. # # Revision 1.10 2003/08/18 10:43:31 foxr # Code/test ValidHost. The hosts.tab and the perl variables are read in as # global hashes as a side effect. May later want to clean this up by making # a separate getconfig function and hoisting the config reads into that. # # Revision 1.9 2003/08/18 10:25:46 foxr # Write ReinitProcess function in terms of ValidHost and Transact. # # Revision 1.8 2003/08/18 10:18:21 foxr # Completed PushFile function in terms of # - ValidHost - Determines if target host is valid. # - Transact - Performs one of the valid transactions with the # appropriate lonc<-->lond client/server pairs. # # Revision 1.7 2003/08/18 09:56:01 foxr # 1. Require to be run as root. # 2. Catch case where no operation switch is supplied and put out usage. # 3. skeleton/comments for PushFile function. # # Revision 1.6 2003/08/12 11:02:59 foxr # Implement command switch dispatching. # # Revision 1.5 2003/08/12 10:55:42 foxr # Complete command line parsing (tested) # # Revision 1.4 2003/08/12 10:40:44 foxr # Get switch parsing right. # # Revision 1.3 2003/08/12 10:22:35 foxr # Put in parameter parsing infrastructure # # Revision 1.2 2003/08/12 09:58:49 foxr # Add usage and skeleton documentation. # # # >>>>BUGBUG<<<< require authentication with lond of some sort. # domain admin login eg. # Modules required: use strict; # Because it's good practice. use English; # Cause I like meaningful names. use Getopt::Long; use LONCAPA::Configuration; # To handle configuration I/O. use IO::Socket::UNIX; # To communicate with lonc. # File scoped variables: my %perlvar; # Perl variable defs from apache config. my %hostshash; # Host table as a host indexed hash. # # prints out utility's command usage info. # sub Usage { print "Usage:"; print < newfile host Push to the lonTabs directory. Note that must be one of: hosts (hosts.tab) domain (domain.tab) lonManage --reinit=lonc host Sends a HUP signal to the remote systems's lond. lonManage --reinit=lond host Requests the remote system's lond perform the same action as if it had received a HUP signal. In the above syntax, the host above is the hosts.tab name of a host, not the IP address of the host. USAGE } # # Lifted from lonnet.pm - and we need to figure out a way to get it back in. # Performas a transaction with lond via the lonc proxy server. # Parameter: # cmd - The text of the request. # host - The host to which the request ultimately goes. # Returns: # The text of the reply from the lond or con_lost if not able to contact # lond/lonc etc. # sub subreply { my ($cmd,$server)=@_; my $peerfile="$perlvar{'lonSockDir'}/$server"; my $client=IO::Socket::UNIX->new(Peer =>"$peerfile", Type => SOCK_STREAM, Timeout => 10) or return "con_lost"; print $client "$cmd\n"; my $answer=<$client>; if (!$answer) { $answer="con_lost"; } chomp($answer); return $answer; } # >>> BUGBUG <<< # # Use Getopt::Long to parse the parameters of the program. # # Return value is a list consisting of: # A 'command' which is one of: # push - table push requested. # reinit - reinit requested. # Additional parameters as follows: # for push: Tablename, hostname # for reinit: Appname hostname # # This function does not validation of the parameters of push and # reinit. # # returns a list. The first element of the list is the operation name # (e.g. reinit or push). The second element is the switch parameter. # for push, this is the table name, for reinit, this is the process name. # Additional elements of the list are the command argument. The count of # command arguments is validated, but not their semantics. # # returns an empty list if the parse fails. # sub ParseArgs { my $pushing = ''; my $reinitting = ''; if(!GetOptions('push=s' => \$pushing, 'reinit=s' => \$reinitting)) { return (); } # Require exactly one of --push and --reinit my $command = ''; my $commandarg = ''; my $paramcount = @ARGV; # Number of additional arguments. if($pushing ne '') { # --push takes in addition a table, and a host: # if($paramcount != 2) { return (); # Invalid parameter count. } if($command ne '') { return (); } else { $command = 'push'; $commandarg = $pushing; } } if ($reinitting ne '') { # --reinit takes in addition just a host name if($paramcount != 1) { return (); } if($command ne '') { return (); } else { $command = 'reinit'; $commandarg = $reinitting; } } # Build the result list: my @result = ($command, $commandarg); my $i; for($i = 0; $i < $paramcount; $i++) { push(@result, $ARGV[$i]); } return @result; } # # Read the loncapa configuration stuff. # sub ReadConfig { my $perlvarref = LONCAPA::Configuration::read_conf('loncapa.conf'); %perlvar = %{$perlvarref}; my $hoststab = LONCAPA::Configuration::read_hosts( "$perlvar{'lonTabDir'}/hosts.tab"); %hostshash = %{$hoststab}; } # # Determine if the target host is valid. # This is done by reading the current hosts.tab file. # For the host to be valid, it must be inthe file. # # Parameters: # host - Name of host to check on. # Returns: # true if host is valid. # false if host is invalid. # sub ValidHost { my $host = shift; ReadConfig; return defined $hostshash{$host}; } # # Performs a transaction with lonc. # By the time this is called, the transaction has already been # validated by the caller. # # Parameters: # # host - hosts.tab name of the host whose lonc we'll be talking to. # command - The base command we'll be asking lond to execute. # body - [optional] If supplied, this is a command body that is a ref. # to an array of lines that will be appended to the # command. # # NOTE: # The command will be done as an encrypted operation. # sub Transact { my $host = shift; my $command = shift; my $haveBody= 0; my $body; my $i; if(scalar @ARG) { $body = shift; $haveBody = 1; } # Construct the command to send to the server: my $request = "encrypt\:"; # All requests are encrypted. $request .= $command; if($haveBody) { $request .= "\:"; my $bodylines = scalar @$body; for($i = 0; $i < $bodylines; $i++) { $request .= $$body[$i]; } } else { $request .= "\n"; } # Body is now built... transact with lond.. my $answer = subreply($request, $host); print "$answer\n"; } # # Called to push a file to the remote system. # The only legal files to push are hosts.tab and domain.tab. # Security is somewhat improved by # # - Requiring the user run as root. # - Connecting with lonc rather than lond directly ensuring this is a loncapa # host # - We must appear in the remote host's hosts.tab file. # - The host must appear in our hosts.tab file. # # Parameters: # tablename - must be one of hosts or domain. # tablefile - name of the file containing the table to push. # host - name of the host to push this file to. # # >>>BUGBUG<<< This belongs in lonnet.pm. # sub PushFile { my $tablename = shift; my $tablefile = shift; my $host = shift; # Open the table file: if(!open(TABLEFILE, "<$tablefile")) { die "ENOENT - No such file or directory $tablefile"; } # Require that the host be valid: if(!ValidHost($host)) { die "EHOSTINVAL - Invalid host $host"; # Ok so I invented this 'errno'. } # Read in the file. If the table name is valid, push it. my @table = ; # These files are pretty small. close TABLEFILE; if( ($tablename eq "host") || ($tablename eq "domain")) { Transact($host, "pushfile:$tablename",\@table); } else { die "EINVAL - Invalid parameter. tablename: $tablename must be host or domain"; } } # # This function is called to reinitialize a server in a remote host. # The servers that can be reinitialized are: # - lonc - The lonc client process. # - lond - The lond daemon. # NOTE: # Reinitialization in this case means re-scanning the hosts table, # starting new lond/lonc's as approprate and stopping existing lonc/lond's. # # Parameters: # process - The name of the process to reinit (lonc or lond). # host - The host in which this reinit will happen. # # >>>BUGBUG<<<< This belongs in lonnet.pm # sub ReinitProcess { my $process = shift; my $host = shift; # Ensure the host is valid: if(!ValidHost($host)) { die "EHOSTINVAL - Invalid host $host"; } # Ensure target process selector is valid: if(($process eq "lonc") || ($process eq "lond")) { Transact($host, "reinit:$process"); } else { die "EINVAL -Invalid parameter. Process $process must be lonc or lond"; } } #--------------------------- Entry point: -------------------------- # Parse the parameters # If command parsing failed, then print usage: my @params = ParseArgs; my $nparam = @params; if($nparam == 0) { Usage; exit -1; } # # Next, ensure we are running as EID root. # if ($EUID != 0) { die "ENOPRIV - No privilege for requested operation" } # Based on the operation requested invoke the appropriate function: my $operation = shift @params; if($operation eq "push") { # push tablename filename host my $tablename = shift @params; my $tablefile = shift @params; my $host = shift @params; PushFile($tablename, $tablefile, $host); } elsif($operation eq "reinit") { # reinit processname host. my $process = shift @params; my $host = shift @params; ReinitProcess($process, $host); } else { Usage; } exit 0; =head1 NAME lonManage - Command line utility for remote management of lonCAPA cluster nodes. =head1 SYNOPSIS Usage: B newfile host> Push to the lonTabs directory. Note that must be one of: hosts (hosts.tab) domain (domain.tab) B Sends a HUP signal to the remote systems's lond. B Requests the remote system's lond perform the same action as if it had received a HUP signal. In the above syntax, the host above is the hosts.tab name of a host, not the IP address of the host. =head1 DESCRIPTION =head1 PREREQUISITES =item strict =item Getopt::Long =item English =item IO::Socket::UNIX =head1 KEY Subroutines. =head1 CATEGORIES Command line utility =cut