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