Annotation of loncom/lond, revision 1.204
1.1 albertel 1: #!/usr/bin/perl
2: # The LearningOnline Network
3: # lond "LON Daemon" Server (port "LOND" 5663)
1.60 www 4: #
1.204 ! albertel 5: # $Id: lond,v 1.203 2004/06/29 15:19:56 albertel Exp $
1.60 www 6: #
7: # Copyright Michigan State University Board of Trustees
8: #
9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
10: #
11: # LON-CAPA is free software; you can redistribute it and/or modify
12: # it under the terms of the GNU General Public License as published by
1.167 foxr 13: # the Free Software Foundation; either version 2 of the License, or
1.60 www 14: # (at your option) any later version.
15: #
16: # LON-CAPA is distributed in the hope that it will be useful,
17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19: # GNU General Public License for more details.
20: #
21: # You should have received a copy of the GNU General Public License
22: # along with LON-CAPA; if not, write to the Free Software
1.178 foxr 23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1.60 www 24: #
25: # /home/httpd/html/adm/gpl.txt
26: #
1.161 foxr 27:
28:
1.60 www 29: # http://www.lon-capa.org/
30: #
1.54 harris41 31:
1.134 albertel 32: use strict;
1.80 harris41 33: use lib '/home/httpd/lib/perl/';
34: use LONCAPA::Configuration;
35:
1.1 albertel 36: use IO::Socket;
37: use IO::File;
1.126 albertel 38: #use Apache::File;
1.1 albertel 39: use Symbol;
40: use POSIX;
41: use Crypt::IDEA;
42: use LWP::UserAgent();
1.3 www 43: use GDBM_File;
44: use Authen::Krb4;
1.91 albertel 45: use Authen::Krb5;
1.49 albertel 46: use lib '/home/httpd/lib/perl/';
47: use localauth;
1.193 raeburn 48: use localenroll;
1.143 foxr 49: use File::Copy;
1.169 foxr 50: use LONCAPA::ConfigFileEdit;
1.200 matthew 51: use LONCAPA::lonlocal;
52: use LONCAPA::lonssl;
1.1 albertel 53:
1.204 ! albertel 54: my $DEBUG = 0; # Non zero to enable debug log entries.
1.77 foxr 55:
1.57 www 56: my $status='';
57: my $lastlog='';
58:
1.204 ! albertel 59: my $VERSION='$Revision: 1.203 $'; #' stupid emacs
1.121 albertel 60: my $remoteVERSION;
1.115 albertel 61: my $currenthostid;
62: my $currentdomainid;
1.134 albertel 63:
64: my $client;
1.200 matthew 65: my $clientip; # IP address of client.
66: my $clientdns; # DNS name of client.
67: my $clientname; # LonCAPA name of client.
1.140 foxr 68:
1.134 albertel 69: my $server;
1.200 matthew 70: my $thisserver; # DNS of us.
71:
72: my $keymode;
1.198 foxr 73:
1.178 foxr 74: #
75: # Connection type is:
76: # client - All client actions are allowed
77: # manager - only management functions allowed.
78: # both - Both management and client actions are allowed
79: #
1.161 foxr 80:
1.178 foxr 81: my $ConnectionType;
1.161 foxr 82:
1.200 matthew 83: my %hostid; # ID's for hosts in cluster by ip.
84: my %hostdom; # LonCAPA domain for hosts in cluster.
85: my %hostip; # IPs for hosts in cluster.
86: my %hostdns; # ID's of hosts looked up by DNS name.
1.161 foxr 87:
1.178 foxr 88: my %managers; # Ip -> manager names
1.161 foxr 89:
1.178 foxr 90: my %perlvar; # Will have the apache conf defined perl vars.
1.134 albertel 91:
1.178 foxr 92: #
93: # The array below are password error strings."
94: #
95: my $lastpwderror = 13; # Largest error number from lcpasswd.
96: my @passwderrors = ("ok",
97: "lcpasswd must be run as user 'www'",
98: "lcpasswd got incorrect number of arguments",
99: "lcpasswd did not get the right nubmer of input text lines",
100: "lcpasswd too many simultaneous pwd changes in progress",
101: "lcpasswd User does not exist.",
102: "lcpasswd Incorrect current passwd",
103: "lcpasswd Unable to su to root.",
104: "lcpasswd Cannot set new passwd.",
105: "lcpasswd Username has invalid characters",
106: "lcpasswd Invalid characters in password",
107: "11", "12",
108: "lcpasswd Password mismatch");
1.97 foxr 109:
110:
1.178 foxr 111: # The array below are lcuseradd error strings.:
1.97 foxr 112:
1.178 foxr 113: my $lastadderror = 13;
114: my @adderrors = ("ok",
115: "User ID mismatch, lcuseradd must run as user www",
116: "lcuseradd Incorrect number of command line parameters must be 3",
117: "lcuseradd Incorrect number of stdinput lines, must be 3",
118: "lcuseradd Too many other simultaneous pwd changes in progress",
119: "lcuseradd User does not exist",
120: "lcuseradd Unable to make www member of users's group",
121: "lcuseradd Unable to su to root",
122: "lcuseradd Unable to set password",
123: "lcuseradd Usrname has invalid characters",
124: "lcuseradd Password has an invalid character",
125: "lcuseradd User already exists",
126: "lcuseradd Could not add user.",
127: "lcuseradd Password mismatch");
1.97 foxr 128:
1.96 foxr 129:
1.200 matthew 130: #------------------------------------------------------------------------
131: #
132: # LocalConnection
133: # Completes the formation of a locally authenticated connection.
134: # This function will ensure that the 'remote' client is really the
135: # local host. If not, the connection is closed, and the function fails.
136: # If so, initcmd is parsed for the name of a file containing the
137: # IDEA session key. The fie is opened, read, deleted and the session
138: # key returned to the caller.
139: #
140: # Parameters:
141: # $Socket - Socket open on client.
142: # $initcmd - The full text of the init command.
143: #
144: # Implicit inputs:
145: # $clientdns - The DNS name of the remote client.
146: # $thisserver - Our DNS name.
147: #
148: # Returns:
149: # IDEA session key on success.
150: # undef on failure.
151: #
152: sub LocalConnection {
153: my ($Socket, $initcmd) = @_;
154: Debug("Attempting local connection: $initcmd client: $clientdns me: $thisserver");
155: if($clientdns ne $thisserver) {
156: &logthis('<font color="red"> LocalConnection rejecting non local: '
157: ."$clientdns ne $thisserver </font>");
158: close $Socket;
159: return undef;
160: }
161: else {
162: chomp($initcmd); # Get rid of \n in filename.
163: my ($init, $type, $name) = split(/:/, $initcmd);
164: Debug(" Init command: $init $type $name ");
165:
166: # Require that $init = init, and $type = local: Otherwise
167: # the caller is insane:
168:
169: if(($init ne "init") && ($type ne "local")) {
170: &logthis('<font color = "red"> LocalConnection: caller is insane! '
171: ."init = $init, and type = $type </font>");
172: close($Socket);;
173: return undef;
174:
175: }
176: # Now get the key filename:
177:
178: my $IDEAKey = lonlocal::ReadKeyFile($name);
179: return $IDEAKey;
180: }
181: }
182: #------------------------------------------------------------------------------
183: #
184: # SSLConnection
185: # Completes the formation of an ssh authenticated connection. The
186: # socket is promoted to an ssl socket. If this promotion and the associated
187: # certificate exchange are successful, the IDEA key is generated and sent
188: # to the remote peer via the SSL tunnel. The IDEA key is also returned to
189: # the caller after the SSL tunnel is torn down.
190: #
191: # Parameters:
192: # Name Type Purpose
193: # $Socket IO::Socket::INET Plaintext socket.
194: #
195: # Returns:
196: # IDEA key on success.
197: # undef on failure.
198: #
199: sub SSLConnection {
200: my $Socket = shift;
201:
202: Debug("SSLConnection: ");
203: my $KeyFile = lonssl::KeyFile();
204: if(!$KeyFile) {
205: my $err = lonssl::LastError();
206: &logthis("<font color=\"red\"> CRITICAL"
207: ."Can't get key file $err </font>");
208: return undef;
209: }
210: my ($CACertificate,
211: $Certificate) = lonssl::CertificateFile();
212:
213:
214: # If any of the key, certificate or certificate authority
215: # certificate filenames are not defined, this can't work.
216:
217: if((!$Certificate) || (!$CACertificate)) {
218: my $err = lonssl::LastError();
219: &logthis("<font color=\"red\"> CRITICAL"
220: ."Can't get certificates: $err </font>");
221:
222: return undef;
223: }
224: Debug("Key: $KeyFile CA: $CACertificate Cert: $Certificate");
225:
226: # Indicate to our peer that we can procede with
227: # a transition to ssl authentication:
228:
229: print $Socket "ok:ssl\n";
230:
231: Debug("Approving promotion -> ssl");
232: # And do so:
233:
234: my $SSLSocket = lonssl::PromoteServerSocket($Socket,
235: $CACertificate,
236: $Certificate,
237: $KeyFile);
238: if(! ($SSLSocket) ) { # SSL socket promotion failed.
239: my $err = lonssl::LastError();
240: &logthis("<font color=\"red\"> CRITICAL "
241: ."SSL Socket promotion failed: $err </font>");
242: return undef;
243: }
244: Debug("SSL Promotion successful");
245:
246: #
247: # The only thing we'll use the socket for is to send the IDEA key
248: # to the peer:
249:
250: my $Key = lonlocal::CreateCipherKey();
251: print $SSLSocket "$Key\n";
252:
253: lonssl::Close($SSLSocket);
254:
255: Debug("Key exchange complete: $Key");
256:
257: return $Key;
258: }
259: #
260: # InsecureConnection:
261: # If insecure connections are allowd,
262: # exchange a challenge with the client to 'validate' the
263: # client (not really, but that's the protocol):
264: # We produce a challenge string that's sent to the client.
265: # The client must then echo the challenge verbatim to us.
266: #
267: # Parameter:
268: # Socket - Socket open on the client.
269: # Returns:
270: # 1 - success.
271: # 0 - failure (e.g.mismatch or insecure not allowed).
272: #
273: sub InsecureConnection {
274: my $Socket = shift;
275:
276: # Don't even start if insecure connections are not allowed.
277:
278: if(! $perlvar{londAllowInsecure}) { # Insecure connections not allowed.
279: return 0;
280: }
281:
282: # Fabricate a challenge string and send it..
283:
284: my $challenge = "$$".time; # pid + time.
285: print $Socket "$challenge\n";
286: &status("Waiting for challenge reply");
287:
288: my $answer = <$Socket>;
289: $answer =~s/\W//g;
290: if($challenge eq $answer) {
291: return 1;
292: }
293: else {
294: logthis("<font color='blue'>WARNING client did not respond to challenge</font>");
295: &status("No challenge reqply");
296: return 0;
297: }
298:
299:
300: }
301:
1.96 foxr 302: #
1.140 foxr 303: # GetCertificate: Given a transaction that requires a certificate,
304: # this function will extract the certificate from the transaction
305: # request. Note that at this point, the only concept of a certificate
306: # is the hostname to which we are connected.
307: #
308: # Parameter:
309: # request - The request sent by our client (this parameterization may
310: # need to change when we really use a certificate granting
311: # authority.
312: #
313: sub GetCertificate {
314: my $request = shift;
315:
316: return $clientip;
317: }
1.161 foxr 318:
1.178 foxr 319: #
320: # Return true if client is a manager.
321: #
322: sub isManager {
323: return (($ConnectionType eq "manager") || ($ConnectionType eq "both"));
324: }
325: #
326: # Return tru if client can do client functions
327: #
328: sub isClient {
329: return (($ConnectionType eq "client") || ($ConnectionType eq "both"));
330: }
1.161 foxr 331:
332:
1.156 foxr 333: #
334: # ReadManagerTable: Reads in the current manager table. For now this is
335: # done on each manager authentication because:
336: # - These authentications are not frequent
337: # - This allows dynamic changes to the manager table
338: # without the need to signal to the lond.
339: #
340:
341: sub ReadManagerTable {
342:
343: # Clean out the old table first..
344:
1.166 foxr 345: foreach my $key (keys %managers) {
346: delete $managers{$key};
347: }
348:
349: my $tablename = $perlvar{'lonTabDir'}."/managers.tab";
350: if (!open (MANAGERS, $tablename)) {
351: logthis('<font color="red">No manager table. Nobody can manage!!</font>');
352: return;
353: }
354: while(my $host = <MANAGERS>) {
355: chomp($host);
356: if ($host =~ "^#") { # Comment line.
357: next;
358: }
359: if (!defined $hostip{$host}) { # This is a non cluster member
1.161 foxr 360: # The entry is of the form:
361: # cluname:hostname
362: # cluname - A 'cluster hostname' is needed in order to negotiate
363: # the host key.
364: # hostname- The dns name of the host.
365: #
1.166 foxr 366: my($cluname, $dnsname) = split(/:/, $host);
367:
368: my $ip = gethostbyname($dnsname);
369: if(defined($ip)) { # bad names don't deserve entry.
370: my $hostip = inet_ntoa($ip);
371: $managers{$hostip} = $cluname;
372: logthis('<font color="green"> registering manager '.
373: "$dnsname as $cluname with $hostip </font>\n");
374: }
375: } else {
376: logthis('<font color="green"> existing host'." $host</font>\n");
377: $managers{$hostip{$host}} = $host; # Use info from cluster tab if clumemeber
378: }
379: }
1.156 foxr 380: }
1.140 foxr 381:
382: #
383: # ValidManager: Determines if a given certificate represents a valid manager.
384: # in this primitive implementation, the 'certificate' is
385: # just the connecting loncapa client name. This is checked
386: # against a valid client list in the configuration.
387: #
388: #
389: sub ValidManager {
390: my $certificate = shift;
391:
1.163 foxr 392: return isManager;
1.140 foxr 393: }
394: #
1.143 foxr 395: # CopyFile: Called as part of the process of installing a
396: # new configuration file. This function copies an existing
397: # file to a backup file.
398: # Parameters:
399: # oldfile - Name of the file to backup.
400: # newfile - Name of the backup file.
401: # Return:
402: # 0 - Failure (errno has failure reason).
403: # 1 - Success.
404: #
405: sub CopyFile {
1.192 foxr 406:
407: my ($oldfile, $newfile) = @_;
1.143 foxr 408:
409: # The file must exist:
410:
411: if(-e $oldfile) {
412:
413: # Read the old file.
414:
415: my $oldfh = IO::File->new("< $oldfile");
416: if(!$oldfh) {
417: return 0;
418: }
419: my @contents = <$oldfh>; # Suck in the entire file.
420:
421: # write the backup file:
422:
423: my $newfh = IO::File->new("> $newfile");
424: if(!(defined $newfh)){
425: return 0;
426: }
427: my $lines = scalar @contents;
428: for (my $i =0; $i < $lines; $i++) {
429: print $newfh ($contents[$i]);
430: }
431:
432: $oldfh->close;
433: $newfh->close;
434:
435: chmod(0660, $newfile);
436:
437: return 1;
438:
439: } else {
440: return 0;
441: }
442: }
1.157 foxr 443: #
444: # Host files are passed out with externally visible host IPs.
445: # If, for example, we are behind a fire-wall or NAT host, our
446: # internally visible IP may be different than the externally
447: # visible IP. Therefore, we always adjust the contents of the
448: # host file so that the entry for ME is the IP that we believe
449: # we have. At present, this is defined as the entry that
450: # DNS has for us. If by some chance we are not able to get a
451: # DNS translation for us, then we assume that the host.tab file
452: # is correct.
453: # BUGBUGBUG - in the future, we really should see if we can
454: # easily query the interface(s) instead.
455: # Parameter(s):
456: # contents - The contents of the host.tab to check.
457: # Returns:
458: # newcontents - The adjusted contents.
459: #
460: #
461: sub AdjustHostContents {
462: my $contents = shift;
463: my $adjusted;
464: my $me = $perlvar{'lonHostID'};
465:
1.166 foxr 466: foreach my $line (split(/\n/,$contents)) {
1.157 foxr 467: if(!(($line eq "") || ($line =~ /^ *\#/) || ($line =~ /^ *$/))) {
468: chomp($line);
469: my ($id,$domain,$role,$name,$ip,$maxcon,$idleto,$mincon)=split(/:/,$line);
470: if ($id eq $me) {
1.166 foxr 471: my $ip = gethostbyname($name);
472: my $ipnew = inet_ntoa($ip);
473: $ip = $ipnew;
1.157 foxr 474: # Reconstruct the host line and append to adjusted:
475:
1.166 foxr 476: my $newline = "$id:$domain:$role:$name:$ip";
477: if($maxcon ne "") { # Not all hosts have loncnew tuning params
478: $newline .= ":$maxcon:$idleto:$mincon";
479: }
480: $adjusted .= $newline."\n";
1.157 foxr 481:
1.166 foxr 482: } else { # Not me, pass unmodified.
483: $adjusted .= $line."\n";
484: }
1.157 foxr 485: } else { # Blank or comment never re-written.
486: $adjusted .= $line."\n"; # Pass blanks and comments as is.
487: }
1.166 foxr 488: }
489: return $adjusted;
1.157 foxr 490: }
1.143 foxr 491: #
492: # InstallFile: Called to install an administrative file:
493: # - The file is created with <name>.tmp
494: # - The <name>.tmp file is then mv'd to <name>
495: # This lugubrious procedure is done to ensure that we are never without
496: # a valid, even if dated, version of the file regardless of who crashes
497: # and when the crash occurs.
498: #
499: # Parameters:
500: # Name of the file
501: # File Contents.
502: # Return:
503: # nonzero - success.
504: # 0 - failure and $! has an errno.
505: #
506: sub InstallFile {
1.192 foxr 507:
508: my ($Filename, $Contents) = @_;
1.143 foxr 509: my $TempFile = $Filename.".tmp";
510:
511: # Open the file for write:
512:
513: my $fh = IO::File->new("> $TempFile"); # Write to temp.
514: if(!(defined $fh)) {
515: &logthis('<font color="red"> Unable to create '.$TempFile."</font>");
516: return 0;
517: }
518: # write the contents of the file:
519:
520: print $fh ($Contents);
521: $fh->close; # In case we ever have a filesystem w. locking
522:
523: chmod(0660, $TempFile);
524:
525: # Now we can move install the file in position.
526:
527: move($TempFile, $Filename);
528:
529: return 1;
530: }
1.200 matthew 531:
532:
1.169 foxr 533: #
534: # ConfigFileFromSelector: converts a configuration file selector
535: # (one of host or domain at this point) into a
536: # configuration file pathname.
537: #
538: # Parameters:
539: # selector - Configuration file selector.
540: # Returns:
541: # Full path to the file or undef if the selector is invalid.
542: #
543: sub ConfigFileFromSelector {
544: my $selector = shift;
545: my $tablefile;
546:
547: my $tabledir = $perlvar{'lonTabDir'}.'/';
548: if ($selector eq "hosts") {
549: $tablefile = $tabledir."hosts.tab";
550: } elsif ($selector eq "domain") {
551: $tablefile = $tabledir."domain.tab";
552: } else {
553: return undef;
554: }
555: return $tablefile;
1.143 foxr 556:
1.169 foxr 557: }
1.143 foxr 558: #
1.141 foxr 559: # PushFile: Called to do an administrative push of a file.
560: # - Ensure the file being pushed is one we support.
561: # - Backup the old file to <filename.saved>
562: # - Separate the contents of the new file out from the
563: # rest of the request.
564: # - Write the new file.
565: # Parameter:
566: # Request - The entire user request. This consists of a : separated
567: # string pushfile:tablename:contents.
568: # NOTE: The contents may have :'s in it as well making things a bit
569: # more interesting... but not much.
570: # Returns:
571: # String to send to client ("ok" or "refused" if bad file).
572: #
573: sub PushFile {
574: my $request = shift;
575: my ($command, $filename, $contents) = split(":", $request, 3);
576:
577: # At this point in time, pushes for only the following tables are
578: # supported:
579: # hosts.tab ($filename eq host).
580: # domain.tab ($filename eq domain).
581: # Construct the destination filename or reject the request.
582: #
583: # lonManage is supposed to ensure this, however this session could be
584: # part of some elaborate spoof that managed somehow to authenticate.
585: #
586:
1.169 foxr 587:
588: my $tablefile = ConfigFileFromSelector($filename);
589: if(! (defined $tablefile)) {
1.141 foxr 590: return "refused";
591: }
592: #
593: # >copy< the old table to the backup table
594: # don't rename in case system crashes/reboots etc. in the time
595: # window between a rename and write.
596: #
597: my $backupfile = $tablefile;
598: $backupfile =~ s/\.tab$/.old/;
1.143 foxr 599: if(!CopyFile($tablefile, $backupfile)) {
600: &logthis('<font color="green"> CopyFile from '.$tablefile." to ".$backupfile." failed </font>");
601: return "error:$!";
602: }
1.141 foxr 603: &logthis('<font color="green"> Pushfile: backed up '
604: .$tablefile." to $backupfile</font>");
605:
1.157 foxr 606: # If the file being pushed is the host file, we adjust the entry for ourself so that the
607: # IP will be our current IP as looked up in dns. Note this is only 99% good as it's possible
608: # to conceive of conditions where we don't have a DNS entry locally. This is possible in a
609: # network sense but it doesn't make much sense in a LonCAPA sense so we ignore (for now)
610: # that possibilty.
611:
612: if($filename eq "host") {
613: $contents = AdjustHostContents($contents);
614: }
615:
1.141 foxr 616: # Install the new file:
617:
1.143 foxr 618: if(!InstallFile($tablefile, $contents)) {
619: &logthis('<font color="red"> Pushfile: unable to install '
1.145 foxr 620: .$tablefile." $! </font>");
1.143 foxr 621: return "error:$!";
622: }
623: else {
624: &logthis('<font color="green"> Installed new '.$tablefile
625: ."</font>");
626:
627: }
628:
1.141 foxr 629:
630: # Indicate success:
631:
632: return "ok";
633:
634: }
1.145 foxr 635:
636: #
637: # Called to re-init either lonc or lond.
638: #
639: # Parameters:
640: # request - The full request by the client. This is of the form
641: # reinit:<process>
642: # where <process> is allowed to be either of
643: # lonc or lond
644: #
645: # Returns:
646: # The string to be sent back to the client either:
647: # ok - Everything worked just fine.
648: # error:why - There was a failure and why describes the reason.
649: #
650: #
651: sub ReinitProcess {
652: my $request = shift;
653:
1.146 foxr 654:
655: # separate the request (reinit) from the process identifier and
656: # validate it producing the name of the .pid file for the process.
657: #
658: #
659: my ($junk, $process) = split(":", $request);
1.147 foxr 660: my $processpidfile = $perlvar{'lonDaemons'}.'/logs/';
1.146 foxr 661: if($process eq 'lonc') {
662: $processpidfile = $processpidfile."lonc.pid";
1.147 foxr 663: if (!open(PIDFILE, "< $processpidfile")) {
664: return "error:Open failed for $processpidfile";
665: }
666: my $loncpid = <PIDFILE>;
667: close(PIDFILE);
668: logthis('<font color="red"> Reinitializing lonc pid='.$loncpid
669: ."</font>");
670: kill("USR2", $loncpid);
1.146 foxr 671: } elsif ($process eq 'lond') {
1.147 foxr 672: logthis('<font color="red"> Reinitializing self (lond) </font>');
673: &UpdateHosts; # Lond is us!!
1.146 foxr 674: } else {
675: &logthis('<font color="yellow" Invalid reinit request for '.$process
676: ."</font>");
677: return "error:Invalid process identifier $process";
678: }
1.145 foxr 679: return 'ok';
680: }
1.168 foxr 681: # Validate a line in a configuration file edit script:
682: # Validation includes:
683: # - Ensuring the command is valid.
684: # - Ensuring the command has sufficient parameters
685: # Parameters:
686: # scriptline - A line to validate (\n has been stripped for what it's worth).
1.167 foxr 687: #
1.168 foxr 688: # Return:
689: # 0 - Invalid scriptline.
690: # 1 - Valid scriptline
691: # NOTE:
692: # Only the command syntax is checked, not the executability of the
693: # command.
694: #
695: sub isValidEditCommand {
696: my $scriptline = shift;
697:
698: # Line elements are pipe separated:
699:
700: my ($command, $key, $newline) = split(/\|/, $scriptline);
701: &logthis('<font color="green"> isValideditCommand checking: '.
702: "Command = '$command', Key = '$key', Newline = '$newline' </font>\n");
703:
704: if ($command eq "delete") {
705: #
706: # key with no newline.
707: #
708: if( ($key eq "") || ($newline ne "")) {
709: return 0; # Must have key but no newline.
710: } else {
711: return 1; # Valid syntax.
712: }
1.169 foxr 713: } elsif ($command eq "replace") {
1.168 foxr 714: #
715: # key and newline:
716: #
717: if (($key eq "") || ($newline eq "")) {
718: return 0;
719: } else {
720: return 1;
721: }
1.169 foxr 722: } elsif ($command eq "append") {
723: if (($key ne "") && ($newline eq "")) {
724: return 1;
725: } else {
726: return 0;
727: }
1.168 foxr 728: } else {
729: return 0; # Invalid command.
730: }
731: return 0; # Should not get here!!!
732: }
1.169 foxr 733: #
734: # ApplyEdit - Applies an edit command to a line in a configuration
735: # file. It is the caller's responsiblity to validate the
736: # edit line.
737: # Parameters:
738: # $directive - A single edit directive to apply.
739: # Edit directives are of the form:
740: # append|newline - Appends a new line to the file.
741: # replace|key|newline - Replaces the line with key value 'key'
742: # delete|key - Deletes the line with key value 'key'.
743: # $editor - A config file editor object that contains the
744: # file being edited.
745: #
746: sub ApplyEdit {
1.192 foxr 747:
748: my ($directive, $editor) = @_;
1.169 foxr 749:
750: # Break the directive down into its command and its parameters
751: # (at most two at this point. The meaning of the parameters, if in fact
752: # they exist depends on the command).
753:
754: my ($command, $p1, $p2) = split(/\|/, $directive);
755:
756: if($command eq "append") {
757: $editor->Append($p1); # p1 - key p2 null.
758: } elsif ($command eq "replace") {
759: $editor->ReplaceLine($p1, $p2); # p1 - key p2 = newline.
760: } elsif ($command eq "delete") {
761: $editor->DeleteLine($p1); # p1 - key p2 null.
762: } else { # Should not get here!!!
763: die "Invalid command given to ApplyEdit $command"
764: }
765: }
766: #
767: # AdjustOurHost:
768: # Adjusts a host file stored in a configuration file editor object
769: # for the true IP address of this host. This is necessary for hosts
770: # that live behind a firewall.
771: # Those hosts have a publicly distributed IP of the firewall, but
772: # internally must use their actual IP. We assume that a given
773: # host only has a single IP interface for now.
774: # Formal Parameters:
775: # editor - The configuration file editor to adjust. This
776: # editor is assumed to contain a hosts.tab file.
777: # Strategy:
778: # - Figure out our hostname.
779: # - Lookup the entry for this host.
780: # - Modify the line to contain our IP
781: # - Do a replace for this host.
782: sub AdjustOurHost {
783: my $editor = shift;
784:
785: # figure out who I am.
786:
787: my $myHostName = $perlvar{'lonHostID'}; # LonCAPA hostname.
788:
789: # Get my host file entry.
790:
791: my $ConfigLine = $editor->Find($myHostName);
792: if(! (defined $ConfigLine)) {
793: die "AdjustOurHost - no entry for me in hosts file $myHostName";
794: }
795: # figure out my IP:
796: # Use the config line to get my hostname.
797: # Use gethostbyname to translate that into an IP address.
798: #
799: my ($id,$domain,$role,$name,$ip,$maxcon,$idleto,$mincon) = split(/:/,$ConfigLine);
800: my $BinaryIp = gethostbyname($name);
801: my $ip = inet_ntoa($ip);
802: #
803: # Reassemble the config line from the elements in the list.
804: # Note that if the loncnew items were not present before, they will
805: # be now even if they would be empty
806: #
807: my $newConfigLine = $id;
808: foreach my $item ($domain, $role, $name, $ip, $maxcon, $idleto, $mincon) {
809: $newConfigLine .= ":".$item;
810: }
811: # Replace the line:
812:
813: $editor->ReplaceLine($id, $newConfigLine);
814:
815: }
816: #
817: # ReplaceConfigFile:
818: # Replaces a configuration file with the contents of a
819: # configuration file editor object.
820: # This is done by:
821: # - Copying the target file to <filename>.old
822: # - Writing the new file to <filename>.tmp
823: # - Moving <filename.tmp> -> <filename>
824: # This laborious process ensures that the system is never without
825: # a configuration file that's at least valid (even if the contents
826: # may be dated).
827: # Parameters:
828: # filename - Name of the file to modify... this is a full path.
829: # editor - Editor containing the file.
830: #
831: sub ReplaceConfigFile {
1.192 foxr 832:
833: my ($filename, $editor) = @_;
1.168 foxr 834:
1.169 foxr 835: CopyFile ($filename, $filename.".old");
836:
837: my $contents = $editor->Get(); # Get the contents of the file.
838:
839: InstallFile($filename, $contents);
840: }
1.168 foxr 841: #
842: #
843: # Called to edit a configuration table file
1.167 foxr 844: # Parameters:
845: # request - The entire command/request sent by lonc or lonManage
846: # Return:
847: # The reply to send to the client.
1.168 foxr 848: #
1.167 foxr 849: sub EditFile {
850: my $request = shift;
851:
852: # Split the command into it's pieces: edit:filetype:script
853:
1.168 foxr 854: my ($request, $filetype, $script) = split(/:/, $request,3); # : in script
1.167 foxr 855:
856: # Check the pre-coditions for success:
857:
858: if($request != "edit") { # Something is amiss afoot alack.
859: return "error:edit request detected, but request != 'edit'\n";
860: }
861: if( ($filetype ne "hosts") &&
862: ($filetype ne "domain")) {
863: return "error:edit requested with invalid file specifier: $filetype \n";
864: }
865:
866: # Split the edit script and check it's validity.
1.168 foxr 867:
868: my @scriptlines = split(/\n/, $script); # one line per element.
869: my $linecount = scalar(@scriptlines);
870: for(my $i = 0; $i < $linecount; $i++) {
871: chomp($scriptlines[$i]);
872: if(!isValidEditCommand($scriptlines[$i])) {
873: return "error:edit with bad script line: '$scriptlines[$i]' \n";
874: }
875: }
1.145 foxr 876:
1.167 foxr 877: # Execute the edit operation.
1.169 foxr 878: # - Create a config file editor for the appropriate file and
879: # - execute each command in the script:
880: #
881: my $configfile = ConfigFileFromSelector($filetype);
882: if (!(defined $configfile)) {
883: return "refused\n";
884: }
885: my $editor = ConfigFileEdit->new($configfile);
1.167 foxr 886:
1.169 foxr 887: for (my $i = 0; $i < $linecount; $i++) {
888: ApplyEdit($scriptlines[$i], $editor);
889: }
890: # If the file is the host file, ensure that our host is
891: # adjusted to have our ip:
892: #
893: if($filetype eq "host") {
894: AdjustOurHost($editor);
895: }
896: # Finally replace the current file with our file.
897: #
898: ReplaceConfigFile($configfile, $editor);
1.167 foxr 899:
900: return "ok\n";
901: }
1.141 foxr 902: #
1.96 foxr 903: # Convert an error return code from lcpasswd to a string value.
904: #
905: sub lcpasswdstrerror {
906: my $ErrorCode = shift;
1.97 foxr 907: if(($ErrorCode < 0) || ($ErrorCode > $lastpwderror)) {
1.96 foxr 908: return "lcpasswd Unrecognized error return value ".$ErrorCode;
909: } else {
1.98 foxr 910: return $passwderrors[$ErrorCode];
1.96 foxr 911: }
912: }
913:
1.97 foxr 914: #
915: # Convert an error return code from lcuseradd to a string value:
916: #
917: sub lcuseraddstrerror {
918: my $ErrorCode = shift;
919: if(($ErrorCode < 0) || ($ErrorCode > $lastadderror)) {
920: return "lcuseradd - Unrecognized error code: ".$ErrorCode;
921: } else {
1.98 foxr 922: return $adderrors[$ErrorCode];
1.97 foxr 923: }
924: }
925:
1.23 harris41 926: # grabs exception and records it to log before exiting
927: sub catchexception {
1.27 albertel 928: my ($error)=@_;
1.25 www 929: $SIG{'QUIT'}='DEFAULT';
930: $SIG{__DIE__}='DEFAULT';
1.165 albertel 931: &status("Catching exception");
1.190 albertel 932: &logthis("<font color='red'>CRITICAL: "
1.134 albertel 933: ."ABNORMAL EXIT. Child $$ for server $thisserver died through "
1.27 albertel 934: ."a crash with this error msg->[$error]</font>");
1.57 www 935: &logthis('Famous last words: '.$status.' - '.$lastlog);
1.27 albertel 936: if ($client) { print $client "error: $error\n"; }
1.59 www 937: $server->close();
1.27 albertel 938: die($error);
1.23 harris41 939: }
940:
1.63 www 941: sub timeout {
1.165 albertel 942: &status("Handling Timeout");
1.190 albertel 943: &logthis("<font color='red'>CRITICAL: TIME OUT ".$$."</font>");
1.63 www 944: &catchexception('Timeout');
945: }
1.22 harris41 946: # -------------------------------- Set signal handlers to record abnormal exits
947:
948: $SIG{'QUIT'}=\&catchexception;
949: $SIG{__DIE__}=\&catchexception;
950:
1.81 matthew 951: # ---------------------------------- Read loncapa_apache.conf and loncapa.conf
1.95 harris41 952: &status("Read loncapa.conf and loncapa_apache.conf");
953: my $perlvarref=LONCAPA::Configuration::read_conf('loncapa.conf');
1.141 foxr 954: %perlvar=%{$perlvarref};
1.80 harris41 955: undef $perlvarref;
1.19 www 956:
1.35 harris41 957: # ----------------------------- Make sure this process is running from user=www
958: my $wwwid=getpwnam('www');
959: if ($wwwid!=$<) {
1.134 albertel 960: my $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
961: my $subj="LON: $currenthostid User ID mismatch";
1.37 harris41 962: system("echo 'User ID mismatch. lond must be run as user www.' |\
1.35 harris41 963: mailto $emailto -s '$subj' > /dev/null");
964: exit 1;
965: }
966:
1.19 www 967: # --------------------------------------------- Check if other instance running
968:
969: my $pidfile="$perlvar{'lonDaemons'}/logs/lond.pid";
970:
971: if (-e $pidfile) {
972: my $lfh=IO::File->new("$pidfile");
973: my $pide=<$lfh>;
974: chomp($pide);
1.29 harris41 975: if (kill 0 => $pide) { die "already running"; }
1.19 www 976: }
1.1 albertel 977:
978: # ------------------------------------------------------------- Read hosts file
979:
980:
981:
982: # establish SERVER socket, bind and listen.
983: $server = IO::Socket::INET->new(LocalPort => $perlvar{'londPort'},
984: Type => SOCK_STREAM,
985: Proto => 'tcp',
986: Reuse => 1,
987: Listen => 10 )
1.29 harris41 988: or die "making socket: $@\n";
1.1 albertel 989:
990: # --------------------------------------------------------- Do global variables
991:
992: # global variables
993:
1.134 albertel 994: my %children = (); # keys are current child process IDs
1.1 albertel 995:
996: sub REAPER { # takes care of dead children
997: $SIG{CHLD} = \&REAPER;
1.165 albertel 998: &status("Handling child death");
1.178 foxr 999: my $pid;
1000: do {
1001: $pid = waitpid(-1,&WNOHANG());
1002: if (defined($children{$pid})) {
1003: &logthis("Child $pid died");
1004: delete($children{$pid});
1.183 albertel 1005: } elsif ($pid > 0) {
1.178 foxr 1006: &logthis("Unknown Child $pid died");
1007: }
1008: } while ( $pid > 0 );
1009: foreach my $child (keys(%children)) {
1010: $pid = waitpid($child,&WNOHANG());
1011: if ($pid > 0) {
1012: &logthis("Child $child - $pid looks like we missed it's death");
1013: delete($children{$pid});
1014: }
1.176 albertel 1015: }
1.165 albertel 1016: &status("Finished Handling child death");
1.1 albertel 1017: }
1018:
1019: sub HUNTSMAN { # signal handler for SIGINT
1.165 albertel 1020: &status("Killing children (INT)");
1.1 albertel 1021: local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children
1022: kill 'INT' => keys %children;
1.59 www 1023: &logthis("Free socket: ".shutdown($server,2)); # free up socket
1.1 albertel 1024: my $execdir=$perlvar{'lonDaemons'};
1025: unlink("$execdir/logs/lond.pid");
1.190 albertel 1026: &logthis("<font color='red'>CRITICAL: Shutting down</font>");
1.165 albertel 1027: &status("Done killing children");
1.1 albertel 1028: exit; # clean up with dignity
1029: }
1030:
1031: sub HUPSMAN { # signal handler for SIGHUP
1032: local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children
1.165 albertel 1033: &status("Killing children for restart (HUP)");
1.1 albertel 1034: kill 'INT' => keys %children;
1.59 www 1035: &logthis("Free socket: ".shutdown($server,2)); # free up socket
1.190 albertel 1036: &logthis("<font color='red'>CRITICAL: Restarting</font>");
1.134 albertel 1037: my $execdir=$perlvar{'lonDaemons'};
1.30 harris41 1038: unlink("$execdir/logs/lond.pid");
1.165 albertel 1039: &status("Restarting self (HUP)");
1.1 albertel 1040: exec("$execdir/lond"); # here we go again
1041: }
1042:
1.144 foxr 1043: #
1.148 foxr 1044: # Kill off hashes that describe the host table prior to re-reading it.
1045: # Hashes affected are:
1.200 matthew 1046: # %hostid, %hostdom %hostip %hostdns.
1.148 foxr 1047: #
1048: sub KillHostHashes {
1049: foreach my $key (keys %hostid) {
1050: delete $hostid{$key};
1051: }
1052: foreach my $key (keys %hostdom) {
1053: delete $hostdom{$key};
1054: }
1055: foreach my $key (keys %hostip) {
1056: delete $hostip{$key};
1057: }
1.200 matthew 1058: foreach my $key (keys %hostdns) {
1059: delete $hostdns{$key};
1060: }
1.148 foxr 1061: }
1062: #
1063: # Read in the host table from file and distribute it into the various hashes:
1064: #
1065: # - %hostid - Indexed by IP, the loncapa hostname.
1066: # - %hostdom - Indexed by loncapa hostname, the domain.
1067: # - %hostip - Indexed by hostid, the Ip address of the host.
1068: sub ReadHostTable {
1069:
1070: open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";
1.200 matthew 1071: my $myloncapaname = $perlvar{'lonHostID'};
1072: Debug("My loncapa name is : $myloncapaname");
1.148 foxr 1073: while (my $configline=<CONFIG>) {
1.178 foxr 1074: if (!($configline =~ /^\s*\#/)) {
1075: my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);
1076: chomp($ip); $ip=~s/\D+$//;
1.200 matthew 1077: $hostid{$ip}=$id; # LonCAPA name of host by IP.
1078: $hostdom{$id}=$domain; # LonCAPA domain name of host.
1079: $hostip{$id}=$ip; # IP address of host.
1080: $hostdns{$name} = $id; # LonCAPA name of host by DNS.
1081:
1082: if ($id eq $perlvar{'lonHostID'}) {
1083: Debug("Found me in the host table: $name");
1084: $thisserver=$name;
1085: }
1.178 foxr 1086: }
1.148 foxr 1087: }
1088: close(CONFIG);
1089: }
1090: #
1091: # Reload the Apache daemon's state.
1.150 foxr 1092: # This is done by invoking /home/httpd/perl/apachereload
1093: # a setuid perl script that can be root for us to do this job.
1.148 foxr 1094: #
1095: sub ReloadApache {
1.150 foxr 1096: my $execdir = $perlvar{'lonDaemons'};
1097: my $script = $execdir."/apachereload";
1098: system($script);
1.148 foxr 1099: }
1100:
1101: #
1.144 foxr 1102: # Called in response to a USR2 signal.
1103: # - Reread hosts.tab
1104: # - All children connected to hosts that were removed from hosts.tab
1105: # are killed via SIGINT
1106: # - All children connected to previously existing hosts are sent SIGUSR1
1107: # - Our internal hosts hash is updated to reflect the new contents of
1108: # hosts.tab causing connections from hosts added to hosts.tab to
1109: # now be honored.
1110: #
1111: sub UpdateHosts {
1.165 albertel 1112: &status("Reload hosts.tab");
1.147 foxr 1113: logthis('<font color="blue"> Updating connections </font>');
1.148 foxr 1114: #
1115: # The %children hash has the set of IP's we currently have children
1116: # on. These need to be matched against records in the hosts.tab
1117: # Any ip's no longer in the table get killed off they correspond to
1118: # either dropped or changed hosts. Note that the re-read of the table
1119: # will take care of new and changed hosts as connections come into being.
1120:
1121:
1122: KillHostHashes;
1123: ReadHostTable;
1124:
1125: foreach my $child (keys %children) {
1126: my $childip = $children{$child};
1127: if(!$hostid{$childip}) {
1.149 foxr 1128: logthis('<font color="blue"> UpdateHosts killing child '
1129: ." $child for ip $childip </font>");
1.148 foxr 1130: kill('INT', $child);
1.149 foxr 1131: } else {
1132: logthis('<font color="green"> keeping child for ip '
1133: ." $childip (pid=$child) </font>");
1.148 foxr 1134: }
1135: }
1136: ReloadApache;
1.165 albertel 1137: &status("Finished reloading hosts.tab");
1.144 foxr 1138: }
1139:
1.148 foxr 1140:
1.57 www 1141: sub checkchildren {
1.165 albertel 1142: &status("Checking on the children (sending signals)");
1.57 www 1143: &initnewstatus();
1144: &logstatus();
1145: &logthis('Going to check on the children');
1.134 albertel 1146: my $docdir=$perlvar{'lonDocRoot'};
1.61 harris41 1147: foreach (sort keys %children) {
1.57 www 1148: sleep 1;
1149: unless (kill 'USR1' => $_) {
1150: &logthis ('Child '.$_.' is dead');
1151: &logstatus($$.' is dead');
1152: }
1.61 harris41 1153: }
1.63 www 1154: sleep 5;
1.113 albertel 1155: $SIG{ALRM} = sub { die "timeout" };
1156: $SIG{__DIE__} = 'DEFAULT';
1.165 albertel 1157: &status("Checking on the children (waiting for reports)");
1.63 www 1158: foreach (sort keys %children) {
1159: unless (-e "$docdir/lon-status/londchld/$_.txt") {
1.113 albertel 1160: eval {
1161: alarm(300);
1.63 www 1162: &logthis('Child '.$_.' did not respond');
1.67 albertel 1163: kill 9 => $_;
1.131 albertel 1164: #$emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
1165: #$subj="LON: $currenthostid killed lond process $_";
1166: #my $result=`echo 'Killed lond process $_.' | mailto $emailto -s '$subj' > /dev/null`;
1167: #$execdir=$perlvar{'lonDaemons'};
1168: #$result=`/bin/cp $execdir/logs/lond.log $execdir/logs/lond.log.$_`;
1.113 albertel 1169: alarm(0);
1170: }
1.63 www 1171: }
1172: }
1.113 albertel 1173: $SIG{ALRM} = 'DEFAULT';
1.155 albertel 1174: $SIG{__DIE__} = \&catchexception;
1.165 albertel 1175: &status("Finished checking children");
1.57 www 1176: }
1177:
1.1 albertel 1178: # --------------------------------------------------------------------- Logging
1179:
1180: sub logthis {
1181: my $message=shift;
1182: my $execdir=$perlvar{'lonDaemons'};
1183: my $fh=IO::File->new(">>$execdir/logs/lond.log");
1184: my $now=time;
1185: my $local=localtime($now);
1.58 www 1186: $lastlog=$local.': '.$message;
1.1 albertel 1187: print $fh "$local ($$): $message\n";
1188: }
1189:
1.77 foxr 1190: # ------------------------- Conditional log if $DEBUG true.
1191: sub Debug {
1192: my $message = shift;
1193: if($DEBUG) {
1194: &logthis($message);
1195: }
1196: }
1.161 foxr 1197:
1198: #
1199: # Sub to do replies to client.. this gives a hook for some
1200: # debug tracing too:
1201: # Parameters:
1202: # fd - File open on client.
1203: # reply - Text to send to client.
1204: # request - Original request from client.
1205: #
1206: sub Reply {
1.192 foxr 1207:
1208: my ($fd, $reply, $request) = @_;
1.161 foxr 1209:
1210: print $fd $reply;
1211: Debug("Request was $request Reply was $reply");
1212:
1213: }
1.57 www 1214: # ------------------------------------------------------------------ Log status
1215:
1216: sub logstatus {
1.178 foxr 1217: &status("Doing logging");
1218: my $docdir=$perlvar{'lonDocRoot'};
1219: {
1220: my $fh=IO::File->new(">>$docdir/lon-status/londstatus.txt");
1.200 matthew 1221: print $fh $$."\t".$clientname."\t".$currenthostid."\t"
1222: .$status."\t".$lastlog."\t $keymode\n";
1.178 foxr 1223: $fh->close();
1224: }
1225: &status("Finished londstatus.txt");
1226: {
1227: my $fh=IO::File->new(">$docdir/lon-status/londchld/$$.txt");
1.200 matthew 1228: print $fh $status."\n".$lastlog."\n".time."\n$keymode";
1.178 foxr 1229: $fh->close();
1230: }
1231: &status("Finished logging");
1.57 www 1232: }
1233:
1234: sub initnewstatus {
1235: my $docdir=$perlvar{'lonDocRoot'};
1236: my $fh=IO::File->new(">$docdir/lon-status/londstatus.txt");
1237: my $now=time;
1238: my $local=localtime($now);
1239: print $fh "LOND status $local - parent $$\n\n";
1.64 www 1240: opendir(DIR,"$docdir/lon-status/londchld");
1.134 albertel 1241: while (my $filename=readdir(DIR)) {
1.64 www 1242: unlink("$docdir/lon-status/londchld/$filename");
1243: }
1244: closedir(DIR);
1.57 www 1245: }
1246:
1247: # -------------------------------------------------------------- Status setting
1248:
1249: sub status {
1250: my $what=shift;
1251: my $now=time;
1252: my $local=localtime($now);
1.178 foxr 1253: $status=$local.': '.$what;
1254: $0='lond: '.$what.' '.$local;
1.57 www 1255: }
1.11 www 1256:
1257: # -------------------------------------------------------- Escape Special Chars
1258:
1259: sub escape {
1260: my $str=shift;
1261: $str =~ s/(\W)/"%".unpack('H2',$1)/eg;
1262: return $str;
1263: }
1264:
1265: # ----------------------------------------------------- Un-Escape Special Chars
1266:
1267: sub unescape {
1268: my $str=shift;
1269: $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1270: return $str;
1271: }
1272:
1.1 albertel 1273: # ----------------------------------------------------------- Send USR1 to lonc
1274:
1275: sub reconlonc {
1276: my $peerfile=shift;
1277: &logthis("Trying to reconnect for $peerfile");
1278: my $loncfile="$perlvar{'lonDaemons'}/logs/lonc.pid";
1279: if (my $fh=IO::File->new("$loncfile")) {
1280: my $loncpid=<$fh>;
1281: chomp($loncpid);
1282: if (kill 0 => $loncpid) {
1283: &logthis("lonc at pid $loncpid responding, sending USR1");
1284: kill USR1 => $loncpid;
1285: } else {
1.9 www 1286: &logthis(
1.190 albertel 1287: "<font color='red'>CRITICAL: "
1.9 www 1288: ."lonc at pid $loncpid not responding, giving up</font>");
1.1 albertel 1289: }
1290: } else {
1.190 albertel 1291: &logthis('<font color="red">CRITICAL: lonc not running, giving up</font>');
1.1 albertel 1292: }
1293: }
1294:
1295: # -------------------------------------------------- Non-critical communication
1.11 www 1296:
1.1 albertel 1297: sub subreply {
1298: my ($cmd,$server)=@_;
1299: my $peerfile="$perlvar{'lonSockDir'}/$server";
1300: my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile",
1301: Type => SOCK_STREAM,
1302: Timeout => 10)
1303: or return "con_lost";
1304: print $sclient "$cmd\n";
1305: my $answer=<$sclient>;
1306: chomp($answer);
1307: if (!$answer) { $answer="con_lost"; }
1308: return $answer;
1309: }
1310:
1311: sub reply {
1312: my ($cmd,$server)=@_;
1313: my $answer;
1.115 albertel 1314: if ($server ne $currenthostid) {
1.1 albertel 1315: $answer=subreply($cmd,$server);
1316: if ($answer eq 'con_lost') {
1317: $answer=subreply("ping",$server);
1318: if ($answer ne $server) {
1.115 albertel 1319: &logthis("sub reply: answer != server answer is $answer, server is $server");
1.1 albertel 1320: &reconlonc("$perlvar{'lonSockDir'}/$server");
1321: }
1322: $answer=subreply($cmd,$server);
1323: }
1324: } else {
1325: $answer='self_reply';
1326: }
1327: return $answer;
1328: }
1329:
1.13 www 1330: # -------------------------------------------------------------- Talk to lonsql
1331:
1.12 harris41 1332: sub sqlreply {
1333: my ($cmd)=@_;
1334: my $answer=subsqlreply($cmd);
1335: if ($answer eq 'con_lost') { $answer=subsqlreply($cmd); }
1336: return $answer;
1337: }
1338:
1339: sub subsqlreply {
1340: my ($cmd)=@_;
1341: my $unixsock="mysqlsock";
1342: my $peerfile="$perlvar{'lonSockDir'}/$unixsock";
1343: my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile",
1344: Type => SOCK_STREAM,
1345: Timeout => 10)
1346: or return "con_lost";
1347: print $sclient "$cmd\n";
1348: my $answer=<$sclient>;
1349: chomp($answer);
1350: if (!$answer) { $answer="con_lost"; }
1351: return $answer;
1352: }
1353:
1.1 albertel 1354: # -------------------------------------------- Return path to profile directory
1.11 www 1355:
1.1 albertel 1356: sub propath {
1357: my ($udom,$uname)=@_;
1358: $udom=~s/\W//g;
1359: $uname=~s/\W//g;
1.16 www 1360: my $subdir=$uname.'__';
1.1 albertel 1361: $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
1362: my $proname="$perlvar{'lonUsersDir'}/$udom/$subdir/$uname";
1363: return $proname;
1364: }
1365:
1366: # --------------------------------------- Is this the home server of an author?
1.11 www 1367:
1.1 albertel 1368: sub ishome {
1369: my $author=shift;
1370: $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
1371: my ($udom,$uname)=split(/\//,$author);
1372: my $proname=propath($udom,$uname);
1373: if (-e $proname) {
1374: return 'owner';
1375: } else {
1376: return 'not_owner';
1377: }
1378: }
1379:
1380: # ======================================================= Continue main program
1381: # ---------------------------------------------------- Fork once and dissociate
1382:
1.134 albertel 1383: my $fpid=fork;
1.1 albertel 1384: exit if $fpid;
1.29 harris41 1385: die "Couldn't fork: $!" unless defined ($fpid);
1.1 albertel 1386:
1.29 harris41 1387: POSIX::setsid() or die "Can't start new session: $!";
1.1 albertel 1388:
1389: # ------------------------------------------------------- Write our PID on disk
1390:
1.134 albertel 1391: my $execdir=$perlvar{'lonDaemons'};
1.1 albertel 1392: open (PIDSAVE,">$execdir/logs/lond.pid");
1393: print PIDSAVE "$$\n";
1394: close(PIDSAVE);
1.190 albertel 1395: &logthis("<font color='red'>CRITICAL: ---------- Starting ----------</font>");
1.57 www 1396: &status('Starting');
1.1 albertel 1397:
1.106 foxr 1398:
1.1 albertel 1399:
1400: # ----------------------------------------------------- Install signal handlers
1401:
1.57 www 1402:
1.1 albertel 1403: $SIG{CHLD} = \&REAPER;
1404: $SIG{INT} = $SIG{TERM} = \&HUNTSMAN;
1405: $SIG{HUP} = \&HUPSMAN;
1.57 www 1406: $SIG{USR1} = \&checkchildren;
1.144 foxr 1407: $SIG{USR2} = \&UpdateHosts;
1.106 foxr 1408:
1.148 foxr 1409: # Read the host hashes:
1410:
1411: ReadHostTable;
1.106 foxr 1412:
1413: # --------------------------------------------------------------
1414: # Accept connections. When a connection comes in, it is validated
1415: # and if good, a child process is created to process transactions
1416: # along the connection.
1417:
1.1 albertel 1418: while (1) {
1.165 albertel 1419: &status('Starting accept');
1.106 foxr 1420: $client = $server->accept() or next;
1.165 albertel 1421: &status('Accepted '.$client.' off to spawn');
1.106 foxr 1422: make_new_child($client);
1.165 albertel 1423: &status('Finished spawning');
1.1 albertel 1424: }
1425:
1426: sub make_new_child {
1427: my $pid;
1.178 foxr 1428: my $cipher;
1.1 albertel 1429: my $sigset;
1.106 foxr 1430:
1431: $client = shift;
1.165 albertel 1432: &status('Starting new child '.$client);
1.161 foxr 1433: &logthis('<font color="green"> Attempting to start child ('.$client.
1434: ")</font>");
1.1 albertel 1435: # block signal for fork
1436: $sigset = POSIX::SigSet->new(SIGINT);
1437: sigprocmask(SIG_BLOCK, $sigset)
1.29 harris41 1438: or die "Can't block SIGINT for fork: $!\n";
1.134 albertel 1439:
1.29 harris41 1440: die "fork: $!" unless defined ($pid = fork);
1.148 foxr 1441:
1442: $client->sockopt(SO_KEEPALIVE, 1); # Enable monitoring of
1443: # connection liveness.
1444:
1445: #
1446: # Figure out who we're talking to so we can record the peer in
1447: # the pid hash.
1448: #
1449: my $caller = getpeername($client);
1.180 albertel 1450: my ($port,$iaddr);
1451: if (defined($caller) && length($caller) > 0) {
1452: ($port,$iaddr)=unpack_sockaddr_in($caller);
1453: } else {
1454: &logthis("Unable to determine who caller was, getpeername returned nothing");
1455: }
1456: if (defined($iaddr)) {
1.200 matthew 1457: $clientip = inet_ntoa($iaddr);
1458: Debug("Connected with $clientip");
1459: $clientdns = gethostbyaddr($iaddr, AF_INET);
1460: Debug("Connected with $clientdns by name");
1.180 albertel 1461: } else {
1.200 matthew 1462: &logthis("Unable to determine clientip");
1.180 albertel 1463: $clientip='Unavailable';
1464: }
1.1 albertel 1465:
1466: if ($pid) {
1467: # Parent records the child's birth and returns.
1468: sigprocmask(SIG_UNBLOCK, $sigset)
1.29 harris41 1469: or die "Can't unblock SIGINT for fork: $!\n";
1.148 foxr 1470: $children{$pid} = $clientip;
1.57 www 1471: &status('Started child '.$pid);
1.1 albertel 1472: return;
1473: } else {
1474: # Child can *not* return from this subroutine.
1475: $SIG{INT} = 'DEFAULT'; # make SIGINT kill us as it did before
1.126 albertel 1476: $SIG{CHLD} = 'DEFAULT'; #make this default so that pwauth returns
1477: #don't get intercepted
1.57 www 1478: $SIG{USR1}= \&logstatus;
1.63 www 1479: $SIG{ALRM}= \&timeout;
1.57 www 1480: $lastlog='Forked ';
1481: $status='Forked';
1482:
1.1 albertel 1483: # unblock signals
1484: sigprocmask(SIG_UNBLOCK, $sigset)
1.29 harris41 1485: or die "Can't unblock SIGINT for fork: $!\n";
1.13 www 1486:
1.178 foxr 1487: my $tmpsnum=0;
1488: #---------------------------------------------------- kerberos 5 initialization
1.91 albertel 1489: &Authen::Krb5::init_context();
1490: &Authen::Krb5::init_ets();
1491:
1.161 foxr 1492: &status('Accepted connection');
1.1 albertel 1493: # =============================================================================
1494: # do something with the connection
1495: # -----------------------------------------------------------------------------
1.200 matthew 1496: # see if we know client and 'check' for spoof IP by ineffective challenge
1.148 foxr 1497:
1.161 foxr 1498: ReadManagerTable; # May also be a manager!!
1499:
1500: my $clientrec=($hostid{$clientip} ne undef);
1501: my $ismanager=($managers{$clientip} ne undef);
1502: $clientname = "[unknonwn]";
1503: if($clientrec) { # Establish client type.
1504: $ConnectionType = "client";
1505: $clientname = $hostid{$clientip};
1506: if($ismanager) {
1507: $ConnectionType = "both";
1508: }
1509: } else {
1510: $ConnectionType = "manager";
1511: $clientname = $managers{$clientip};
1512: }
1513: my $clientok;
1.200 matthew 1514:
1.161 foxr 1515: if ($clientrec || $ismanager) {
1516: &status("Waiting for init from $clientip $clientname");
1517: &logthis('<font color="yellow">INFO: Connection, '.
1518: $clientip.
1519: " ($clientname) connection type = $ConnectionType </font>" );
1520: &status("Connecting $clientip ($clientname))");
1521: my $remotereq=<$client>;
1.200 matthew 1522: chomp($remotereq);
1523: Debug("Got init: $remotereq");
1524: my $inikeyword = split(/:/, $remotereq);
1.161 foxr 1525: if ($remotereq =~ /^init/) {
1526: &sethost("sethost:$perlvar{'lonHostID'}");
1.200 matthew 1527: #
1528: # If the remote is attempting a local init... give that a try:
1529: #
1530: my ($i, $inittype) = split(/:/, $remotereq);
1531:
1532: # If the connection type is ssl, but I didn't get my
1533: # certificate files yet, then I'll drop back to
1534: # insecure (if allowed).
1535:
1536: if($inittype eq "ssl") {
1537: my ($ca, $cert) = lonssl::CertificateFile;
1538: my $kfile = lonssl::KeyFile;
1539: if((!$ca) ||
1540: (!$cert) ||
1541: (!$kfile)) {
1542: $inittype = ""; # This forces insecure attempt.
1543: &logthis("<font color=\"blue\"> Certificates not "
1544: ."installed -- trying insecure auth</font>");
1545: }
1546: else { # SSL certificates are in place so
1547: } # Leave the inittype alone.
1548: }
1549:
1550: if($inittype eq "local") {
1551: my $key = LocalConnection($client, $remotereq);
1552: if($key) {
1553: Debug("Got local key $key");
1554: $clientok = 1;
1555: my $cipherkey = pack("H32", $key);
1556: $cipher = new IDEA($cipherkey);
1557: print $client "ok:local\n";
1558: &logthis('<font color="green"'
1559: . "Successful local authentication </font>");
1560: $keymode = "local"
1561: } else {
1562: Debug("Failed to get local key");
1563: $clientok = 0;
1564: shutdown($client, 3);
1565: close $client;
1566: }
1567: } elsif ($inittype eq "ssl") {
1568: my $key = SSLConnection($client);
1569: if ($key) {
1570: $clientok = 1;
1571: my $cipherkey = pack("H32", $key);
1572: $cipher = new IDEA($cipherkey);
1573: &logthis('<font color="green">'
1574: ."Successfull ssl authentication with $clientname </font>");
1575: $keymode = "ssl";
1576:
1577: } else {
1578: $clientok = 0;
1579: close $client;
1580: }
1581:
1.161 foxr 1582: } else {
1.200 matthew 1583: my $ok = InsecureConnection($client);
1584: if($ok) {
1585: $clientok = 1;
1586: &logthis('<font color="green">'
1587: ."Successful insecure authentication with $clientname </font>");
1588: print $client "ok\n";
1589: $keymode = "insecure";
1590: } else {
1591: &logthis('<font color="yellow">'
1592: ."Attempted insecure connection disallowed </font>");
1593: close $client;
1594: $clientok = 0;
1595:
1596: }
1.161 foxr 1597: }
1.2 www 1598: } else {
1.161 foxr 1599: &logthis(
1.190 albertel 1600: "<font color='blue'>WARNING: "
1.161 foxr 1601: ."$clientip failed to initialize: >$remotereq< </font>");
1602: &status('No init '.$clientip);
1603: }
1.200 matthew 1604:
1.161 foxr 1605: } else {
1606: &logthis(
1.190 albertel 1607: "<font color='blue'>WARNING: Unknown client $clientip</font>");
1.161 foxr 1608: &status('Hung up on '.$clientip);
1609: }
1.200 matthew 1610:
1.161 foxr 1611: if ($clientok) {
1.1 albertel 1612: # ---------------- New known client connecting, could mean machine online again
1.161 foxr 1613:
1614: foreach my $id (keys(%hostip)) {
1615: if ($hostip{$id} ne $clientip ||
1616: $hostip{$currenthostid} eq $clientip) {
1617: # no need to try to do recon's to myself
1618: next;
1.115 albertel 1619: }
1.161 foxr 1620: &reconlonc("$perlvar{'lonSockDir'}/$id");
1621: }
1.190 albertel 1622: &logthis("<font color='green'>Established connection: $clientname</font>");
1.161 foxr 1623: &status('Will listen to '.$clientname);
1.178 foxr 1624: # ------------------------------------------------------------ Process requests
1625: while (my $userinput=<$client>) {
1626: chomp($userinput);
1627: Debug("Request = $userinput\n");
1628: &status('Processing '.$clientname.': '.$userinput);
1629: my $wasenc=0;
1630: alarm(120);
1631: # ------------------------------------------------------------ See if encrypted
1632: if ($userinput =~ /^enc/) {
1633: if ($cipher) {
1634: my ($cmd,$cmdlength,$encinput)=split(/:/,$userinput);
1635: $userinput='';
1636: for (my $encidx=0;$encidx<length($encinput);$encidx+=16) {
1637: $userinput.=
1638: $cipher->decrypt(
1639: pack("H16",substr($encinput,$encidx,16))
1640: );
1641: }
1642: $userinput=substr($userinput,0,$cmdlength);
1643: $wasenc=1;
1644: }
1645: }
1646:
1647: # ------------------------------------------------------------- Normal commands
1648: # ------------------------------------------------------------------------ ping
1649: if ($userinput =~ /^ping/) { # client only
1650: if(isClient) {
1651: print $client "$currenthostid\n";
1652: } else {
1653: Reply($client, "refused\n", $userinput);
1654: }
1655: # ------------------------------------------------------------------------ pong
1656: }elsif ($userinput =~ /^pong/) { # client only
1657: if(isClient) {
1658: my $reply=&reply("ping",$clientname);
1659: print $client "$currenthostid:$reply\n";
1660: } else {
1661: Reply($client, "refused\n", $userinput);
1662: }
1663: # ------------------------------------------------------------------------ ekey
1664: } elsif ($userinput =~ /^ekey/) { # ok for both clients & mgrs
1665: my $buildkey=time.$$.int(rand 100000);
1666: $buildkey=~tr/1-6/A-F/;
1667: $buildkey=int(rand 100000).$buildkey.int(rand 100000);
1668: my $key=$currenthostid.$clientname;
1669: $key=~tr/a-z/A-Z/;
1670: $key=~tr/G-P/0-9/;
1671: $key=~tr/Q-Z/0-9/;
1672: $key=$key.$buildkey.$key.$buildkey.$key.$buildkey;
1673: $key=substr($key,0,32);
1674: my $cipherkey=pack("H32",$key);
1675: $cipher=new IDEA $cipherkey;
1676: print $client "$buildkey\n";
1677: # ------------------------------------------------------------------------ load
1678: } elsif ($userinput =~ /^load/) { # client only
1679: if (isClient) {
1680: my $loadavg;
1681: {
1682: my $loadfile=IO::File->new('/proc/loadavg');
1683: $loadavg=<$loadfile>;
1684: }
1685: $loadavg =~ s/\s.*//g;
1686: my $loadpercent=100*$loadavg/$perlvar{'lonLoadLim'};
1687: print $client "$loadpercent\n";
1688: } else {
1689: Reply($client, "refused\n", $userinput);
1690:
1691: }
1692: # -------------------------------------------------------------------- userload
1693: } elsif ($userinput =~ /^userload/) { # client only
1694: if(isClient) {
1695: my $userloadpercent=&userload();
1696: print $client "$userloadpercent\n";
1697: } else {
1698: Reply($client, "refused\n", $userinput);
1699:
1700: }
1701: #
1702: # Transactions requiring encryption:
1703: #
1704: # ----------------------------------------------------------------- currentauth
1705: } elsif ($userinput =~ /^currentauth/) {
1706: if (($wasenc==1) && isClient) { # Encoded & client only.
1707: my ($cmd,$udom,$uname)=split(/:/,$userinput);
1708: my $result = GetAuthType($udom, $uname);
1709: if($result eq "nouser") {
1710: print $client "unknown_user\n";
1711: }
1712: else {
1713: print $client "$result\n"
1714: }
1715: } else {
1716: Reply($client, "refused\n", $userinput);
1717:
1718: }
1719: #--------------------------------------------------------------------- pushfile
1720: } elsif($userinput =~ /^pushfile/) { # encoded & manager.
1721: if(($wasenc == 1) && isManager) {
1722: my $cert = GetCertificate($userinput);
1723: if(ValidManager($cert)) {
1724: my $reply = PushFile($userinput);
1725: print $client "$reply\n";
1726: } else {
1727: print $client "refused\n";
1728: }
1729: } else {
1730: Reply($client, "refused\n", $userinput);
1731:
1732: }
1733: #--------------------------------------------------------------------- reinit
1734: } elsif($userinput =~ /^reinit/) { # Encoded and manager
1735: if (($wasenc == 1) && isManager) {
1736: my $cert = GetCertificate($userinput);
1737: if(ValidManager($cert)) {
1738: chomp($userinput);
1739: my $reply = ReinitProcess($userinput);
1740: print $client "$reply\n";
1741: } else {
1742: print $client "refused\n";
1743: }
1744: } else {
1745: Reply($client, "refused\n", $userinput);
1746: }
1747: #------------------------------------------------------------------------- edit
1748: } elsif ($userinput =~ /^edit/) { # encoded and manager:
1749: if(($wasenc ==1) && (isManager)) {
1750: my $cert = GetCertificate($userinput);
1751: if(ValidManager($cert)) {
1752: my($command, $filetype, $script) = split(/:/, $userinput);
1753: if (($filetype eq "hosts") || ($filetype eq "domain")) {
1754: if($script ne "") {
1755: Reply($client, EditFile($userinput));
1756: } else {
1757: Reply($client,"refused\n",$userinput);
1758: }
1759: } else {
1760: Reply($client,"refused\n",$userinput);
1761: }
1762: } else {
1763: Reply($client,"refused\n",$userinput);
1764: }
1765: } else {
1766: Reply($client,"refused\n",$userinput);
1767: }
1768: # ------------------------------------------------------------------------ auth
1769: } elsif ($userinput =~ /^auth/) { # Encoded and client only.
1770: if (($wasenc==1) && isClient) {
1771: my ($cmd,$udom,$uname,$upass)=split(/:/,$userinput);
1772: chomp($upass);
1773: $upass=unescape($upass);
1774: my $proname=propath($udom,$uname);
1775: my $passfilename="$proname/passwd";
1776: if (-e $passfilename) {
1777: my $pf = IO::File->new($passfilename);
1778: my $realpasswd=<$pf>;
1779: chomp($realpasswd);
1780: my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
1781: my $pwdcorrect=0;
1782: if ($howpwd eq 'internal') {
1783: &Debug("Internal auth");
1784: $pwdcorrect=
1785: (crypt($upass,$contentpwd) eq $contentpwd);
1786: } elsif ($howpwd eq 'unix') {
1787: &Debug("Unix auth");
1788: if((getpwnam($uname))[1] eq "") { #no such user!
1789: $pwdcorrect = 0;
1790: } else {
1791: $contentpwd=(getpwnam($uname))[1];
1792: my $pwauth_path="/usr/local/sbin/pwauth";
1793: unless ($contentpwd eq 'x') {
1794: $pwdcorrect=
1795: (crypt($upass,$contentpwd) eq
1796: $contentpwd);
1797: }
1798:
1799: elsif (-e $pwauth_path) {
1800: open PWAUTH, "|$pwauth_path" or
1801: die "Cannot invoke authentication";
1802: print PWAUTH "$uname\n$upass\n";
1803: close PWAUTH;
1804: $pwdcorrect=!$?;
1805: }
1806: }
1807: } elsif ($howpwd eq 'krb4') {
1808: my $null=pack("C",0);
1809: unless ($upass=~/$null/) {
1810: my $krb4_error = &Authen::Krb4::get_pw_in_tkt
1811: ($uname,"",$contentpwd,'krbtgt',
1812: $contentpwd,1,$upass);
1813: if (!$krb4_error) {
1814: $pwdcorrect = 1;
1815: } else {
1816: $pwdcorrect=0;
1817: # log error if it is not a bad password
1818: if ($krb4_error != 62) {
1.191 albertel 1819: &logthis('krb4:'.$uname.','.
1.178 foxr 1820: &Authen::Krb4::get_err_txt($Authen::Krb4::error));
1821: }
1822: }
1823: }
1824: } elsif ($howpwd eq 'krb5') {
1825: my $null=pack("C",0);
1826: unless ($upass=~/$null/) {
1827: my $krbclient=&Authen::Krb5::parse_name($uname.'@'.$contentpwd);
1828: my $krbservice="krbtgt/".$contentpwd."\@".$contentpwd;
1829: my $krbserver=&Authen::Krb5::parse_name($krbservice);
1830: my $credentials=&Authen::Krb5::cc_default();
1831: $credentials->initialize($krbclient);
1832: my $krbreturn =
1833: &Authen::Krb5::get_in_tkt_with_password(
1834: $krbclient,$krbserver,$upass,$credentials);
1835: # unless ($krbreturn) {
1836: # &logthis("Krb5 Error: ".
1837: # &Authen::Krb5::error());
1838: # }
1839: $pwdcorrect = ($krbreturn == 1);
1840: } else { $pwdcorrect=0; }
1841: } elsif ($howpwd eq 'localauth') {
1842: $pwdcorrect=&localauth::localauth($uname,$upass,
1843: $contentpwd);
1844: }
1845: if ($pwdcorrect) {
1846: print $client "authorized\n";
1847: } else {
1848: print $client "non_authorized\n";
1849: }
1850: } else {
1851: print $client "unknown_user\n";
1852: }
1853: } else {
1854: Reply($client, "refused\n", $userinput);
1855:
1856: }
1857: # ---------------------------------------------------------------------- passwd
1858: } elsif ($userinput =~ /^passwd/) { # encoded and client
1859: if (($wasenc==1) && isClient) {
1860: my
1861: ($cmd,$udom,$uname,$upass,$npass)=split(/:/,$userinput);
1862: chomp($npass);
1863: $upass=&unescape($upass);
1864: $npass=&unescape($npass);
1865: &Debug("Trying to change password for $uname");
1866: my $proname=propath($udom,$uname);
1867: my $passfilename="$proname/passwd";
1868: if (-e $passfilename) {
1869: my $realpasswd;
1870: { my $pf = IO::File->new($passfilename);
1871: $realpasswd=<$pf>; }
1872: chomp($realpasswd);
1873: my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
1874: if ($howpwd eq 'internal') {
1875: &Debug("internal auth");
1876: if (crypt($upass,$contentpwd) eq $contentpwd) {
1877: my $salt=time;
1878: $salt=substr($salt,6,2);
1879: my $ncpass=crypt($npass,$salt);
1880: {
1881: my $pf;
1882: if ($pf = IO::File->new(">$passfilename")) {
1883: print $pf "internal:$ncpass\n";
1884: &logthis("Result of password change for $uname: pwchange_success");
1885: print $client "ok\n";
1886: } else {
1887: &logthis("Unable to open $uname passwd to change password");
1888: print $client "non_authorized\n";
1889: }
1890: }
1891:
1892: } else {
1893: print $client "non_authorized\n";
1894: }
1895: } elsif ($howpwd eq 'unix') {
1896: # Unix means we have to access /etc/password
1897: # one way or another.
1898: # First: Make sure the current password is
1899: # correct
1900: &Debug("auth is unix");
1901: $contentpwd=(getpwnam($uname))[1];
1902: my $pwdcorrect = "0";
1903: my $pwauth_path="/usr/local/sbin/pwauth";
1904: unless ($contentpwd eq 'x') {
1905: $pwdcorrect=
1906: (crypt($upass,$contentpwd) eq $contentpwd);
1907: } elsif (-e $pwauth_path) {
1908: open PWAUTH, "|$pwauth_path" or
1909: die "Cannot invoke authentication";
1910: print PWAUTH "$uname\n$upass\n";
1911: close PWAUTH;
1912: &Debug("exited pwauth with $? ($uname,$upass) ");
1913: $pwdcorrect=($? == 0);
1914: }
1915: if ($pwdcorrect) {
1916: my $execdir=$perlvar{'lonDaemons'};
1917: &Debug("Opening lcpasswd pipeline");
1918: my $pf = IO::File->new("|$execdir/lcpasswd > $perlvar{'lonDaemons'}/logs/lcpasswd.log");
1919: print $pf "$uname\n$npass\n$npass\n";
1920: close $pf;
1921: my $err = $?;
1922: my $result = ($err>0 ? 'pwchange_failure'
1923: : 'ok');
1924: &logthis("Result of password change for $uname: ".
1925: &lcpasswdstrerror($?));
1926: print $client "$result\n";
1927: } else {
1928: print $client "non_authorized\n";
1929: }
1930: } else {
1931: print $client "auth_mode_error\n";
1932: }
1933: } else {
1934: print $client "unknown_user\n";
1935: }
1936: } else {
1937: Reply($client, "refused\n", $userinput);
1938:
1939: }
1940: # -------------------------------------------------------------------- makeuser
1941: } elsif ($userinput =~ /^makeuser/) { # encoded and client.
1942: &Debug("Make user received");
1943: my $oldumask=umask(0077);
1944: if (($wasenc==1) && isClient) {
1945: my
1946: ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
1947: &Debug("cmd =".$cmd." $udom =".$udom.
1948: " uname=".$uname);
1949: chomp($npass);
1950: $npass=&unescape($npass);
1951: my $proname=propath($udom,$uname);
1952: my $passfilename="$proname/passwd";
1953: &Debug("Password file created will be:".
1954: $passfilename);
1955: if (-e $passfilename) {
1956: print $client "already_exists\n";
1957: } elsif ($udom ne $currentdomainid) {
1958: print $client "not_right_domain\n";
1959: } else {
1960: my @fpparts=split(/\//,$proname);
1961: my $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2];
1962: my $fperror='';
1963: for (my $i=3;$i<=$#fpparts;$i++) {
1964: $fpnow.='/'.$fpparts[$i];
1965: unless (-e $fpnow) {
1966: unless (mkdir($fpnow,0777)) {
1967: $fperror="error: ".($!+0)
1968: ." mkdir failed while attempting "
1969: ."makeuser";
1970: }
1971: }
1972: }
1973: unless ($fperror) {
1974: my $result=&make_passwd_file($uname, $umode,$npass,
1975: $passfilename);
1976: print $client $result;
1977: } else {
1978: print $client "$fperror\n";
1979: }
1980: }
1981: } else {
1982: Reply($client, "refused\n", $userinput);
1983:
1984: }
1985: umask($oldumask);
1986: # -------------------------------------------------------------- changeuserauth
1987: } elsif ($userinput =~ /^changeuserauth/) { # encoded & client
1988: &Debug("Changing authorization");
1989: if (($wasenc==1) && isClient) {
1990: my
1991: ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
1992: chomp($npass);
1993: &Debug("cmd = ".$cmd." domain= ".$udom.
1994: "uname =".$uname." umode= ".$umode);
1995: $npass=&unescape($npass);
1996: my $proname=&propath($udom,$uname);
1997: my $passfilename="$proname/passwd";
1998: if ($udom ne $currentdomainid) {
1999: print $client "not_right_domain\n";
2000: } else {
2001: my $result=&make_passwd_file($uname, $umode,$npass,
2002: $passfilename);
2003: print $client $result;
2004: }
2005: } else {
2006: Reply($client, "refused\n", $userinput);
2007:
2008: }
2009: # ------------------------------------------------------------------------ home
2010: } elsif ($userinput =~ /^home/) { # client clear or encoded
2011: if(isClient) {
2012: my ($cmd,$udom,$uname)=split(/:/,$userinput);
2013: chomp($uname);
2014: my $proname=propath($udom,$uname);
2015: if (-e $proname) {
2016: print $client "found\n";
2017: } else {
2018: print $client "not_found\n";
2019: }
2020: } else {
2021: Reply($client, "refused\n", $userinput);
2022:
2023: }
2024: # ---------------------------------------------------------------------- update
2025: } elsif ($userinput =~ /^update/) { # client clear or encoded.
2026: if(isClient) {
2027: my ($cmd,$fname)=split(/:/,$userinput);
2028: my $ownership=ishome($fname);
2029: if ($ownership eq 'not_owner') {
2030: if (-e $fname) {
2031: my ($dev,$ino,$mode,$nlink,
2032: $uid,$gid,$rdev,$size,
2033: $atime,$mtime,$ctime,
2034: $blksize,$blocks)=stat($fname);
2035: my $now=time;
2036: my $since=$now-$atime;
2037: if ($since>$perlvar{'lonExpire'}) {
2038: my $reply=
2039: &reply("unsub:$fname","$clientname");
2040: unlink("$fname");
2041: } else {
2042: my $transname="$fname.in.transfer";
2043: my $remoteurl=
2044: &reply("sub:$fname","$clientname");
2045: my $response;
2046: {
2047: my $ua=new LWP::UserAgent;
2048: my $request=new HTTP::Request('GET',"$remoteurl");
2049: $response=$ua->request($request,$transname);
2050: }
2051: if ($response->is_error()) {
2052: unlink($transname);
2053: my $message=$response->status_line;
2054: &logthis(
2055: "LWP GET: $message for $fname ($remoteurl)");
2056: } else {
2057: if ($remoteurl!~/\.meta$/) {
2058: my $ua=new LWP::UserAgent;
2059: my $mrequest=
2060: new HTTP::Request('GET',$remoteurl.'.meta');
2061: my $mresponse=
2062: $ua->request($mrequest,$fname.'.meta');
2063: if ($mresponse->is_error()) {
2064: unlink($fname.'.meta');
2065: }
2066: }
2067: rename($transname,$fname);
2068: }
2069: }
2070: print $client "ok\n";
2071: } else {
2072: print $client "not_found\n";
2073: }
2074: } else {
2075: print $client "rejected\n";
2076: }
2077: } else {
2078: Reply($client, "refused\n", $userinput);
2079:
2080: }
2081: # -------------------------------------- fetch a user file from a remote server
2082: } elsif ($userinput =~ /^fetchuserfile/) { # Client clear or enc.
2083: if(isClient) {
1.184 raeburn 2084: my ($cmd,$fname)=split(/:/,$userinput);
1.185 albertel 2085: my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|);
1.178 foxr 2086: my $udir=propath($udom,$uname).'/userfiles';
2087: unless (-e $udir) { mkdir($udir,0770); }
2088: if (-e $udir) {
1.184 raeburn 2089: $ufile=~s/^[\.\~]+//;
2090: my $path = $udir;
1.185 albertel 2091: if ($ufile =~m|(.+)/([^/]+)$|) {
2092: my @parts=split('/',$1);
1.184 raeburn 2093: foreach my $part (@parts) {
2094: $path .= '/'.$part;
2095: if ((-e $path)!=1) {
2096: mkdir($path,0770);
1.182 raeburn 2097: }
2098: }
2099: }
1.184 raeburn 2100: my $destname=$udir.'/'.$ufile;
2101: my $transname=$udir.'/'.$ufile.'.in.transit';
2102: my $remoteurl='http://'.$clientip.'/userfiles/'.$fname;
1.178 foxr 2103: my $response;
2104: {
2105: my $ua=new LWP::UserAgent;
2106: my $request=new HTTP::Request('GET',"$remoteurl");
2107: $response=$ua->request($request,$transname);
2108: }
2109: if ($response->is_error()) {
2110: unlink($transname);
2111: my $message=$response->status_line;
1.184 raeburn 2112: &logthis("LWP GET: $message for $fname ($remoteurl)");
1.178 foxr 2113: print $client "failed\n";
2114: } else {
2115: if (!rename($transname,$destname)) {
2116: &logthis("Unable to move $transname to $destname");
2117: unlink($transname);
2118: print $client "failed\n";
2119: } else {
2120: print $client "ok\n";
2121: }
2122: }
2123: } else {
2124: print $client "not_home\n";
1.187 albertel 2125: }
2126: } else {
2127: Reply($client, "refused\n", $userinput);
2128: }
2129: # --------------------------------------------------------- remove a user file
2130: } elsif ($userinput =~ /^removeuserfile/) { # Client clear or enc.
2131: if(isClient) {
2132: my ($cmd,$fname)=split(/:/,$userinput);
2133: my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|);
2134: &logthis("$udom - $uname - $ufile");
2135: if ($ufile =~m|/\.\./|) {
2136: # any files paths with /../ in them refuse
2137: # to deal with
2138: print $client "refused\n";
2139: } else {
2140: my $udir=propath($udom,$uname);
2141: if (-e $udir) {
2142: my $file=$udir.'/userfiles/'.$ufile;
2143: if (-e $file) {
2144: unlink($file);
2145: if (-e $file) {
2146: print $client "failed\n";
2147: } else {
2148: print $client "ok\n";
2149: }
2150: } else {
2151: print $client "not_found\n";
2152: }
2153: } else {
2154: print $client "not_home\n";
2155: }
1.178 foxr 2156: }
2157: } else {
2158: Reply($client, "refused\n", $userinput);
2159: }
2160: # ------------------------------------------ authenticate access to a user file
2161: } elsif ($userinput =~ /^tokenauthuserfile/) { # Client only
2162: if(isClient) {
2163: my ($cmd,$fname,$session)=split(/:/,$userinput);
2164: chomp($session);
2165: my $reply='non_auth';
2166: if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'.
2167: $session.'.id')) {
2168: while (my $line=<ENVIN>) {
1.185 albertel 2169: if ($line=~ m|userfile\.\Q$fname\E\=|) { $reply='ok'; }
1.178 foxr 2170: }
2171: close(ENVIN);
2172: print $client $reply."\n";
2173: } else {
2174: print $client "invalid_token\n";
2175: }
2176: } else {
2177: Reply($client, "refused\n", $userinput);
2178:
2179: }
2180: # ----------------------------------------------------------------- unsubscribe
2181: } elsif ($userinput =~ /^unsub/) {
2182: if(isClient) {
2183: my ($cmd,$fname)=split(/:/,$userinput);
2184: if (-e $fname) {
1.188 foxr 2185: print $client &unsub($fname,$clientip);
1.178 foxr 2186: } else {
2187: print $client "not_found\n";
2188: }
2189: } else {
2190: Reply($client, "refused\n", $userinput);
2191:
2192: }
2193: # ------------------------------------------------------------------- subscribe
2194: } elsif ($userinput =~ /^sub/) {
2195: if(isClient) {
2196: print $client &subscribe($userinput,$clientip);
2197: } else {
2198: Reply($client, "refused\n", $userinput);
2199:
2200: }
2201: # ------------------------------------------------------------- current version
2202: } elsif ($userinput =~ /^currentversion/) {
2203: if(isClient) {
2204: my ($cmd,$fname)=split(/:/,$userinput);
2205: print $client ¤tversion($fname)."\n";
2206: } else {
2207: Reply($client, "refused\n", $userinput);
2208:
2209: }
2210: # ------------------------------------------------------------------------- log
2211: } elsif ($userinput =~ /^log/) {
2212: if(isClient) {
2213: my ($cmd,$udom,$uname,$what)=split(/:/,$userinput);
2214: chomp($what);
2215: my $proname=propath($udom,$uname);
2216: my $now=time;
2217: {
2218: my $hfh;
2219: if ($hfh=IO::File->new(">>$proname/activity.log")) {
2220: print $hfh "$now:$clientname:$what\n";
2221: print $client "ok\n";
2222: } else {
2223: print $client "error: ".($!+0)
2224: ." IO::File->new Failed "
2225: ."while attempting log\n";
2226: }
2227: }
2228: } else {
2229: Reply($client, "refused\n", $userinput);
2230:
2231: }
2232: # ------------------------------------------------------------------------- put
2233: } elsif ($userinput =~ /^put/) {
2234: if(isClient) {
2235: my ($cmd,$udom,$uname,$namespace,$what)
2236: =split(/:/,$userinput);
2237: $namespace=~s/\//\_/g;
2238: $namespace=~s/\W//g;
2239: if ($namespace ne 'roles') {
2240: chomp($what);
2241: my $proname=propath($udom,$uname);
2242: my $now=time;
2243: unless ($namespace=~/^nohist\_/) {
2244: my $hfh;
2245: if (
2246: $hfh=IO::File->new(">>$proname/$namespace.hist")
2247: ) { print $hfh "P:$now:$what\n"; }
2248: }
2249: my @pairs=split(/\&/,$what);
2250: my %hash;
2251: if (tie(%hash,'GDBM_File',
2252: "$proname/$namespace.db",
2253: &GDBM_WRCREAT(),0640)) {
2254: foreach my $pair (@pairs) {
2255: my ($key,$value)=split(/=/,$pair);
2256: $hash{$key}=$value;
2257: }
2258: if (untie(%hash)) {
2259: print $client "ok\n";
2260: } else {
2261: print $client "error: ".($!+0)
2262: ." untie(GDBM) failed ".
2263: "while attempting put\n";
2264: }
2265: } else {
2266: print $client "error: ".($!)
2267: ." tie(GDBM) Failed ".
2268: "while attempting put\n";
2269: }
2270: } else {
2271: print $client "refused\n";
2272: }
2273: } else {
2274: Reply($client, "refused\n", $userinput);
2275:
2276: }
2277: # ------------------------------------------------------------------- inc
2278: } elsif ($userinput =~ /^inc:/) {
2279: if(isClient) {
2280: my ($cmd,$udom,$uname,$namespace,$what)
2281: =split(/:/,$userinput);
2282: $namespace=~s/\//\_/g;
2283: $namespace=~s/\W//g;
2284: if ($namespace ne 'roles') {
2285: chomp($what);
2286: my $proname=propath($udom,$uname);
2287: my $now=time;
2288: unless ($namespace=~/^nohist\_/) {
2289: my $hfh;
2290: if (
2291: $hfh=IO::File->new(">>$proname/$namespace.hist")
2292: ) { print $hfh "P:$now:$what\n"; }
2293: }
2294: my @pairs=split(/\&/,$what);
2295: my %hash;
2296: if (tie(%hash,'GDBM_File',
2297: "$proname/$namespace.db",
2298: &GDBM_WRCREAT(),0640)) {
2299: foreach my $pair (@pairs) {
2300: my ($key,$value)=split(/=/,$pair);
2301: # We could check that we have a number...
2302: if (! defined($value) || $value eq '') {
2303: $value = 1;
2304: }
2305: $hash{$key}+=$value;
2306: }
2307: if (untie(%hash)) {
2308: print $client "ok\n";
2309: } else {
2310: print $client "error: ".($!+0)
2311: ." untie(GDBM) failed ".
1.181 albertel 2312: "while attempting inc\n";
1.178 foxr 2313: }
2314: } else {
2315: print $client "error: ".($!)
2316: ." tie(GDBM) Failed ".
1.181 albertel 2317: "while attempting inc\n";
1.178 foxr 2318: }
2319: } else {
2320: print $client "refused\n";
2321: }
2322: } else {
2323: Reply($client, "refused\n", $userinput);
2324:
2325: }
2326: # -------------------------------------------------------------------- rolesput
2327: } elsif ($userinput =~ /^rolesput/) {
2328: if(isClient) {
2329: &Debug("rolesput");
2330: if ($wasenc==1) {
2331: my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
2332: =split(/:/,$userinput);
2333: &Debug("cmd = ".$cmd." exedom= ".$exedom.
2334: "user = ".$exeuser." udom=".$udom.
2335: "what = ".$what);
2336: my $namespace='roles';
2337: chomp($what);
2338: my $proname=propath($udom,$uname);
2339: my $now=time;
2340: {
2341: my $hfh;
2342: if (
2343: $hfh=IO::File->new(">>$proname/$namespace.hist")
2344: ) {
2345: print $hfh "P:$now:$exedom:$exeuser:$what\n";
2346: }
2347: }
2348: my @pairs=split(/\&/,$what);
2349: my %hash;
2350: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
2351: foreach my $pair (@pairs) {
2352: my ($key,$value)=split(/=/,$pair);
2353: &ManagePermissions($key, $udom, $uname,
2354: &GetAuthType( $udom,
2355: $uname));
2356: $hash{$key}=$value;
2357: }
2358: if (untie(%hash)) {
2359: print $client "ok\n";
2360: } else {
2361: print $client "error: ".($!+0)
2362: ." untie(GDBM) Failed ".
2363: "while attempting rolesput\n";
2364: }
2365: } else {
2366: print $client "error: ".($!+0)
2367: ." tie(GDBM) Failed ".
2368: "while attempting rolesput\n";
2369: }
2370: } else {
2371: print $client "refused\n";
2372: }
2373: } else {
2374: Reply($client, "refused\n", $userinput);
2375:
2376: }
2377: # -------------------------------------------------------------------- rolesdel
2378: } elsif ($userinput =~ /^rolesdel/) {
2379: if(isClient) {
2380: &Debug("rolesdel");
2381: if ($wasenc==1) {
2382: my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
2383: =split(/:/,$userinput);
2384: &Debug("cmd = ".$cmd." exedom= ".$exedom.
2385: "user = ".$exeuser." udom=".$udom.
2386: "what = ".$what);
2387: my $namespace='roles';
2388: chomp($what);
2389: my $proname=propath($udom,$uname);
2390: my $now=time;
2391: {
2392: my $hfh;
2393: if (
2394: $hfh=IO::File->new(">>$proname/$namespace.hist")
2395: ) {
2396: print $hfh "D:$now:$exedom:$exeuser:$what\n";
2397: }
2398: }
2399: my @rolekeys=split(/\&/,$what);
2400: my %hash;
2401: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
2402: foreach my $key (@rolekeys) {
2403: delete $hash{$key};
2404: }
2405: if (untie(%hash)) {
2406: print $client "ok\n";
2407: } else {
2408: print $client "error: ".($!+0)
2409: ." untie(GDBM) Failed ".
2410: "while attempting rolesdel\n";
2411: }
2412: } else {
2413: print $client "error: ".($!+0)
2414: ." tie(GDBM) Failed ".
2415: "while attempting rolesdel\n";
2416: }
2417: } else {
2418: print $client "refused\n";
2419: }
2420: } else {
2421: Reply($client, "refused\n", $userinput);
2422:
2423: }
2424: # ------------------------------------------------------------------------- get
2425: } elsif ($userinput =~ /^get/) {
2426: if(isClient) {
2427: my ($cmd,$udom,$uname,$namespace,$what)
2428: =split(/:/,$userinput);
2429: $namespace=~s/\//\_/g;
2430: $namespace=~s/\W//g;
2431: chomp($what);
2432: my @queries=split(/\&/,$what);
2433: my $proname=propath($udom,$uname);
2434: my $qresult='';
2435: my %hash;
2436: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
2437: for (my $i=0;$i<=$#queries;$i++) {
2438: $qresult.="$hash{$queries[$i]}&";
2439: }
2440: if (untie(%hash)) {
2441: $qresult=~s/\&$//;
2442: print $client "$qresult\n";
2443: } else {
2444: print $client "error: ".($!+0)
2445: ." untie(GDBM) Failed ".
2446: "while attempting get\n";
2447: }
2448: } else {
2449: if ($!+0 == 2) {
2450: print $client "error:No such file or ".
2451: "GDBM reported bad block error\n";
2452: } else {
2453: print $client "error: ".($!+0)
2454: ." tie(GDBM) Failed ".
2455: "while attempting get\n";
2456: }
2457: }
2458: } else {
2459: Reply($client, "refused\n", $userinput);
2460:
2461: }
2462: # ------------------------------------------------------------------------ eget
2463: } elsif ($userinput =~ /^eget/) {
2464: if (isClient) {
2465: my ($cmd,$udom,$uname,$namespace,$what)
2466: =split(/:/,$userinput);
2467: $namespace=~s/\//\_/g;
2468: $namespace=~s/\W//g;
2469: chomp($what);
2470: my @queries=split(/\&/,$what);
2471: my $proname=propath($udom,$uname);
2472: my $qresult='';
2473: my %hash;
2474: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
2475: for (my $i=0;$i<=$#queries;$i++) {
2476: $qresult.="$hash{$queries[$i]}&";
2477: }
2478: if (untie(%hash)) {
2479: $qresult=~s/\&$//;
2480: if ($cipher) {
2481: my $cmdlength=length($qresult);
2482: $qresult.=" ";
2483: my $encqresult='';
2484: for
2485: (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
2486: $encqresult.=
2487: unpack("H16",
2488: $cipher->encrypt(substr($qresult,$encidx,8)));
2489: }
2490: print $client "enc:$cmdlength:$encqresult\n";
2491: } else {
2492: print $client "error:no_key\n";
2493: }
2494: } else {
2495: print $client "error: ".($!+0)
2496: ." untie(GDBM) Failed ".
2497: "while attempting eget\n";
2498: }
2499: } else {
2500: print $client "error: ".($!+0)
2501: ." tie(GDBM) Failed ".
2502: "while attempting eget\n";
2503: }
2504: } else {
2505: Reply($client, "refused\n", $userinput);
2506:
2507: }
2508: # ------------------------------------------------------------------------- del
2509: } elsif ($userinput =~ /^del/) {
2510: if(isClient) {
2511: my ($cmd,$udom,$uname,$namespace,$what)
2512: =split(/:/,$userinput);
2513: $namespace=~s/\//\_/g;
2514: $namespace=~s/\W//g;
2515: chomp($what);
2516: my $proname=propath($udom,$uname);
2517: my $now=time;
2518: unless ($namespace=~/^nohist\_/) {
2519: my $hfh;
2520: if (
2521: $hfh=IO::File->new(">>$proname/$namespace.hist")
2522: ) { print $hfh "D:$now:$what\n"; }
2523: }
2524: my @keys=split(/\&/,$what);
2525: my %hash;
2526: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
2527: foreach my $key (@keys) {
2528: delete($hash{$key});
2529: }
2530: if (untie(%hash)) {
2531: print $client "ok\n";
2532: } else {
2533: print $client "error: ".($!+0)
2534: ." untie(GDBM) Failed ".
2535: "while attempting del\n";
2536: }
2537: } else {
2538: print $client "error: ".($!+0)
2539: ." tie(GDBM) Failed ".
2540: "while attempting del\n";
2541: }
2542: } else {
2543: Reply($client, "refused\n", $userinput);
2544:
2545: }
2546: # ------------------------------------------------------------------------ keys
2547: } elsif ($userinput =~ /^keys/) {
2548: if(isClient) {
2549: my ($cmd,$udom,$uname,$namespace)
2550: =split(/:/,$userinput);
2551: $namespace=~s/\//\_/g;
2552: $namespace=~s/\W//g;
2553: my $proname=propath($udom,$uname);
2554: my $qresult='';
2555: my %hash;
2556: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
2557: foreach my $key (keys %hash) {
2558: $qresult.="$key&";
2559: }
2560: if (untie(%hash)) {
2561: $qresult=~s/\&$//;
2562: print $client "$qresult\n";
2563: } else {
2564: print $client "error: ".($!+0)
2565: ." untie(GDBM) Failed ".
2566: "while attempting keys\n";
2567: }
2568: } else {
2569: print $client "error: ".($!+0)
2570: ." tie(GDBM) Failed ".
2571: "while attempting keys\n";
2572: }
2573: } else {
2574: Reply($client, "refused\n", $userinput);
2575:
2576: }
2577: # ----------------------------------------------------------------- dumpcurrent
2578: } elsif ($userinput =~ /^currentdump/) {
2579: if (isClient) {
2580: my ($cmd,$udom,$uname,$namespace)
2581: =split(/:/,$userinput);
2582: $namespace=~s/\//\_/g;
2583: $namespace=~s/\W//g;
2584: my $qresult='';
2585: my $proname=propath($udom,$uname);
2586: my %hash;
2587: if (tie(%hash,'GDBM_File',
2588: "$proname/$namespace.db",
2589: &GDBM_READER(),0640)) {
2590: # Structure of %data:
2591: # $data{$symb}->{$parameter}=$value;
2592: # $data{$symb}->{'v.'.$parameter}=$version;
2593: # since $parameter will be unescaped, we do not
2594: # have to worry about silly parameter names...
2595: my %data = ();
2596: while (my ($key,$value) = each(%hash)) {
2597: my ($v,$symb,$param) = split(/:/,$key);
2598: next if ($v eq 'version' || $symb eq 'keys');
2599: next if (exists($data{$symb}) &&
2600: exists($data{$symb}->{$param}) &&
2601: $data{$symb}->{'v.'.$param} > $v);
2602: $data{$symb}->{$param}=$value;
2603: $data{$symb}->{'v.'.$param}=$v;
2604: }
2605: if (untie(%hash)) {
2606: while (my ($symb,$param_hash) = each(%data)) {
2607: while(my ($param,$value) = each (%$param_hash)){
2608: next if ($param =~ /^v\./);
2609: $qresult.=$symb.':'.$param.'='.$value.'&';
2610: }
2611: }
2612: chop($qresult);
2613: print $client "$qresult\n";
2614: } else {
2615: print $client "error: ".($!+0)
2616: ." untie(GDBM) Failed ".
2617: "while attempting currentdump\n";
2618: }
2619: } else {
2620: print $client "error: ".($!+0)
2621: ." tie(GDBM) Failed ".
2622: "while attempting currentdump\n";
2623: }
2624: } else {
2625: Reply($client, "refused\n", $userinput);
2626: }
2627: # ------------------------------------------------------------------------ dump
2628: } elsif ($userinput =~ /^dump/) {
2629: if(isClient) {
2630: my ($cmd,$udom,$uname,$namespace,$regexp)
2631: =split(/:/,$userinput);
2632: $namespace=~s/\//\_/g;
2633: $namespace=~s/\W//g;
2634: if (defined($regexp)) {
2635: $regexp=&unescape($regexp);
2636: } else {
2637: $regexp='.';
2638: }
2639: my $qresult='';
2640: my $proname=propath($udom,$uname);
2641: my %hash;
2642: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
2643: while (my ($key,$value) = each(%hash)) {
2644: if ($regexp eq '.') {
2645: $qresult.=$key.'='.$value.'&';
2646: } else {
2647: my $unescapeKey = &unescape($key);
2648: if (eval('$unescapeKey=~/$regexp/')) {
2649: $qresult.="$key=$value&";
2650: }
2651: }
2652: }
2653: if (untie(%hash)) {
2654: chop($qresult);
2655: print $client "$qresult\n";
2656: } else {
2657: print $client "error: ".($!+0)
2658: ." untie(GDBM) Failed ".
2659: "while attempting dump\n";
2660: }
2661: } else {
2662: print $client "error: ".($!+0)
2663: ." tie(GDBM) Failed ".
2664: "while attempting dump\n";
2665: }
2666: } else {
2667: Reply($client, "refused\n", $userinput);
2668:
2669: }
2670: # ----------------------------------------------------------------------- store
2671: } elsif ($userinput =~ /^store/) {
2672: if(isClient) {
2673: my ($cmd,$udom,$uname,$namespace,$rid,$what)
2674: =split(/:/,$userinput);
2675: $namespace=~s/\//\_/g;
2676: $namespace=~s/\W//g;
2677: if ($namespace ne 'roles') {
2678: chomp($what);
2679: my $proname=propath($udom,$uname);
2680: my $now=time;
2681: unless ($namespace=~/^nohist\_/) {
2682: my $hfh;
2683: if (
2684: $hfh=IO::File->new(">>$proname/$namespace.hist")
2685: ) { print $hfh "P:$now:$rid:$what\n"; }
2686: }
2687: my @pairs=split(/\&/,$what);
2688: my %hash;
2689: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
2690: my @previouskeys=split(/&/,$hash{"keys:$rid"});
2691: my $key;
2692: $hash{"version:$rid"}++;
2693: my $version=$hash{"version:$rid"};
2694: my $allkeys='';
2695: foreach my $pair (@pairs) {
2696: my ($key,$value)=split(/=/,$pair);
2697: $allkeys.=$key.':';
2698: $hash{"$version:$rid:$key"}=$value;
2699: }
2700: $hash{"$version:$rid:timestamp"}=$now;
2701: $allkeys.='timestamp';
2702: $hash{"$version:keys:$rid"}=$allkeys;
2703: if (untie(%hash)) {
2704: print $client "ok\n";
2705: } else {
2706: print $client "error: ".($!+0)
2707: ." untie(GDBM) Failed ".
2708: "while attempting store\n";
2709: }
2710: } else {
2711: print $client "error: ".($!+0)
2712: ." tie(GDBM) Failed ".
2713: "while attempting store\n";
2714: }
2715: } else {
2716: print $client "refused\n";
2717: }
2718: } else {
2719: Reply($client, "refused\n", $userinput);
2720:
2721: }
2722: # --------------------------------------------------------------------- restore
2723: } elsif ($userinput =~ /^restore/) {
2724: if(isClient) {
2725: my ($cmd,$udom,$uname,$namespace,$rid)
2726: =split(/:/,$userinput);
2727: $namespace=~s/\//\_/g;
2728: $namespace=~s/\W//g;
2729: chomp($rid);
2730: my $proname=propath($udom,$uname);
2731: my $qresult='';
2732: my %hash;
2733: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
2734: my $version=$hash{"version:$rid"};
2735: $qresult.="version=$version&";
2736: my $scope;
2737: for ($scope=1;$scope<=$version;$scope++) {
2738: my $vkeys=$hash{"$scope:keys:$rid"};
2739: my @keys=split(/:/,$vkeys);
2740: my $key;
2741: $qresult.="$scope:keys=$vkeys&";
2742: foreach $key (@keys) {
2743: $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&";
2744: }
2745: }
2746: if (untie(%hash)) {
2747: $qresult=~s/\&$//;
2748: print $client "$qresult\n";
2749: } else {
2750: print $client "error: ".($!+0)
2751: ." untie(GDBM) Failed ".
2752: "while attempting restore\n";
2753: }
2754: } else {
2755: print $client "error: ".($!+0)
2756: ." tie(GDBM) Failed ".
2757: "while attempting restore\n";
2758: }
2759: } else {
2760: Reply($client, "refused\n", $userinput);
2761:
2762: }
2763: # -------------------------------------------------------------------- chatsend
2764: } elsif ($userinput =~ /^chatsend/) {
2765: if(isClient) {
2766: my ($cmd,$cdom,$cnum,$newpost)=split(/\:/,$userinput);
2767: &chatadd($cdom,$cnum,$newpost);
2768: print $client "ok\n";
2769: } else {
2770: Reply($client, "refused\n", $userinput);
2771:
2772: }
2773: # -------------------------------------------------------------------- chatretr
2774: } elsif ($userinput =~ /^chatretr/) {
2775: if(isClient) {
2776: my
2777: ($cmd,$cdom,$cnum,$udom,$uname)=split(/\:/,$userinput);
2778: my $reply='';
2779: foreach (&getchat($cdom,$cnum,$udom,$uname)) {
2780: $reply.=&escape($_).':';
2781: }
2782: $reply=~s/\:$//;
2783: print $client $reply."\n";
2784: } else {
2785: Reply($client, "refused\n", $userinput);
2786:
2787: }
2788: # ------------------------------------------------------------------- querysend
2789: } elsif ($userinput =~ /^querysend/) {
1.193 raeburn 2790: if (isClient) {
1.178 foxr 2791: my ($cmd,$query,
2792: $arg1,$arg2,$arg3)=split(/\:/,$userinput);
2793: $query=~s/\n*$//g;
2794: print $client "".
2795: sqlreply("$clientname\&$query".
2796: "\&$arg1"."\&$arg2"."\&$arg3")."\n";
2797: } else {
2798: Reply($client, "refused\n", $userinput);
2799:
2800: }
2801: # ------------------------------------------------------------------ queryreply
2802: } elsif ($userinput =~ /^queryreply/) {
2803: if(isClient) {
2804: my ($cmd,$id,$reply)=split(/:/,$userinput);
2805: my $store;
2806: my $execdir=$perlvar{'lonDaemons'};
2807: if ($store=IO::File->new(">$execdir/tmp/$id")) {
2808: $reply=~s/\&/\n/g;
2809: print $store $reply;
2810: close $store;
2811: my $store2=IO::File->new(">$execdir/tmp/$id.end");
2812: print $store2 "done\n";
2813: close $store2;
2814: print $client "ok\n";
2815: }
2816: else {
2817: print $client "error: ".($!+0)
2818: ." IO::File->new Failed ".
2819: "while attempting queryreply\n";
2820: }
2821: } else {
2822: Reply($client, "refused\n", $userinput);
2823:
2824: }
2825: # ----------------------------------------------------------------- courseidput
2826: } elsif ($userinput =~ /^courseidput/) {
2827: if(isClient) {
2828: my ($cmd,$udom,$what)=split(/:/,$userinput);
2829: chomp($what);
2830: $udom=~s/\W//g;
2831: my $proname=
2832: "$perlvar{'lonUsersDir'}/$udom/nohist_courseids";
2833: my $now=time;
2834: my @pairs=split(/\&/,$what);
2835: my %hash;
2836: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) {
2837: foreach my $pair (@pairs) {
1.202 raeburn 2838: my ($key,$descr,$inst_code)=split(/=/,$pair);
2839: $hash{$key}=$descr.':'.$inst_code.':'.$now;
1.178 foxr 2840: }
2841: if (untie(%hash)) {
2842: print $client "ok\n";
2843: } else {
2844: print $client "error: ".($!+0)
2845: ." untie(GDBM) Failed ".
2846: "while attempting courseidput\n";
2847: }
2848: } else {
2849: print $client "error: ".($!+0)
2850: ." tie(GDBM) Failed ".
2851: "while attempting courseidput\n";
2852: }
2853: } else {
2854: Reply($client, "refused\n", $userinput);
2855:
2856: }
2857: # ---------------------------------------------------------------- courseiddump
2858: } elsif ($userinput =~ /^courseiddump/) {
2859: if(isClient) {
2860: my ($cmd,$udom,$since,$description)
2861: =split(/:/,$userinput);
2862: if (defined($description)) {
2863: $description=&unescape($description);
2864: } else {
2865: $description='.';
2866: }
2867: unless (defined($since)) { $since=0; }
2868: my $qresult='';
2869: my $proname=
2870: "$perlvar{'lonUsersDir'}/$udom/nohist_courseids";
2871: my %hash;
2872: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) {
2873: while (my ($key,$value) = each(%hash)) {
1.202 raeburn 2874: my ($descr,$lasttime,$inst_code);
2875: if ($value =~ m/^([^\:]*):([^\:]*):(\d+)$/) {
2876: ($descr,$inst_code,$lasttime)=($1,$2,$3);
2877: } else {
2878: ($descr,$lasttime) = split(/\:/,$value);
2879: }
1.178 foxr 2880: if ($lasttime<$since) { next; }
2881: if ($description eq '.') {
1.202 raeburn 2882: $qresult.=$key.'='.$descr.':'.$inst_code.'&';
1.178 foxr 2883: } else {
2884: my $unescapeVal = &unescape($descr);
1.189 www 2885: if (eval('$unescapeVal=~/\Q$description\E/i')) {
1.202 raeburn 2886: $qresult.=$key.'='.$descr.':'.$inst_code.'&';
1.178 foxr 2887: }
2888: }
2889: }
2890: if (untie(%hash)) {
2891: chop($qresult);
2892: print $client "$qresult\n";
2893: } else {
2894: print $client "error: ".($!+0)
2895: ." untie(GDBM) Failed ".
2896: "while attempting courseiddump\n";
2897: }
2898: } else {
2899: print $client "error: ".($!+0)
2900: ." tie(GDBM) Failed ".
2901: "while attempting courseiddump\n";
2902: }
2903: } else {
2904: Reply($client, "refused\n", $userinput);
2905:
2906: }
2907: # ----------------------------------------------------------------------- idput
2908: } elsif ($userinput =~ /^idput/) {
2909: if(isClient) {
2910: my ($cmd,$udom,$what)=split(/:/,$userinput);
2911: chomp($what);
2912: $udom=~s/\W//g;
2913: my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
2914: my $now=time;
2915: {
2916: my $hfh;
2917: if (
2918: $hfh=IO::File->new(">>$proname.hist")
2919: ) { print $hfh "P:$now:$what\n"; }
2920: }
2921: my @pairs=split(/\&/,$what);
2922: my %hash;
2923: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) {
2924: foreach my $pair (@pairs) {
2925: my ($key,$value)=split(/=/,$pair);
2926: $hash{$key}=$value;
2927: }
2928: if (untie(%hash)) {
2929: print $client "ok\n";
2930: } else {
2931: print $client "error: ".($!+0)
2932: ." untie(GDBM) Failed ".
2933: "while attempting idput\n";
2934: }
2935: } else {
2936: print $client "error: ".($!+0)
2937: ." tie(GDBM) Failed ".
2938: "while attempting idput\n";
2939: }
2940: } else {
2941: Reply($client, "refused\n", $userinput);
2942:
2943: }
2944: # ----------------------------------------------------------------------- idget
2945: } elsif ($userinput =~ /^idget/) {
2946: if(isClient) {
2947: my ($cmd,$udom,$what)=split(/:/,$userinput);
2948: chomp($what);
2949: $udom=~s/\W//g;
2950: my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
2951: my @queries=split(/\&/,$what);
2952: my $qresult='';
2953: my %hash;
2954: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) {
2955: for (my $i=0;$i<=$#queries;$i++) {
2956: $qresult.="$hash{$queries[$i]}&";
2957: }
2958: if (untie(%hash)) {
2959: $qresult=~s/\&$//;
2960: print $client "$qresult\n";
2961: } else {
2962: print $client "error: ".($!+0)
2963: ." untie(GDBM) Failed ".
2964: "while attempting idget\n";
2965: }
2966: } else {
2967: print $client "error: ".($!+0)
2968: ." tie(GDBM) Failed ".
2969: "while attempting idget\n";
2970: }
2971: } else {
2972: Reply($client, "refused\n", $userinput);
2973:
2974: }
2975: # ---------------------------------------------------------------------- tmpput
2976: } elsif ($userinput =~ /^tmpput/) {
2977: if(isClient) {
2978: my ($cmd,$what)=split(/:/,$userinput);
2979: my $store;
2980: $tmpsnum++;
2981: my $id=$$.'_'.$clientip.'_'.$tmpsnum;
2982: $id=~s/\W/\_/g;
2983: $what=~s/\n//g;
2984: my $execdir=$perlvar{'lonDaemons'};
2985: if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) {
2986: print $store $what;
2987: close $store;
2988: print $client "$id\n";
2989: }
2990: else {
2991: print $client "error: ".($!+0)
2992: ."IO::File->new Failed ".
2993: "while attempting tmpput\n";
2994: }
2995: } else {
2996: Reply($client, "refused\n", $userinput);
2997:
2998: }
2999:
3000: # ---------------------------------------------------------------------- tmpget
3001: } elsif ($userinput =~ /^tmpget/) {
3002: if(isClient) {
3003: my ($cmd,$id)=split(/:/,$userinput);
3004: chomp($id);
3005: $id=~s/\W/\_/g;
3006: my $store;
3007: my $execdir=$perlvar{'lonDaemons'};
3008: if ($store=IO::File->new("$execdir/tmp/$id.tmp")) {
3009: my $reply=<$store>;
3010: print $client "$reply\n";
3011: close $store;
3012: }
3013: else {
3014: print $client "error: ".($!+0)
3015: ."IO::File->new Failed ".
3016: "while attempting tmpget\n";
3017: }
3018: } else {
3019: Reply($client, "refused\n", $userinput);
3020:
3021: }
3022: # ---------------------------------------------------------------------- tmpdel
3023: } elsif ($userinput =~ /^tmpdel/) {
3024: if(isClient) {
3025: my ($cmd,$id)=split(/:/,$userinput);
3026: chomp($id);
3027: $id=~s/\W/\_/g;
3028: my $execdir=$perlvar{'lonDaemons'};
3029: if (unlink("$execdir/tmp/$id.tmp")) {
3030: print $client "ok\n";
3031: } else {
3032: print $client "error: ".($!+0)
3033: ."Unlink tmp Failed ".
3034: "while attempting tmpdel\n";
3035: }
3036: } else {
3037: Reply($client, "refused\n", $userinput);
3038:
3039: }
1.201 matthew 3040: # ----------------------------------------- portfolio directory list (portls)
3041: } elsif ($userinput =~ /^portls/) {
3042: if(isClient) {
3043: my ($cmd,$uname,$udom)=split(/:/,$userinput);
3044: my $udir=propath($udom,$uname).'/userfiles/portfolio';
3045: my $dirLine='';
3046: my $dirContents='';
3047: if (opendir(LSDIR,$udir.'/')){
3048: while ($dirLine = readdir(LSDIR)){
3049: $dirContents = $dirContents.$dirLine.'<br />';
3050: }
3051: } else {
3052: $dirContents = "No directory found\n";
3053: }
3054: print $client $dirContents."\n";
3055: } else {
3056: Reply($client, "refused\n", $userinput);
3057: }
1.178 foxr 3058: # -------------------------------------------------------------------------- ls
3059: } elsif ($userinput =~ /^ls/) {
3060: if(isClient) {
3061: my $obs;
3062: my $rights;
3063: my ($cmd,$ulsdir)=split(/:/,$userinput);
3064: my $ulsout='';
3065: my $ulsfn;
3066: if (-e $ulsdir) {
3067: if(-d $ulsdir) {
3068: if (opendir(LSDIR,$ulsdir)) {
3069: while ($ulsfn=readdir(LSDIR)) {
3070: undef $obs, $rights;
3071: my @ulsstats=stat($ulsdir.'/'.$ulsfn);
3072: #We do some obsolete checking here
3073: if(-e $ulsdir.'/'.$ulsfn.".meta") {
3074: open(FILE, $ulsdir.'/'.$ulsfn.".meta");
3075: my @obsolete=<FILE>;
3076: foreach my $obsolete (@obsolete) {
3077: if($obsolete =~ m|(<obsolete>)(on)|) { $obs = 1; }
3078: if($obsolete =~ m|(<copyright>)(default)|) { $rights = 1; }
3079: }
3080: }
3081: $ulsout.=$ulsfn.'&'.join('&',@ulsstats);
3082: if($obs eq '1') { $ulsout.="&1"; }
3083: else { $ulsout.="&0"; }
3084: if($rights eq '1') { $ulsout.="&1:"; }
3085: else { $ulsout.="&0:"; }
3086: }
3087: closedir(LSDIR);
3088: }
3089: } else {
3090: my @ulsstats=stat($ulsdir);
3091: $ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
3092: }
3093: } else {
3094: $ulsout='no_such_dir';
3095: }
3096: if ($ulsout eq '') { $ulsout='empty'; }
3097: print $client "$ulsout\n";
3098: } else {
3099: Reply($client, "refused\n", $userinput);
3100:
3101: }
3102: # ----------------------------------------------------------------- setannounce
3103: } elsif ($userinput =~ /^setannounce/) {
3104: if (isClient) {
3105: my ($cmd,$announcement)=split(/:/,$userinput);
3106: chomp($announcement);
3107: $announcement=&unescape($announcement);
3108: if (my $store=IO::File->new('>'.$perlvar{'lonDocRoot'}.
3109: '/announcement.txt')) {
3110: print $store $announcement;
3111: close $store;
3112: print $client "ok\n";
3113: } else {
3114: print $client "error: ".($!+0)."\n";
3115: }
3116: } else {
3117: Reply($client, "refused\n", $userinput);
3118:
3119: }
3120: # ------------------------------------------------------------------ Hanging up
3121: } elsif (($userinput =~ /^exit/) ||
3122: ($userinput =~ /^init/)) { # no restrictions.
3123: &logthis(
3124: "Client $clientip ($clientname) hanging up: $userinput");
3125: print $client "bye\n";
3126: $client->shutdown(2); # shutdown the socket forcibly.
3127: $client->close();
3128: last;
1.161 foxr 3129:
1.178 foxr 3130: # ---------------------------------- set current host/domain
3131: } elsif ($userinput =~ /^sethost:/) {
3132: if (isClient) {
3133: print $client &sethost($userinput)."\n";
3134: } else {
3135: print $client "refused\n";
3136: }
3137: #---------------------------------- request file (?) version.
3138: } elsif ($userinput =~/^version:/) {
3139: if (isClient) {
3140: print $client &version($userinput)."\n";
3141: } else {
3142: print $client "refused\n";
3143: }
1.193 raeburn 3144: #------------------------------- is auto-enrollment enabled?
1.200 matthew 3145: } elsif ($userinput =~/^autorun:/) {
1.193 raeburn 3146: if (isClient) {
1.200 matthew 3147: my ($cmd,$cdom) = split(/:/,$userinput);
3148: my $outcome = &localenroll::run($cdom);
1.193 raeburn 3149: print $client "$outcome\n";
3150: } else {
3151: print $client "0\n";
3152: }
3153: #------------------------------- get official sections (for auto-enrollment).
1.200 matthew 3154: } elsif ($userinput =~/^autogetsections:/) {
1.193 raeburn 3155: if (isClient) {
1.200 matthew 3156: my ($cmd,$coursecode,$cdom)=split(/:/,$userinput);
3157: my @secs = &localenroll::get_sections($coursecode,$cdom);
1.193 raeburn 3158: my $seclist = &escape(join(':',@secs));
3159: print $client "$seclist\n";
3160: } else {
3161: print $client "refused\n";
3162: }
3163: #----------------------- validate owner of new course section (for auto-enrollment).
1.200 matthew 3164: } elsif ($userinput =~/^autonewcourse:/) {
1.193 raeburn 3165: if (isClient) {
1.200 matthew 3166: my ($cmd,$inst_course_id,$owner,$cdom)=split(/:/,$userinput);
3167: my $outcome = &localenroll::new_course($inst_course_id,$owner,$cdom);
1.193 raeburn 3168: print $client "$outcome\n";
3169: } else {
3170: print $client "refused\n";
3171: }
3172: #-------------- validate course section in schedule of classes (for auto-enrollment).
1.200 matthew 3173: } elsif ($userinput =~/^autovalidatecourse:/) {
1.193 raeburn 3174: if (isClient) {
1.200 matthew 3175: my ($cmd,$inst_course_id,$cdom)=split(/:/,$userinput);
3176: my $outcome=&localenroll::validate_courseID($inst_course_id,$cdom);
1.193 raeburn 3177: print $client "$outcome\n";
3178: } else {
3179: print $client "refused\n";
3180: }
3181: #--------------------------- create password for new user (for auto-enrollment).
1.200 matthew 3182: } elsif ($userinput =~/^autocreatepassword:/) {
1.193 raeburn 3183: if (isClient) {
1.200 matthew 3184: my ($cmd,$authparam,$cdom)=split(/:/,$userinput);
3185: my ($create_passwd,$authchk);
3186: ($authparam,$create_passwd,$authchk) = &localenroll::create_password($authparam,$cdom);
1.193 raeburn 3187: print $client &escape($authparam.':'.$create_passwd.':'.$authchk)."\n";
3188: } else {
3189: print $client "refused\n";
3190: }
3191: #--------------------------- read and remove temporary files (for auto-enrollment).
1.200 matthew 3192: } elsif ($userinput =~/^autoretrieve:/) {
1.193 raeburn 3193: if (isClient) {
3194: my ($cmd,$filename) = split(/:/,$userinput);
3195: my $source = $perlvar{'lonDaemons'}.'/tmp/'.$filename;
3196: if ( (-e $source) && ($filename ne '') ) {
3197: my $reply = '';
3198: if (open(my $fh,$source)) {
3199: while (<$fh>) {
3200: chomp($_);
3201: $_ =~ s/^\s+//g;
3202: $_ =~ s/\s+$//g;
3203: $reply .= $_;
3204: }
3205: close($fh);
3206: print $client &escape($reply)."\n";
3207: # unlink($source);
3208: } else {
3209: print $client "error\n";
3210: }
3211: } else {
3212: print $client "error\n";
3213: }
3214: } else {
3215: print $client "refused\n";
3216: }
1.178 foxr 3217: # ------------------------------------------------------------- unknown command
1.161 foxr 3218:
1.178 foxr 3219: } else {
3220: # unknown command
3221: print $client "unknown_cmd\n";
3222: }
1.177 foxr 3223: # -------------------------------------------------------------------- complete
1.178 foxr 3224: alarm(0);
1.200 matthew 3225: &status('Listening to '.$clientname." ($keymode)");
1.161 foxr 3226: }
1.59 www 3227: # --------------------------------------------- client unknown or fishy, refuse
1.161 foxr 3228: } else {
3229: print $client "refused\n";
3230: $client->close();
1.190 albertel 3231: &logthis("<font color='blue'>WARNING: "
1.161 foxr 3232: ."Rejected client $clientip, closing connection</font>");
3233: }
3234: }
3235:
1.1 albertel 3236: # =============================================================================
1.161 foxr 3237:
1.190 albertel 3238: &logthis("<font color='red'>CRITICAL: "
1.161 foxr 3239: ."Disconnect from $clientip ($clientname)</font>");
3240:
3241:
3242: # this exit is VERY important, otherwise the child will become
3243: # a producer of more and more children, forking yourself into
3244: # process death.
3245: exit;
1.106 foxr 3246:
1.78 foxr 3247: }
3248:
3249:
3250: #
3251: # Checks to see if the input roleput request was to set
3252: # an author role. If so, invokes the lchtmldir script to set
3253: # up a correct public_html
3254: # Parameters:
3255: # request - The request sent to the rolesput subchunk.
3256: # We're looking for /domain/_au
3257: # domain - The domain in which the user is having roles doctored.
3258: # user - Name of the user for which the role is being put.
3259: # authtype - The authentication type associated with the user.
3260: #
3261: sub ManagePermissions
3262: {
1.192 foxr 3263:
3264: my ($request, $domain, $user, $authtype) = @_;
1.78 foxr 3265:
3266: # See if the request is of the form /$domain/_au
3267: if($request =~ /^(\/$domain\/_au)$/) { # It's an author rolesput...
3268: my $execdir = $perlvar{'lonDaemons'};
3269: my $userhome= "/home/$user" ;
1.134 albertel 3270: &logthis("system $execdir/lchtmldir $userhome $user $authtype");
1.78 foxr 3271: system("$execdir/lchtmldir $userhome $user $authtype");
3272: }
3273: }
3274: #
3275: # GetAuthType - Determines the authorization type of a user in a domain.
3276:
3277: # Returns the authorization type or nouser if there is no such user.
3278: #
3279: sub GetAuthType
3280: {
1.192 foxr 3281:
3282: my ($domain, $user) = @_;
1.78 foxr 3283:
1.79 foxr 3284: Debug("GetAuthType( $domain, $user ) \n");
1.78 foxr 3285: my $proname = &propath($domain, $user);
3286: my $passwdfile = "$proname/passwd";
3287: if( -e $passwdfile ) {
3288: my $pf = IO::File->new($passwdfile);
3289: my $realpassword = <$pf>;
3290: chomp($realpassword);
1.79 foxr 3291: Debug("Password info = $realpassword\n");
1.78 foxr 3292: my ($authtype, $contentpwd) = split(/:/, $realpassword);
1.79 foxr 3293: Debug("Authtype = $authtype, content = $contentpwd\n");
1.78 foxr 3294: my $availinfo = '';
1.91 albertel 3295: if($authtype eq 'krb4' or $authtype eq 'krb5') {
1.78 foxr 3296: $availinfo = $contentpwd;
3297: }
1.79 foxr 3298:
1.78 foxr 3299: return "$authtype:$availinfo";
3300: }
3301: else {
1.79 foxr 3302: Debug("Returning nouser");
1.78 foxr 3303: return "nouser";
3304: }
1.1 albertel 3305: }
3306:
1.84 albertel 3307: sub addline {
3308: my ($fname,$hostid,$ip,$newline)=@_;
3309: my $contents;
3310: my $found=0;
3311: my $expr='^'.$hostid.':'.$ip.':';
3312: $expr =~ s/\./\\\./g;
1.134 albertel 3313: my $sh;
1.84 albertel 3314: if ($sh=IO::File->new("$fname.subscription")) {
3315: while (my $subline=<$sh>) {
3316: if ($subline !~ /$expr/) {$contents.= $subline;} else {$found=1;}
3317: }
3318: $sh->close();
3319: }
3320: $sh=IO::File->new(">$fname.subscription");
3321: if ($contents) { print $sh $contents; }
3322: if ($newline) { print $sh $newline; }
3323: $sh->close();
3324: return $found;
1.86 www 3325: }
3326:
3327: sub getchat {
1.122 www 3328: my ($cdom,$cname,$udom,$uname)=@_;
1.87 www 3329: my %hash;
3330: my $proname=&propath($cdom,$cname);
3331: my @entries=();
1.88 albertel 3332: if (tie(%hash,'GDBM_File',"$proname/nohist_chatroom.db",
3333: &GDBM_READER(),0640)) {
3334: @entries=map { $_.':'.$hash{$_} } sort keys %hash;
3335: untie %hash;
1.123 www 3336: }
1.124 www 3337: my @participants=();
1.134 albertel 3338: my $cutoff=time-60;
1.123 www 3339: if (tie(%hash,'GDBM_File',"$proname/nohist_inchatroom.db",
1.124 www 3340: &GDBM_WRCREAT(),0640)) {
3341: $hash{$uname.':'.$udom}=time;
1.123 www 3342: foreach (sort keys %hash) {
3343: if ($hash{$_}>$cutoff) {
1.124 www 3344: $participants[$#participants+1]='active_participant:'.$_;
1.123 www 3345: }
3346: }
3347: untie %hash;
1.86 www 3348: }
1.124 www 3349: return (@participants,@entries);
1.86 www 3350: }
3351:
3352: sub chatadd {
1.88 albertel 3353: my ($cdom,$cname,$newchat)=@_;
3354: my %hash;
3355: my $proname=&propath($cdom,$cname);
3356: my @entries=();
1.142 www 3357: my $time=time;
1.88 albertel 3358: if (tie(%hash,'GDBM_File',"$proname/nohist_chatroom.db",
3359: &GDBM_WRCREAT(),0640)) {
3360: @entries=map { $_.':'.$hash{$_} } sort keys %hash;
3361: my ($lastid)=($entries[$#entries]=~/^(\w+)\:/);
3362: my ($thentime,$idnum)=split(/\_/,$lastid);
3363: my $newid=$time.'_000000';
3364: if ($thentime==$time) {
3365: $idnum=~s/^0+//;
3366: $idnum++;
3367: $idnum=substr('000000'.$idnum,-6,6);
3368: $newid=$time.'_'.$idnum;
3369: }
3370: $hash{$newid}=$newchat;
3371: my $expired=$time-3600;
3372: foreach (keys %hash) {
3373: my ($thistime)=($_=~/(\d+)\_/);
3374: if ($thistime<$expired) {
1.89 www 3375: delete $hash{$_};
1.88 albertel 3376: }
3377: }
3378: untie %hash;
1.142 www 3379: }
3380: {
3381: my $hfh;
3382: if ($hfh=IO::File->new(">>$proname/chatroom.log")) {
3383: print $hfh "$time:".&unescape($newchat)."\n";
3384: }
1.86 www 3385: }
1.84 albertel 3386: }
3387:
3388: sub unsub {
3389: my ($fname,$clientip)=@_;
3390: my $result;
1.188 foxr 3391: my $unsubs = 0; # Number of successful unsubscribes:
3392:
3393:
3394: # An old way subscriptions were handled was to have a
3395: # subscription marker file:
3396:
3397: Debug("Attempting unlink of $fname.$clientname");
1.161 foxr 3398: if (unlink("$fname.$clientname")) {
1.188 foxr 3399: $unsubs++; # Successful unsub via marker file.
3400: }
3401:
3402: # The more modern way to do it is to have a subscription list
3403: # file:
3404:
1.84 albertel 3405: if (-e "$fname.subscription") {
1.161 foxr 3406: my $found=&addline($fname,$clientname,$clientip,'');
1.188 foxr 3407: if ($found) {
3408: $unsubs++;
3409: }
3410: }
3411:
3412: # If either or both of these mechanisms succeeded in unsubscribing a
3413: # resource we can return ok:
3414:
3415: if($unsubs) {
3416: $result = "ok\n";
1.84 albertel 3417: } else {
1.188 foxr 3418: $result = "not_subscribed\n";
1.84 albertel 3419: }
1.188 foxr 3420:
1.84 albertel 3421: return $result;
3422: }
3423:
1.101 www 3424: sub currentversion {
3425: my $fname=shift;
3426: my $version=-1;
3427: my $ulsdir='';
3428: if ($fname=~/^(.+)\/[^\/]+$/) {
3429: $ulsdir=$1;
3430: }
1.114 albertel 3431: my ($fnamere1,$fnamere2);
3432: # remove version if already specified
1.101 www 3433: $fname=~s/\.\d+\.(\w+(?:\.meta)*)$/\.$1/;
1.114 albertel 3434: # get the bits that go before and after the version number
3435: if ( $fname=~/^(.*\.)(\w+(?:\.meta)*)$/ ) {
3436: $fnamere1=$1;
3437: $fnamere2='.'.$2;
3438: }
1.101 www 3439: if (-e $fname) { $version=1; }
3440: if (-e $ulsdir) {
1.134 albertel 3441: if(-d $ulsdir) {
3442: if (opendir(LSDIR,$ulsdir)) {
3443: my $ulsfn;
3444: while ($ulsfn=readdir(LSDIR)) {
1.101 www 3445: # see if this is a regular file (ignore links produced earlier)
1.134 albertel 3446: my $thisfile=$ulsdir.'/'.$ulsfn;
3447: unless (-l $thisfile) {
1.160 www 3448: if ($thisfile=~/\Q$fnamere1\E(\d+)\Q$fnamere2\E$/) {
1.134 albertel 3449: if ($1>$version) { $version=$1; }
3450: }
3451: }
3452: }
3453: closedir(LSDIR);
3454: $version++;
3455: }
3456: }
3457: }
3458: return $version;
1.101 www 3459: }
3460:
3461: sub thisversion {
3462: my $fname=shift;
3463: my $version=-1;
3464: if ($fname=~/\.(\d+)\.\w+(?:\.meta)*$/) {
3465: $version=$1;
3466: }
3467: return $version;
3468: }
3469:
1.84 albertel 3470: sub subscribe {
3471: my ($userinput,$clientip)=@_;
3472: my $result;
3473: my ($cmd,$fname)=split(/:/,$userinput);
3474: my $ownership=&ishome($fname);
3475: if ($ownership eq 'owner') {
1.101 www 3476: # explitly asking for the current version?
3477: unless (-e $fname) {
3478: my $currentversion=¤tversion($fname);
3479: if (&thisversion($fname)==$currentversion) {
3480: if ($fname=~/^(.+)\.\d+\.(\w+(?:\.meta)*)$/) {
3481: my $root=$1;
3482: my $extension=$2;
3483: symlink($root.'.'.$extension,
3484: $root.'.'.$currentversion.'.'.$extension);
1.102 www 3485: unless ($extension=~/\.meta$/) {
3486: symlink($root.'.'.$extension.'.meta',
3487: $root.'.'.$currentversion.'.'.$extension.'.meta');
3488: }
1.101 www 3489: }
3490: }
3491: }
1.84 albertel 3492: if (-e $fname) {
3493: if (-d $fname) {
3494: $result="directory\n";
3495: } else {
1.161 foxr 3496: if (-e "$fname.$clientname") {&unsub($fname,$clientip);}
1.134 albertel 3497: my $now=time;
1.161 foxr 3498: my $found=&addline($fname,$clientname,$clientip,
3499: "$clientname:$clientip:$now\n");
1.84 albertel 3500: if ($found) { $result="$fname\n"; }
3501: # if they were subscribed to only meta data, delete that
3502: # subscription, when you subscribe to a file you also get
3503: # the metadata
3504: unless ($fname=~/\.meta$/) { &unsub("$fname.meta",$clientip); }
3505: $fname=~s/\/home\/httpd\/html\/res/raw/;
3506: $fname="http://$thisserver/".$fname;
3507: $result="$fname\n";
3508: }
3509: } else {
3510: $result="not_found\n";
3511: }
3512: } else {
3513: $result="rejected\n";
3514: }
3515: return $result;
3516: }
1.91 albertel 3517:
3518: sub make_passwd_file {
1.98 foxr 3519: my ($uname, $umode,$npass,$passfilename)=@_;
1.91 albertel 3520: my $result="ok\n";
3521: if ($umode eq 'krb4' or $umode eq 'krb5') {
3522: {
3523: my $pf = IO::File->new(">$passfilename");
3524: print $pf "$umode:$npass\n";
3525: }
3526: } elsif ($umode eq 'internal') {
3527: my $salt=time;
3528: $salt=substr($salt,6,2);
3529: my $ncpass=crypt($npass,$salt);
3530: {
3531: &Debug("Creating internal auth");
3532: my $pf = IO::File->new(">$passfilename");
3533: print $pf "internal:$ncpass\n";
3534: }
3535: } elsif ($umode eq 'localauth') {
3536: {
3537: my $pf = IO::File->new(">$passfilename");
3538: print $pf "localauth:$npass\n";
3539: }
3540: } elsif ($umode eq 'unix') {
3541: {
1.186 foxr 3542: #
3543: # Don't allow the creation of privileged accounts!!! that would
3544: # be real bad!!!
3545: #
3546: my $uid = getpwnam($uname);
3547: if((defined $uid) && ($uid == 0)) {
3548: &logthis(">>>Attempted to create privilged account blocked");
3549: return "no_priv_account_error\n";
3550: }
3551:
1.91 albertel 3552: my $execpath="$perlvar{'lonDaemons'}/"."lcuseradd";
3553: {
3554: &Debug("Executing external: ".$execpath);
1.98 foxr 3555: &Debug("user = ".$uname.", Password =". $npass);
1.132 matthew 3556: my $se = IO::File->new("|$execpath > $perlvar{'lonDaemons'}/logs/lcuseradd.log");
1.91 albertel 3557: print $se "$uname\n";
3558: print $se "$npass\n";
3559: print $se "$npass\n";
1.97 foxr 3560: }
3561: my $useraddok = $?;
3562: if($useraddok > 0) {
3563: &logthis("Failed lcuseradd: ".&lcuseraddstrerror($useraddok));
1.91 albertel 3564: }
3565: my $pf = IO::File->new(">$passfilename");
3566: print $pf "unix:\n";
3567: }
3568: } elsif ($umode eq 'none') {
3569: {
3570: my $pf = IO::File->new(">$passfilename");
3571: print $pf "none:\n";
3572: }
3573: } else {
3574: $result="auth_mode_error\n";
3575: }
3576: return $result;
1.121 albertel 3577: }
3578:
3579: sub sethost {
3580: my ($remotereq) = @_;
3581: my (undef,$hostid)=split(/:/,$remotereq);
3582: if (!defined($hostid)) { $hostid=$perlvar{'lonHostID'}; }
3583: if ($hostip{$perlvar{'lonHostID'}} eq $hostip{$hostid}) {
1.200 matthew 3584: $currenthostid =$hostid;
1.121 albertel 3585: $currentdomainid=$hostdom{$hostid};
3586: &logthis("Setting hostid to $hostid, and domain to $currentdomainid");
3587: } else {
3588: &logthis("Requested host id $hostid not an alias of ".
3589: $perlvar{'lonHostID'}." refusing connection");
3590: return 'unable_to_set';
3591: }
3592: return 'ok';
3593: }
3594:
3595: sub version {
3596: my ($userinput)=@_;
3597: $remoteVERSION=(split(/:/,$userinput))[1];
3598: return "version:$VERSION";
1.127 albertel 3599: }
1.178 foxr 3600:
1.128 albertel 3601: #There is a copy of this in lonnet.pm
1.127 albertel 3602: sub userload {
3603: my $numusers=0;
3604: {
3605: opendir(LONIDS,$perlvar{'lonIDsDir'});
3606: my $filename;
3607: my $curtime=time;
3608: while ($filename=readdir(LONIDS)) {
3609: if ($filename eq '.' || $filename eq '..') {next;}
1.138 albertel 3610: my ($mtime)=(stat($perlvar{'lonIDsDir'}.'/'.$filename))[9];
1.159 albertel 3611: if ($curtime-$mtime < 1800) { $numusers++; }
1.127 albertel 3612: }
3613: closedir(LONIDS);
3614: }
3615: my $userloadpercent=0;
3616: my $maxuserload=$perlvar{'lonUserLoadLim'};
3617: if ($maxuserload) {
1.129 albertel 3618: $userloadpercent=100*$numusers/$maxuserload;
1.127 albertel 3619: }
1.130 albertel 3620: $userloadpercent=sprintf("%.2f",$userloadpercent);
1.127 albertel 3621: return $userloadpercent;
1.91 albertel 3622: }
3623:
1.200 matthew 3624:
1.61 harris41 3625: # ----------------------------------- POD (plain old documentation, CPAN style)
3626:
3627: =head1 NAME
3628:
3629: lond - "LON Daemon" Server (port "LOND" 5663)
3630:
3631: =head1 SYNOPSIS
3632:
1.74 harris41 3633: Usage: B<lond>
3634:
3635: Should only be run as user=www. This is a command-line script which
3636: is invoked by B<loncron>. There is no expectation that a typical user
3637: will manually start B<lond> from the command-line. (In other words,
3638: DO NOT START B<lond> YOURSELF.)
1.61 harris41 3639:
3640: =head1 DESCRIPTION
3641:
1.74 harris41 3642: There are two characteristics associated with the running of B<lond>,
3643: PROCESS MANAGEMENT (starting, stopping, handling child processes)
3644: and SERVER-SIDE ACTIVITIES (password authentication, user creation,
3645: subscriptions, etc). These are described in two large
3646: sections below.
3647:
3648: B<PROCESS MANAGEMENT>
3649:
1.61 harris41 3650: Preforker - server who forks first. Runs as a daemon. HUPs.
3651: Uses IDEA encryption
3652:
1.74 harris41 3653: B<lond> forks off children processes that correspond to the other servers
3654: in the network. Management of these processes can be done at the
3655: parent process level or the child process level.
3656:
3657: B<logs/lond.log> is the location of log messages.
3658:
3659: The process management is now explained in terms of linux shell commands,
3660: subroutines internal to this code, and signal assignments:
3661:
3662: =over 4
3663:
3664: =item *
3665:
3666: PID is stored in B<logs/lond.pid>
3667:
3668: This is the process id number of the parent B<lond> process.
3669:
3670: =item *
3671:
3672: SIGTERM and SIGINT
3673:
3674: Parent signal assignment:
3675: $SIG{INT} = $SIG{TERM} = \&HUNTSMAN;
3676:
3677: Child signal assignment:
3678: $SIG{INT} = 'DEFAULT'; (and SIGTERM is DEFAULT also)
3679: (The child dies and a SIGALRM is sent to parent, awaking parent from slumber
3680: to restart a new child.)
3681:
3682: Command-line invocations:
3683: B<kill> B<-s> SIGTERM I<PID>
3684: B<kill> B<-s> SIGINT I<PID>
3685:
3686: Subroutine B<HUNTSMAN>:
3687: This is only invoked for the B<lond> parent I<PID>.
3688: This kills all the children, and then the parent.
3689: The B<lonc.pid> file is cleared.
3690:
3691: =item *
3692:
3693: SIGHUP
3694:
3695: Current bug:
3696: This signal can only be processed the first time
3697: on the parent process. Subsequent SIGHUP signals
3698: have no effect.
3699:
3700: Parent signal assignment:
3701: $SIG{HUP} = \&HUPSMAN;
3702:
3703: Child signal assignment:
3704: none (nothing happens)
3705:
3706: Command-line invocations:
3707: B<kill> B<-s> SIGHUP I<PID>
3708:
3709: Subroutine B<HUPSMAN>:
3710: This is only invoked for the B<lond> parent I<PID>,
3711: This kills all the children, and then the parent.
3712: The B<lond.pid> file is cleared.
3713:
3714: =item *
3715:
3716: SIGUSR1
3717:
3718: Parent signal assignment:
3719: $SIG{USR1} = \&USRMAN;
3720:
3721: Child signal assignment:
3722: $SIG{USR1}= \&logstatus;
3723:
3724: Command-line invocations:
3725: B<kill> B<-s> SIGUSR1 I<PID>
3726:
3727: Subroutine B<USRMAN>:
3728: When invoked for the B<lond> parent I<PID>,
3729: SIGUSR1 is sent to all the children, and the status of
3730: each connection is logged.
1.144 foxr 3731:
3732: =item *
3733:
3734: SIGUSR2
3735:
3736: Parent Signal assignment:
3737: $SIG{USR2} = \&UpdateHosts
3738:
3739: Child signal assignment:
3740: NONE
3741:
1.74 harris41 3742:
3743: =item *
3744:
3745: SIGCHLD
3746:
3747: Parent signal assignment:
3748: $SIG{CHLD} = \&REAPER;
3749:
3750: Child signal assignment:
3751: none
3752:
3753: Command-line invocations:
3754: B<kill> B<-s> SIGCHLD I<PID>
3755:
3756: Subroutine B<REAPER>:
3757: This is only invoked for the B<lond> parent I<PID>.
3758: Information pertaining to the child is removed.
3759: The socket port is cleaned up.
3760:
3761: =back
3762:
3763: B<SERVER-SIDE ACTIVITIES>
3764:
3765: Server-side information can be accepted in an encrypted or non-encrypted
3766: method.
3767:
3768: =over 4
3769:
3770: =item ping
3771:
3772: Query a client in the hosts.tab table; "Are you there?"
3773:
3774: =item pong
3775:
3776: Respond to a ping query.
3777:
3778: =item ekey
3779:
3780: Read in encrypted key, make cipher. Respond with a buildkey.
3781:
3782: =item load
3783:
3784: Respond with CPU load based on a computation upon /proc/loadavg.
3785:
3786: =item currentauth
3787:
3788: Reply with current authentication information (only over an
3789: encrypted channel).
3790:
3791: =item auth
3792:
3793: Only over an encrypted channel, reply as to whether a user's
3794: authentication information can be validated.
3795:
3796: =item passwd
3797:
3798: Allow for a password to be set.
3799:
3800: =item makeuser
3801:
3802: Make a user.
3803:
3804: =item passwd
3805:
3806: Allow for authentication mechanism and password to be changed.
3807:
3808: =item home
1.61 harris41 3809:
1.74 harris41 3810: Respond to a question "are you the home for a given user?"
3811:
3812: =item update
3813:
3814: Update contents of a subscribed resource.
3815:
3816: =item unsubscribe
3817:
3818: The server is unsubscribing from a resource.
3819:
3820: =item subscribe
3821:
3822: The server is subscribing to a resource.
3823:
3824: =item log
3825:
3826: Place in B<logs/lond.log>
3827:
3828: =item put
3829:
3830: stores hash in namespace
3831:
3832: =item rolesput
3833:
3834: put a role into a user's environment
3835:
3836: =item get
3837:
3838: returns hash with keys from array
3839: reference filled in from namespace
3840:
3841: =item eget
3842:
3843: returns hash with keys from array
3844: reference filled in from namesp (encrypts the return communication)
3845:
3846: =item rolesget
3847:
3848: get a role from a user's environment
3849:
3850: =item del
3851:
3852: deletes keys out of array from namespace
3853:
3854: =item keys
3855:
3856: returns namespace keys
3857:
3858: =item dump
3859:
3860: dumps the complete (or key matching regexp) namespace into a hash
3861:
3862: =item store
3863:
3864: stores hash permanently
3865: for this url; hashref needs to be given and should be a \%hashname; the
3866: remaining args aren't required and if they aren't passed or are '' they will
3867: be derived from the ENV
3868:
3869: =item restore
3870:
3871: returns a hash for a given url
3872:
3873: =item querysend
3874:
3875: Tells client about the lonsql process that has been launched in response
3876: to a sent query.
3877:
3878: =item queryreply
3879:
3880: Accept information from lonsql and make appropriate storage in temporary
3881: file space.
3882:
3883: =item idput
3884:
3885: Defines usernames as corresponding to IDs. (These "IDs" are unique identifiers
3886: for each student, defined perhaps by the institutional Registrar.)
3887:
3888: =item idget
3889:
3890: Returns usernames corresponding to IDs. (These "IDs" are unique identifiers
3891: for each student, defined perhaps by the institutional Registrar.)
3892:
3893: =item tmpput
3894:
3895: Accept and store information in temporary space.
3896:
3897: =item tmpget
3898:
3899: Send along temporarily stored information.
3900:
3901: =item ls
3902:
3903: List part of a user's directory.
3904:
1.135 foxr 3905: =item pushtable
3906:
3907: Pushes a file in /home/httpd/lonTab directory. Currently limited to:
3908: hosts.tab and domain.tab. The old file is copied to *.tab.backup but
3909: must be restored manually in case of a problem with the new table file.
3910: pushtable requires that the request be encrypted and validated via
3911: ValidateManager. The form of the command is:
3912: enc:pushtable tablename <tablecontents> \n
3913: where pushtable, tablename and <tablecontents> will be encrypted, but \n is a
3914: cleartext newline.
3915:
1.74 harris41 3916: =item Hanging up (exit or init)
3917:
3918: What to do when a client tells the server that they (the client)
3919: are leaving the network.
3920:
3921: =item unknown command
3922:
3923: If B<lond> is sent an unknown command (not in the list above),
3924: it replys to the client "unknown_cmd".
1.135 foxr 3925:
1.74 harris41 3926:
3927: =item UNKNOWN CLIENT
3928:
3929: If the anti-spoofing algorithm cannot verify the client,
3930: the client is rejected (with a "refused" message sent
3931: to the client, and the connection is closed.
3932:
3933: =back
1.61 harris41 3934:
3935: =head1 PREREQUISITES
3936:
3937: IO::Socket
3938: IO::File
3939: Apache::File
3940: Symbol
3941: POSIX
3942: Crypt::IDEA
3943: LWP::UserAgent()
3944: GDBM_File
3945: Authen::Krb4
1.91 albertel 3946: Authen::Krb5
1.61 harris41 3947:
3948: =head1 COREQUISITES
3949:
3950: =head1 OSNAMES
3951:
3952: linux
3953:
3954: =head1 SCRIPT CATEGORIES
3955:
3956: Server/Process
3957:
3958: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>