Annotation of loncom/lonManage, revision 1.13
1.1 foxr 1: #!/usr/bin/perl
2: # The LearningOnline Network with CAPA
3: #
4: # lonManage supports remote management of nodes in a LonCAPA cluster.
5: #
1.13 ! foxr 6: # $Id: lonManage,v 1.12 2003/08/18 11:08:07 foxr Exp $
1.1 foxr 7: #
1.13 ! foxr 8: # $Id: lonManage,v 1.12 2003/08/18 11:08:07 foxr Exp $
1.1 foxr 9: #
10: # Copyright Michigan State University Board of Trustees
11: #
12: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
13: ## LON-CAPA is free software; you can redistribute it and/or modify
14: # it under the terms of the GNU General Public License as published by
15: # the Free Software Foundation; either version 2 of the License, or
16: # (at your option) any later version.
17: #
18: # LON-CAPA is distributed in the hope that it will be useful,
19: # but WITHOUT ANY WARRANTY; without even the implied warranty of
20: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21: # GNU General Public License for more details.
22: #
23: # You should have received a copy of the GNU General Public License
24: # along with LON-CAPA; if not, write to the Free Software
25: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26: #
27: # /home/httpd/html/adm/gpl.txt
28: #
29: # http://www.lon-capa.org/
30: #
31: #
32: # lonManage supports management of remot nodes in a lonCAPA cluster.
33: # it is a command line tool. The following command line syntax (usage)
34: # is supported:
35: #
36: # lonManage -push <tablename> newfile host
37: # Push <tablename> to the lonTabs directory. Note that
38: # <tablename> must be one of:
39: # hosts (hosts.tab)
40: # domain (domain.tab)
41: #
42: # lonManage -reinit lonc host
43: # Sends a HUP signal to the remote systems's lond.
44: #
45: # lonmanage -reinit lond host
46: # Requests the remote system's lond perform the same action as if
47: # it had received a HUP signal.
48: #
49: # In the above syntax, the host above is the hosts.tab name of a host,
50: # not the IP address of the host.
51: #
1.3 foxr 52: # $Log: lonManage,v $
1.13 ! foxr 53: # Revision 1.12 2003/08/18 11:08:07 foxr
! 54: # Debug request building in Transact.
! 55: #
1.12 foxr 56: # Revision 1.11 2003/08/18 10:45:32 foxr
57: # Felt strongly enough about hoisting ReadConfiguration into a separate sub
58: # that I did it now before I forgot.
59: #
1.11 foxr 60: # Revision 1.10 2003/08/18 10:43:31 foxr
61: # Code/test ValidHost. The hosts.tab and the perl variables are read in as
62: # global hashes as a side effect. May later want to clean this up by making
63: # a separate getconfig function and hoisting the config reads into that.
64: #
1.10 foxr 65: # Revision 1.9 2003/08/18 10:25:46 foxr
66: # Write ReinitProcess function in terms of ValidHost and Transact.
67: #
1.9 foxr 68: # Revision 1.8 2003/08/18 10:18:21 foxr
69: # Completed PushFile function in terms of
70: # - ValidHost - Determines if target host is valid.
71: # - Transact - Performs one of the valid transactions with the
72: # appropriate lonc<-->lond client/server pairs.
73: #
1.8 foxr 74: # Revision 1.7 2003/08/18 09:56:01 foxr
75: # 1. Require to be run as root.
76: # 2. Catch case where no operation switch is supplied and put out usage.
77: # 3. skeleton/comments for PushFile function.
78: #
1.7 foxr 79: # Revision 1.6 2003/08/12 11:02:59 foxr
80: # Implement command switch dispatching.
81: #
1.6 foxr 82: # Revision 1.5 2003/08/12 10:55:42 foxr
83: # Complete command line parsing (tested)
84: #
1.5 foxr 85: # Revision 1.4 2003/08/12 10:40:44 foxr
86: # Get switch parsing right.
87: #
1.4 foxr 88: # Revision 1.3 2003/08/12 10:22:35 foxr
89: # Put in parameter parsing infrastructure
90: #
1.3 foxr 91: # Revision 1.2 2003/08/12 09:58:49 foxr
92: # Add usage and skeleton documentation.
1.2 foxr 93: #
1.3 foxr 94: #
1.10 foxr 95:
1.13 ! foxr 96: # >>>>BUGBUG<<<< require authentication with lond of some sort.
! 97: # domain admin login eg.
! 98:
1.10 foxr 99: # Modules required:
100:
1.7 foxr 101: use strict; # Because it's good practice.
102: use English; # Cause I like meaningful names.
1.3 foxr 103: use Getopt::Long;
1.10 foxr 104: use LONCAPA::Configuration; # To handle configuration I/O.
1.13 ! foxr 105: use IO::Socket::UNIX; # To communicate with lonc.
1.10 foxr 106:
107: # File scoped variables:
108:
109: my %perlvar; # Perl variable defs from apache config.
110: my %hostshash; # Host table as a host indexed hash.
1.2 foxr 111:
1.13 ! foxr 112: #
! 113: # prints out utility's command usage info.
! 114: #
1.3 foxr 115: sub Usage {
1.2 foxr 116: print "Usage:";
117: print <<USAGE;
1.3 foxr 118: lonManage --push=<tablename> newfile host
1.2 foxr 119: Push <tablename> to the lonTabs directory. Note that
120: <tablename> must be one of:
121: hosts (hosts.tab)
122: domain (domain.tab)
123:
1.3 foxr 124: lonManage --reinit=lonc host
1.2 foxr 125: Sends a HUP signal to the remote systems's lond.
126:
1.7 foxr 127: lonManage --reinit=lond host
1.2 foxr 128: Requests the remote system's lond perform the same action as if
129: it had received a HUP signal.
130:
131: In the above syntax, the host above is the hosts.tab name of a host,
132: not the IP address of the host.
133: USAGE
134:
135:
136: }
1.13 ! foxr 137: #
! 138: # Lifted from lonnet.pm - and we need to figure out a way to get it back in.
! 139: # Performas a transaction with lond via the lonc proxy server.
! 140: # Parameter:
! 141: # cmd - The text of the request.
! 142: # host - The host to which the request ultimately goes.
! 143: # Returns:
! 144: # The text of the reply from the lond or con_lost if not able to contact
! 145: # lond/lonc etc.
! 146: #
! 147: sub subreply {
! 148: my ($cmd,$server)=@_;
! 149: my $peerfile="$perlvar{'lonSockDir'}/$server";
! 150: my $client=IO::Socket::UNIX->new(Peer =>"$peerfile",
! 151: Type => SOCK_STREAM,
! 152: Timeout => 10)
! 153: or return "con_lost";
! 154: print $client "$cmd\n";
! 155: my $answer=<$client>;
! 156: if (!$answer) { $answer="con_lost"; }
! 157: chomp($answer);
! 158: return $answer;
! 159: }
! 160: # >>> BUGBUG <<<
1.2 foxr 161: #
1.3 foxr 162: # Use Getopt::Long to parse the parameters of the program.
163: #
164: # Return value is a list consisting of:
165: # A 'command' which is one of:
166: # push - table push requested.
167: # reinit - reinit requested.
168: # Additional parameters as follows:
169: # for push: Tablename, hostname
170: # for reinit: Appname hostname
171: #
172: # This function does not validation of the parameters of push and
173: # reinit.
1.4 foxr 174: #
175: # returns a list. The first element of the list is the operation name
176: # (e.g. reinit or push). The second element is the switch parameter.
177: # for push, this is the table name, for reinit, this is the process name.
178: # Additional elements of the list are the command argument. The count of
179: # command arguments is validated, but not their semantics.
180: #
1.3 foxr 181: # returns an empty list if the parse fails.
182: #
183:
184: sub ParseArgs {
1.4 foxr 185: my $pushing = '';
1.7 foxr 186: my $reinitting = '';
1.5 foxr 187:
1.4 foxr 188: if(!GetOptions('push=s' => \$pushing,
189: 'reinit=s' => \$reinitting)) {
190: return ();
191: }
192:
193: # Require exactly one of --push and --reinit
194:
1.5 foxr 195: my $command = '';
1.4 foxr 196: my $commandarg = '';
1.5 foxr 197: my $paramcount = @ARGV; # Number of additional arguments.
198:
199:
1.4 foxr 200: if($pushing ne '') {
1.5 foxr 201:
202: # --push takes in addition a table, and a host:
203: #
204: if($paramcount != 2) {
205: return (); # Invalid parameter count.
206: }
1.4 foxr 207: if($command ne '') {
208: return ();
209: } else {
1.5 foxr 210:
1.4 foxr 211: $command = 'push';
212: $commandarg = $pushing;
213: }
214: }
1.5 foxr 215:
1.4 foxr 216: if ($reinitting ne '') {
1.5 foxr 217:
218: # --reinit takes in addition just a host name
219:
220: if($paramcount != 1) {
221: return ();
222: }
1.4 foxr 223: if($command ne '') {
224: return ();
225: } else {
226: $command = 'reinit';
227: $commandarg = $reinitting;
228: }
229: }
230:
1.5 foxr 231: # Build the result list:
232:
233: my @result = ($command, $commandarg);
234: my $i;
235: for($i = 0; $i < $paramcount; $i++) {
236: push(@result, $ARGV[$i]);
237: }
238:
239: return @result;
1.3 foxr 240: }
1.10 foxr 241: #
1.11 foxr 242: # Read the loncapa configuration stuff.
243: #
244: sub ReadConfig {
245: my $perlvarref = LONCAPA::Configuration::read_conf('loncapa.conf');
246: %perlvar = %{$perlvarref};
247: my $hoststab = LONCAPA::Configuration::read_hosts(
248: "$perlvar{'lonTabDir'}/hosts.tab");
249: %hostshash = %{$hoststab};
250:
251: }
252: #
1.10 foxr 253: # Determine if the target host is valid.
254: # This is done by reading the current hosts.tab file.
255: # For the host to be valid, it must be inthe file.
256: #
257: # Parameters:
258: # host - Name of host to check on.
259: # Returns:
260: # true if host is valid.
261: # false if host is invalid.
262: #
1.8 foxr 263: sub ValidHost {
1.10 foxr 264: my $host = shift;
1.11 foxr 265:
266: ReadConfig;
1.10 foxr 267:
268: return defined $hostshash{$host};
269:
1.8 foxr 270: }
1.13 ! foxr 271:
! 272:
! 273:
1.12 foxr 274: #
275: # Performs a transaction with lonc.
276: # By the time this is called, the transaction has already been
277: # validated by the caller.
278: #
279: # Parameters:
280: #
281: # host - hosts.tab name of the host whose lonc we'll be talking to.
282: # command - The base command we'll be asking lond to execute.
283: # body - [optional] If supplied, this is a command body that is a ref.
284: # to an array of lines that will be appended to the
285: # command.
286: #
287: # NOTE:
288: # The command will be done as an encrypted operation.
289: #
1.8 foxr 290: sub Transact {
1.12 foxr 291: my $host = shift;
292: my $command = shift;
293: my $haveBody= 0;
294: my $body;
295: my $i;
296:
297: if(scalar @ARG) {
298: $body = shift;
299: $haveBody = 1;
300: }
301: # Construct the command to send to the server:
302:
303: my $request = "encrypt\:"; # All requests are encrypted.
304: $request .= $command;
305: if($haveBody) {
306: $request .= "\:";
307: my $bodylines = scalar @$body;
308: for($i = 0; $i < $bodylines; $i++) {
309: $request .= $$body[$i];
310: }
311: } else {
312: $request .= "\n";
313: }
1.13 ! foxr 314: # Body is now built... transact with lond..
! 315:
! 316: my $answer = subreply($request, $host);
! 317:
! 318: print "$answer\n";
1.10 foxr 319:
1.8 foxr 320: }
1.7 foxr 321: #
322: # Called to push a file to the remote system.
323: # The only legal files to push are hosts.tab and domain.tab.
324: # Security is somewhat improved by
325: #
326: # - Requiring the user run as root.
327: # - Connecting with lonc rather than lond directly ensuring this is a loncapa
328: # host
329: # - We must appear in the remote host's hosts.tab file.
330: # - The host must appear in our hosts.tab file.
331: #
332: # Parameters:
333: # tablename - must be one of hosts or domain.
334: # tablefile - name of the file containing the table to push.
335: # host - name of the host to push this file to.
336: #
1.13 ! foxr 337: # >>>BUGBUG<<< This belongs in lonnet.pm.
! 338: #
1.7 foxr 339: sub PushFile {
340: my $tablename = shift;
341: my $tablefile = shift;
342: my $host = shift;
343:
1.8 foxr 344: # Open the table file:
345:
346: if(!open(TABLEFILE, "<$tablefile")) {
347: die "ENOENT - No such file or directory $tablefile";
348: }
349:
350: # Require that the host be valid:
351:
352: if(!ValidHost($host)) {
353: die "EHOSTINVAL - Invalid host $host"; # Ok so I invented this 'errno'.
354: }
355: # Read in the file. If the table name is valid, push it.
356:
357: my @table = <TABLEFILE>; # These files are pretty small.
358: close TABLEFILE;
359:
360: if( ($tablename eq "host") ||
361: ($tablename eq "domain")) {
1.12 foxr 362: Transact($host, "pushfile:$tablename",\@table);
1.8 foxr 363: } else {
364: die "EINVAL - Invalid parameter. tablename: $tablename must be host or domain";
365: }
1.7 foxr 366: }
1.9 foxr 367: #
368: # This function is called to reinitialize a server in a remote host.
369: # The servers that can be reinitialized are:
370: # - lonc - The lonc client process.
371: # - lond - The lond daemon.
372: # NOTE:
373: # Reinitialization in this case means re-scanning the hosts table,
374: # starting new lond/lonc's as approprate and stopping existing lonc/lond's.
375: #
376: # Parameters:
377: # process - The name of the process to reinit (lonc or lond).
378: # host - The host in which this reinit will happen.
379: #
1.13 ! foxr 380: # >>>BUGBUG<<<< This belongs in lonnet.pm
! 381: #
1.9 foxr 382: sub ReinitProcess {
383: my $process = shift;
384: my $host = shift;
1.3 foxr 385:
1.9 foxr 386: # Ensure the host is valid:
387:
388: if(!ValidHost($host)) {
389: die "EHOSTINVAL - Invalid host $host";
390: }
391: # Ensure target process selector is valid:
392:
393: if(($process eq "lonc") ||
394: ($process eq "lond")) {
395: Transact($host, "reinit:$process");
396: } else {
397: die "EINVAL -Invalid parameter. Process $process must be lonc or lond";
398: }
1.7 foxr 399: }
1.6 foxr 400: #--------------------------- Entry point: --------------------------
401:
402: # Parse the parameters
403: # If command parsing failed, then print usage:
1.2 foxr 404:
1.7 foxr 405: my @params = ParseArgs;
406: my $nparam = @params;
1.3 foxr 407:
408: if($nparam == 0) {
1.2 foxr 409: Usage;
1.4 foxr 410: exit -1;
1.2 foxr 411: }
1.7 foxr 412: #
413: # Next, ensure we are running as EID root.
414: #
415: if ($EUID != 0) {
416: die "ENOPRIV - No privilege for requested operation"
1.6 foxr 417: }
418:
1.4 foxr 419:
1.6 foxr 420: # Based on the operation requested invoke the appropriate function:
421:
1.7 foxr 422: my $operation = shift @params;
1.6 foxr 423:
424: if($operation eq "push") { # push tablename filename host
1.7 foxr 425: my $tablename = shift @params;
426: my $tablefile = shift @params;
427: my $host = shift @params;
1.6 foxr 428: PushFile($tablename, $tablefile, $host);
429:
1.7 foxr 430: } elsif($operation eq "reinit") { # reinit processname host.
431: my $process = shift @params;
432: my $host = shift @params;
433: ReinitProcess($process, $host);
1.6 foxr 434: }
1.7 foxr 435: else {
436: Usage;
1.6 foxr 437: }
1.4 foxr 438: exit 0;
1.2 foxr 439:
440: =head1 NAME
441: lonManage - Command line utility for remote management of lonCAPA
442: cluster nodes.
443:
444: =head1 SYNOPSIS
445:
446: Usage:
1.3 foxr 447: B<lonManage --push=<tablename> newfile host>
1.2 foxr 448: Push <tablename> to the lonTabs directory. Note that
449: <tablename> must be one of:
450: hosts (hosts.tab)
451: domain (domain.tab)
452:
1.3 foxr 453: B<lonManage --reinit=lonc host>
1.2 foxr 454: Sends a HUP signal to the remote systems's lond.
455:
1.3 foxr 456: B<lonmanage --reinit=lond host>
1.2 foxr 457: Requests the remote system's lond perform the same action as if
458: it had received a HUP signal.
459:
460: In the above syntax, the host above is the hosts.tab name of a host,
461: not the IP address of the host.
462:
463:
464: =head1 DESCRIPTION
465:
466: =head1 PREREQUISITES
1.3 foxr 467:
1.7 foxr 468: =item strict
1.3 foxr 469: =item Getopt::Long
1.7 foxr 470: =item English
1.13 ! foxr 471: =item IO::Socket::UNIX
! 472:
! 473: =head1 KEY Subroutines.
1.2 foxr 474:
475: =head1 CATEGORIES
476: Command line utility
477:
478: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>