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