Annotation of loncom/lonManage, revision 1.19
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.19 ! foxr 6: # $Id: lonManage,v 1.18 2003/10/28 11:55:58 foxr Exp $
1.1 foxr 7: #
1.19 ! foxr 8: # $Id: lonManage,v 1.18 2003/10/28 11:55:58 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: #
1.16 foxr 36: # lonManage -push <tablename> newfile [host]
1.1 foxr 37: # Push <tablename> to the lonTabs directory. Note that
38: # <tablename> must be one of:
1.15 foxr 39: # host (hosts.tab)
1.1 foxr 40: # domain (domain.tab)
41: #
1.16 foxr 42: # lonManage -reinit lonc [host]
1.1 foxr 43: # Sends a HUP signal to the remote systems's lond.
44: #
1.16 foxr 45: # lonmanage -reinit lond [host]
1.1 foxr 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,
1.16 foxr 50: # not the IP address of the host
51: #
52: # If [host] is not supplied, every host in the client's hosts.tab
53: # table is iterated through and procesed..
1.1 foxr 54: #
1.3 foxr 55: #
1.10 foxr 56:
1.14 foxr 57:
1.13 foxr 58:
1.10 foxr 59: # Modules required:
60:
1.17 foxr 61: use lib ".";
62:
1.7 foxr 63: use strict; # Because it's good practice.
64: use English; # Cause I like meaningful names.
1.3 foxr 65: use Getopt::Long;
1.13 foxr 66: use IO::Socket::UNIX; # To communicate with lonc.
1.17 foxr 67: use LondConnection;
1.10 foxr 68:
69: # File scoped variables:
70:
71: my %perlvar; # Perl variable defs from apache config.
72: my %hostshash; # Host table as a host indexed hash.
1.2 foxr 73:
1.19 ! foxr 74: my $MyHost=""; # Host name to use as me.
! 75: my $ForeignHostTab=""; # Name of foreign hosts table.
1.18 foxr 76:
1.13 foxr 77: #
78: # prints out utility's command usage info.
79: #
1.3 foxr 80: sub Usage {
1.2 foxr 81: print "Usage:";
82: print <<USAGE;
1.18 foxr 83: lonManage [--myname=host --hosts=table] --push=<tablename> newfile [host]
1.2 foxr 84: Push <tablename> to the lonTabs directory. Note that
85: <tablename> must be one of:
1.15 foxr 86: host (hosts.tab)
1.2 foxr 87: domain (domain.tab)
88:
1.18 foxr 89: lonManage [--myname=host --hosts=table] --reinit=lonc [host]
1.15 foxr 90: Causes lonc in the remote system to reread hosts.tab and
91: adjust the set of clients that are being maintained to match
92: the new file.
93:
1.2 foxr 94:
1.18 foxr 95: lonManage [--myname=host --hosts=table] --reinit=lond [host]
1.15 foxr 96: Causes lond in the remote system to reread the hosts.tab file
97: and adjust the set of servers to match changes in that file.
1.2 foxr 98:
99: In the above syntax, the host above is the hosts.tab name of a host,
100: not the IP address of the host.
1.16 foxr 101:
102: If [host] is omitted, all hosts in the hosts.tab file are iterated
103: over.
104:
1.18 foxr 105: For all of the above syntaxes if --myname=host and --hosts=table are
106: supplied (both must be present), the utility runs in standalone mode
107: presenting itself to the world as 'host' and using the hosts.tab file
108: specified in the --hosts switch.
1.2 foxr 109: USAGE
110:
111:
112: }
1.13 foxr 113: #
114: # Lifted from lonnet.pm - and we need to figure out a way to get it back in.
115: # Performas a transaction with lond via the lonc proxy server.
116: # Parameter:
117: # cmd - The text of the request.
118: # host - The host to which the request ultimately goes.
119: # Returns:
120: # The text of the reply from the lond or con_lost if not able to contact
121: # lond/lonc etc.
122: #
123: sub subreply {
124: my ($cmd,$server)=@_;
125: my $peerfile="$perlvar{'lonSockDir'}/$server";
126: my $client=IO::Socket::UNIX->new(Peer =>"$peerfile",
127: Type => SOCK_STREAM,
128: Timeout => 10)
129: or return "con_lost";
130: print $client "$cmd\n";
131: my $answer=<$client>;
132: if (!$answer) { $answer="con_lost"; }
133: chomp($answer);
134: return $answer;
135: }
136: # >>> BUGBUG <<<
1.2 foxr 137: #
1.3 foxr 138: # Use Getopt::Long to parse the parameters of the program.
139: #
140: # Return value is a list consisting of:
141: # A 'command' which is one of:
142: # push - table push requested.
143: # reinit - reinit requested.
144: # Additional parameters as follows:
145: # for push: Tablename, hostname
146: # for reinit: Appname hostname
147: #
148: # This function does not validation of the parameters of push and
149: # reinit.
1.4 foxr 150: #
151: # returns a list. The first element of the list is the operation name
152: # (e.g. reinit or push). The second element is the switch parameter.
153: # for push, this is the table name, for reinit, this is the process name.
154: # Additional elements of the list are the command argument. The count of
155: # command arguments is validated, but not their semantics.
156: #
1.3 foxr 157: # returns an empty list if the parse fails.
158: #
159:
1.18 foxr 160:
1.3 foxr 161: sub ParseArgs {
1.4 foxr 162: my $pushing = '';
1.7 foxr 163: my $reinitting = '';
1.5 foxr 164:
1.4 foxr 165: if(!GetOptions('push=s' => \$pushing,
1.18 foxr 166: 'reinit=s' => \$reinitting,
167: 'myname=s' => \$MyHost,
168: 'hosts=s' => \$ForeignHostTab)) {
169: return ();
170: }
171: # The --myname and --hosts switch must have values and
172: # most both appear if either appears:
173:
174: if(($MyHost ne "") && ($ForeignHostTab eq "")) {
175: return ();
176: }
177: if(($ForeignHostTab ne "") && ($MyHost eq "")) {
1.4 foxr 178: return ();
179: }
180:
181: # Require exactly one of --push and --reinit
182:
1.5 foxr 183: my $command = '';
1.4 foxr 184: my $commandarg = '';
1.5 foxr 185: my $paramcount = @ARGV; # Number of additional arguments.
186:
187:
1.4 foxr 188: if($pushing ne '') {
1.5 foxr 189:
1.16 foxr 190: # --push takes in addition a table, and an optional host:
1.5 foxr 191: #
1.16 foxr 192: if(($paramcount != 2) && ($paramcount != 1)) {
1.5 foxr 193: return (); # Invalid parameter count.
194: }
1.4 foxr 195: if($command ne '') {
196: return ();
197: } else {
1.5 foxr 198:
1.4 foxr 199: $command = 'push';
200: $commandarg = $pushing;
201: }
202: }
1.5 foxr 203:
1.4 foxr 204: if ($reinitting ne '') {
1.5 foxr 205:
1.16 foxr 206: # --reinit takes in addition just an optional host name
1.5 foxr 207:
1.16 foxr 208: if($paramcount > 1) {
1.5 foxr 209: return ();
210: }
1.4 foxr 211: if($command ne '') {
212: return ();
213: } else {
214: $command = 'reinit';
215: $commandarg = $reinitting;
216: }
217: }
218:
1.5 foxr 219: # Build the result list:
220:
221: my @result = ($command, $commandarg);
222: my $i;
223: for($i = 0; $i < $paramcount; $i++) {
224: push(@result, $ARGV[$i]);
225: }
226:
227: return @result;
1.3 foxr 228: }
1.10 foxr 229: #
1.19 ! foxr 230: # Read the loncapa configuration stuff. If ForeignHostTab is empty,
! 231: # assume we are part of a loncapa cluster and read the hosts.tab
! 232: # file from the config directory. Otherwise, ForeignHossTab
! 233: # is the name of an alternate configuration file to read in
! 234: # standalone mode.
1.11 foxr 235: #
236: sub ReadConfig {
1.19 ! foxr 237:
! 238: if($ForeignHostTab eq "") {
! 239: my $perlvarref = LondConnection::read_conf('loncapa.conf');
! 240: %perlvar = %{$perlvarref};
! 241: my $hoststab = LondConnection::read_hosts(
! 242: "$perlvar{'lonTabDir'}/hosts.tab");
! 243: %hostshash = %{$hoststab};
! 244: } else {
! 245: my $hoststab = LondConnection::read_hosts($ForeignHostTab);
! 246: %hostshash = %{$hoststab};
! 247: $perlvar{londPort} = 5663;
! 248: $perlvar{lonSockDir} = "/home/httpd/sockets";
! 249: }
1.11 foxr 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:
1.10 foxr 266:
267: return defined $hostshash{$host};
268:
1.8 foxr 269: }
1.13 foxr 270:
271:
272:
1.12 foxr 273: #
274: # Performs a transaction with lonc.
275: # By the time this is called, the transaction has already been
276: # validated by the caller.
277: #
278: # Parameters:
279: #
280: # host - hosts.tab name of the host whose lonc we'll be talking to.
281: # command - The base command we'll be asking lond to execute.
282: # body - [optional] If supplied, this is a command body that is a ref.
283: # to an array of lines that will be appended to the
284: # command.
285: #
286: # NOTE:
287: # The command will be done as an encrypted operation.
288: #
1.8 foxr 289: sub Transact {
1.12 foxr 290: my $host = shift;
291: my $command = shift;
292: my $haveBody= 0;
293: my $body;
294: my $i;
295:
296: if(scalar @ARG) {
297: $body = shift;
298: $haveBody = 1;
299: }
300: # Construct the command to send to the server:
301:
302: my $request = "encrypt\:"; # All requests are encrypted.
303: $request .= $command;
304: if($haveBody) {
305: $request .= "\:";
306: my $bodylines = scalar @$body;
307: for($i = 0; $i < $bodylines; $i++) {
308: $request .= $$body[$i];
309: }
310: } else {
311: $request .= "\n";
312: }
1.13 foxr 313: # Body is now built... transact with lond..
314:
315: my $answer = subreply($request, $host);
316:
317: print "$answer\n";
1.10 foxr 318:
1.8 foxr 319: }
1.7 foxr 320: #
321: # Called to push a file to the remote system.
322: # The only legal files to push are hosts.tab and domain.tab.
323: # Security is somewhat improved by
324: #
325: # - Requiring the user run as root.
326: # - Connecting with lonc rather than lond directly ensuring this is a loncapa
327: # host
328: # - We must appear in the remote host's hosts.tab file.
329: # - The host must appear in our hosts.tab file.
330: #
331: # Parameters:
332: # tablename - must be one of hosts or domain.
333: # tablefile - name of the file containing the table to push.
334: # host - name of the host to push this file to.
335: #
1.13 foxr 336: # >>>BUGBUG<<< This belongs in lonnet.pm.
337: #
1.7 foxr 338: sub PushFile {
339: my $tablename = shift;
340: my $tablefile = shift;
341: my $host = shift;
342:
1.8 foxr 343: # Open the table file:
344:
345: if(!open(TABLEFILE, "<$tablefile")) {
346: die "ENOENT - No such file or directory $tablefile";
347: }
348:
349: # Require that the host be valid:
350:
351: if(!ValidHost($host)) {
352: die "EHOSTINVAL - Invalid host $host"; # Ok so I invented this 'errno'.
353: }
354: # Read in the file. If the table name is valid, push it.
355:
356: my @table = <TABLEFILE>; # These files are pretty small.
357: close TABLEFILE;
358:
359: if( ($tablename eq "host") ||
360: ($tablename eq "domain")) {
1.16 foxr 361: print("Pushing $tablename to $host\n");
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")) {
1.16 foxr 395: print("Reinitializing $process in $host\n");
1.9 foxr 396: Transact($host, "reinit:$process");
397: } else {
398: die "EINVAL -Invalid parameter. Process $process must be lonc or lond";
399: }
1.7 foxr 400: }
1.6 foxr 401: #--------------------------- Entry point: --------------------------
402:
1.16 foxr 403:
404:
1.6 foxr 405: # Parse the parameters
406: # If command parsing failed, then print usage:
1.2 foxr 407:
1.7 foxr 408: my @params = ParseArgs;
409: my $nparam = @params;
1.3 foxr 410:
411: if($nparam == 0) {
1.2 foxr 412: Usage;
1.4 foxr 413: exit -1;
1.2 foxr 414: }
1.7 foxr 415: #
416: # Next, ensure we are running as EID root.
417: #
418: if ($EUID != 0) {
419: die "ENOPRIV - No privilege for requested operation"
1.6 foxr 420: }
421:
1.19 ! foxr 422: #
! 423: # Read the configuration file.
! 424: #
! 425:
! 426: ReadConfig; # Read the configuration info (incl.hosts).
1.4 foxr 427:
1.6 foxr 428: # Based on the operation requested invoke the appropriate function:
429:
1.7 foxr 430: my $operation = shift @params;
1.6 foxr 431:
432: if($operation eq "push") { # push tablename filename host
1.7 foxr 433: my $tablename = shift @params;
434: my $tablefile = shift @params;
435: my $host = shift @params;
1.16 foxr 436: if($host) {
437: PushFile($tablename, $tablefile, $host);
438: } else { # Push to whole cluster.
439: foreach my $host (keys %hostshash) {
440: PushFile($tablename, $tablefile, $host);
441: }
442: }
1.6 foxr 443:
1.7 foxr 444: } elsif($operation eq "reinit") { # reinit processname host.
445: my $process = shift @params;
446: my $host = shift @params;
1.16 foxr 447: if ($host) {
448: ReinitProcess($process, $host);
449: } else { # Reinit whole cluster.
450: foreach my $host (keys %hostshash) {
451: ReinitProcess($process,$host);
452: }
453: }
454: }
1.7 foxr 455: else {
456: Usage;
1.6 foxr 457: }
1.4 foxr 458: exit 0;
1.2 foxr 459:
460: =head1 NAME
461: lonManage - Command line utility for remote management of lonCAPA
462: cluster nodes.
463:
464: =head1 SYNOPSIS
465:
466: Usage:
1.3 foxr 467: B<lonManage --push=<tablename> newfile host>
1.2 foxr 468: Push <tablename> to the lonTabs directory. Note that
469: <tablename> must be one of:
470: hosts (hosts.tab)
471: domain (domain.tab)
472:
1.3 foxr 473: B<lonManage --reinit=lonc host>
1.2 foxr 474: Sends a HUP signal to the remote systems's lond.
475:
1.3 foxr 476: B<lonmanage --reinit=lond host>
1.2 foxr 477: Requests the remote system's lond perform the same action as if
478: it had received a HUP signal.
479:
480: In the above syntax, the host above is the hosts.tab name of a host,
481: not the IP address of the host.
482:
483:
484: =head1 DESCRIPTION
485:
486: =head1 PREREQUISITES
1.3 foxr 487:
1.7 foxr 488: =item strict
1.3 foxr 489: =item Getopt::Long
1.7 foxr 490: =item English
1.13 foxr 491: =item IO::Socket::UNIX
492:
493: =head1 KEY Subroutines.
1.2 foxr 494:
495: =head1 CATEGORIES
496: Command line utility
497:
498: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>