1: #!/usr/bin/perl
2: # The LearningOnline Network
3: # lond "LON Daemon" Server (port "LOND" 5663)
4: #
5: # $Id: lond,v 1.152 2003/10/08 14:18:34 www Exp $
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
13: # the Free Software Foundation; either version 2 of the License, or
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
23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24: #
25: # /home/httpd/html/adm/gpl.txt
26: #
27: # http://www.lon-capa.org/
28: #
29: # 5/26/99,6/4,6/10,6/11,6/14,6/15,6/26,6/28,6/30,
30: # 7/8,7/9,7/10,7/12,7/17,7/19,9/21,
31: # 10/7,10/8,10/9,10/11,10/13,10/15,11/4,11/16,
32: # 12/7,12/15,01/06,01/11,01/12,01/14,2/8,
33: # 03/07,05/31 Gerd Kortemeyer
34: # 06/29,06/30,07/14,07/15,07/17,07/20,07/25,09/18 Gerd Kortemeyer
35: # 12/05,12/13,12/29 Gerd Kortemeyer
36: # YEAR=2001
37: # 02/12 Gerd Kortemeyer
38: # 03/24 Gerd Kortemeyer
39: # 05/11,05/28,08/30 Gerd Kortemeyer
40: # 11/26,11/27 Gerd Kortemeyer
41: # 12/22 Gerd Kortemeyer
42: # YEAR=2002
43: # 01/20/02,02/05 Gerd Kortemeyer
44: # 02/05 Guy Albertelli
45: # 02/12 Gerd Kortemeyer
46: # 02/19 Matthew Hall
47: # 02/25 Gerd Kortemeyer
48: # 01/xx/2003 Ron Fox.. Remove preforking. This makes the general daemon
49: # logic simpler (and there were problems maintaining the preforked
50: # population). Since the time averaged connection rate is close to zero
51: # because lonc's purpose is to maintain near continuous connnections,
52: # preforking is not really needed.
53: # 08/xx/2003 Ron Fox: Add management requests. Management requests
54: # will be validated via a call to ValidateManager. At present, this
55: # is done by simple host verification. In the future we can modify
56: # this function to do a certificate check.
57: # Management functions supported include:
58: # - pushing /home/httpd/lonTabs/hosts.tab
59: # - pushing /home/httpd/lonTabs/domain.tab
60: # 09/08/2003 Ron Fox: Told lond to take care of change logging so we
61: # don't have to remember it:
62: # $Log: lond,v $
63: # Revision 1.152 2003/10/08 14:18:34 www
64: # Not good: this should be backported into 1.0.2!
65: #
66: # Revision 1.151 2003/10/03 15:11:03 albertel
67: # - if we fail to fetch an update to the file, don't blow away the old one
68: # (this was the BUG that blew away that one default.sequence that Matthew
69: # ended up restoring from data.)
70: #
71: # Revision 1.150 2003/09/30 10:16:06 foxr
72: # Added invocation of apachereload in ReloadApache sub.
73: # This completes the addtion of the reinit functionality.
74: #
75: # Revision 1.149 2003/09/30 09:44:13 foxr
76: # Tested UpdateHosts ability to
77: # - Remove live children for hosts that are no longer in the hosts.tab
78: # - Remove live children for hosts whose IPs have changed in the hosts.tab
79: #
80: # Revision 1.148 2003/09/29 10:09:18 foxr
81: # Put in logic to reinit lond itself (except for apache reload). I don't believe
82: # this logic works correctly yet, however lond still does everything it used to doso I'll do the commit anyway.
83: #
84: # Revision 1.147 2003/09/23 11:23:31 foxr
85: # Comlplete implementation of reinit functionality. Must still implement
86: # the actual initialization functionality, but the process can now
87: # receive the request and either invoke the appropriate internal function or
88: # signal the correct lonc.
89: #
90: # Revision 1.146 2003/09/16 10:28:14 foxr
91: # ReinitProcess - decode the process selector and produce the associated pid
92: # filename. Note: While it is possible to test that valid process selectors are
93: # handled properly I am not able to test that invalid process selectors produce
94: # the appropriate error as lonManage also blocks the use of invalid process selectors.
95: #
96: # Revision 1.145 2003/09/16 10:13:20 foxr
97: # Added ReinitProcess function to oversee the parsing and processing of the
98: # reinit:<process> client request.
99: #
100: # Revision 1.144 2003/09/16 09:47:01 foxr
101: # Added skeletal support for SIGUSR2 (update hosts.tab)
102: #
103: # Revision 1.143 2003/09/15 10:03:52 foxr
104: # Completed and tested code for pushfile.
105: #
106: # Revision 1.142 2003/09/09 20:47:46 www
107: # Permanently store chatroom entries in chatroom.log
108: #
109: # Revision 1.141 2003/09/08 10:32:07 foxr
110: # Added PushFile sub This sub oversees the push of a new configuration table file
111: # Currently supported files are:
112: # - hosts.tab (transaction pushfile:hosts:contents)
113: # - domain.tab (transaction pushfile:domain:contents)
114: #
115:
116:
117: use strict;
118: use lib '/home/httpd/lib/perl/';
119: use LONCAPA::Configuration;
120:
121: use IO::Socket;
122: use IO::File;
123: #use Apache::File;
124: use Symbol;
125: use POSIX;
126: use Crypt::IDEA;
127: use LWP::UserAgent();
128: use GDBM_File;
129: use Authen::Krb4;
130: use Authen::Krb5;
131: use lib '/home/httpd/lib/perl/';
132: use localauth;
133: use File::Copy;
134:
135: my $DEBUG = 0; # Non zero to enable debug log entries.
136:
137: my $status='';
138: my $lastlog='';
139:
140: my $VERSION='$Revision: 1.152 $'; #' stupid emacs
141: my $remoteVERSION;
142: my $currenthostid;
143: my $currentdomainid;
144:
145: my $client;
146: my $clientip;
147:
148: my $server;
149: my $thisserver;
150:
151: my %hostid;
152: my %hostdom;
153: my %hostip;
154: my %perlvar; # Will have the apache conf defined perl vars.
155:
156: #
157: # The array below are password error strings."
158: #
159: my $lastpwderror = 13; # Largest error number from lcpasswd.
160: my @passwderrors = ("ok",
161: "lcpasswd must be run as user 'www'",
162: "lcpasswd got incorrect number of arguments",
163: "lcpasswd did not get the right nubmer of input text lines",
164: "lcpasswd too many simultaneous pwd changes in progress",
165: "lcpasswd User does not exist.",
166: "lcpasswd Incorrect current passwd",
167: "lcpasswd Unable to su to root.",
168: "lcpasswd Cannot set new passwd.",
169: "lcpasswd Username has invalid characters",
170: "lcpasswd Invalid characters in password",
171: "11", "12",
172: "lcpasswd Password mismatch");
173:
174:
175: # The array below are lcuseradd error strings.:
176:
177: my $lastadderror = 13;
178: my @adderrors = ("ok",
179: "User ID mismatch, lcuseradd must run as user www",
180: "lcuseradd Incorrect number of command line parameters must be 3",
181: "lcuseradd Incorrect number of stdinput lines, must be 3",
182: "lcuseradd Too many other simultaneous pwd changes in progress",
183: "lcuseradd User does not exist",
184: "lcuseradd Unabel to mak ewww member of users's group",
185: "lcuseradd Unable to su to root",
186: "lcuseradd Unable to set password",
187: "lcuseradd Usrname has invbalid charcters",
188: "lcuseradd Password has an invalid character",
189: "lcuseradd User already exists",
190: "lcuseradd Could not add user.",
191: "lcuseradd Password mismatch");
192:
193:
194: #
195: # GetCertificate: Given a transaction that requires a certificate,
196: # this function will extract the certificate from the transaction
197: # request. Note that at this point, the only concept of a certificate
198: # is the hostname to which we are connected.
199: #
200: # Parameter:
201: # request - The request sent by our client (this parameterization may
202: # need to change when we really use a certificate granting
203: # authority.
204: #
205: sub GetCertificate {
206: my $request = shift;
207:
208: return $clientip;
209: }
210:
211:
212: #
213: # ValidManager: Determines if a given certificate represents a valid manager.
214: # in this primitive implementation, the 'certificate' is
215: # just the connecting loncapa client name. This is checked
216: # against a valid client list in the configuration.
217: #
218: #
219: sub ValidManager {
220: my $certificate = shift;
221:
222: my $hostentry = $hostid{$certificate};
223: if ($hostentry ne undef) {
224: &logthis('<font color="yellow">Authenticating manager'.
225: " $hostentry</font>");
226: return 1;
227: } else {
228: &logthis('<font color="red"> Failed manager authentication '.
229: "$certificate </font>");
230: }
231: }
232: #
233: # CopyFile: Called as part of the process of installing a
234: # new configuration file. This function copies an existing
235: # file to a backup file.
236: # Parameters:
237: # oldfile - Name of the file to backup.
238: # newfile - Name of the backup file.
239: # Return:
240: # 0 - Failure (errno has failure reason).
241: # 1 - Success.
242: #
243: sub CopyFile {
244: my $oldfile = shift;
245: my $newfile = shift;
246:
247: # The file must exist:
248:
249: if(-e $oldfile) {
250:
251: # Read the old file.
252:
253: my $oldfh = IO::File->new("< $oldfile");
254: if(!$oldfh) {
255: return 0;
256: }
257: my @contents = <$oldfh>; # Suck in the entire file.
258:
259: # write the backup file:
260:
261: my $newfh = IO::File->new("> $newfile");
262: if(!(defined $newfh)){
263: return 0;
264: }
265: my $lines = scalar @contents;
266: for (my $i =0; $i < $lines; $i++) {
267: print $newfh ($contents[$i]);
268: }
269:
270: $oldfh->close;
271: $newfh->close;
272:
273: chmod(0660, $newfile);
274:
275: return 1;
276:
277: } else {
278: return 0;
279: }
280: }
281:
282: #
283: # InstallFile: Called to install an administrative file:
284: # - The file is created with <name>.tmp
285: # - The <name>.tmp file is then mv'd to <name>
286: # This lugubrious procedure is done to ensure that we are never without
287: # a valid, even if dated, version of the file regardless of who crashes
288: # and when the crash occurs.
289: #
290: # Parameters:
291: # Name of the file
292: # File Contents.
293: # Return:
294: # nonzero - success.
295: # 0 - failure and $! has an errno.
296: #
297: sub InstallFile {
298: my $Filename = shift;
299: my $Contents = shift;
300: my $TempFile = $Filename.".tmp";
301:
302: # Open the file for write:
303:
304: my $fh = IO::File->new("> $TempFile"); # Write to temp.
305: if(!(defined $fh)) {
306: &logthis('<font color="red"> Unable to create '.$TempFile."</font>");
307: return 0;
308: }
309: # write the contents of the file:
310:
311: print $fh ($Contents);
312: $fh->close; # In case we ever have a filesystem w. locking
313:
314: chmod(0660, $TempFile);
315:
316: # Now we can move install the file in position.
317:
318: move($TempFile, $Filename);
319:
320: return 1;
321: }
322:
323: #
324: # PushFile: Called to do an administrative push of a file.
325: # - Ensure the file being pushed is one we support.
326: # - Backup the old file to <filename.saved>
327: # - Separate the contents of the new file out from the
328: # rest of the request.
329: # - Write the new file.
330: # Parameter:
331: # Request - The entire user request. This consists of a : separated
332: # string pushfile:tablename:contents.
333: # NOTE: The contents may have :'s in it as well making things a bit
334: # more interesting... but not much.
335: # Returns:
336: # String to send to client ("ok" or "refused" if bad file).
337: #
338: sub PushFile {
339: my $request = shift;
340: my ($command, $filename, $contents) = split(":", $request, 3);
341:
342: # At this point in time, pushes for only the following tables are
343: # supported:
344: # hosts.tab ($filename eq host).
345: # domain.tab ($filename eq domain).
346: # Construct the destination filename or reject the request.
347: #
348: # lonManage is supposed to ensure this, however this session could be
349: # part of some elaborate spoof that managed somehow to authenticate.
350: #
351:
352: my $tablefile = $perlvar{'lonTabDir'}.'/'; # need to precede with dir.
353: if ($filename eq "host") {
354: $tablefile .= "hosts.tab";
355: } elsif ($filename eq "domain") {
356: $tablefile .= "domain.tab";
357: } else {
358: return "refused";
359: }
360: #
361: # >copy< the old table to the backup table
362: # don't rename in case system crashes/reboots etc. in the time
363: # window between a rename and write.
364: #
365: my $backupfile = $tablefile;
366: $backupfile =~ s/\.tab$/.old/;
367: if(!CopyFile($tablefile, $backupfile)) {
368: &logthis('<font color="green"> CopyFile from '.$tablefile." to ".$backupfile." failed </font>");
369: return "error:$!";
370: }
371: &logthis('<font color="green"> Pushfile: backed up '
372: .$tablefile." to $backupfile</font>");
373:
374: # Install the new file:
375:
376: if(!InstallFile($tablefile, $contents)) {
377: &logthis('<font color="red"> Pushfile: unable to install '
378: .$tablefile." $! </font>");
379: return "error:$!";
380: }
381: else {
382: &logthis('<font color="green"> Installed new '.$tablefile
383: ."</font>");
384:
385: }
386:
387:
388: # Indicate success:
389:
390: return "ok";
391:
392: }
393:
394: #
395: # Called to re-init either lonc or lond.
396: #
397: # Parameters:
398: # request - The full request by the client. This is of the form
399: # reinit:<process>
400: # where <process> is allowed to be either of
401: # lonc or lond
402: #
403: # Returns:
404: # The string to be sent back to the client either:
405: # ok - Everything worked just fine.
406: # error:why - There was a failure and why describes the reason.
407: #
408: #
409: sub ReinitProcess {
410: my $request = shift;
411:
412:
413: # separate the request (reinit) from the process identifier and
414: # validate it producing the name of the .pid file for the process.
415: #
416: #
417: my ($junk, $process) = split(":", $request);
418: my $processpidfile = $perlvar{'lonDaemons'}.'/logs/';
419: if($process eq 'lonc') {
420: $processpidfile = $processpidfile."lonc.pid";
421: if (!open(PIDFILE, "< $processpidfile")) {
422: return "error:Open failed for $processpidfile";
423: }
424: my $loncpid = <PIDFILE>;
425: close(PIDFILE);
426: logthis('<font color="red"> Reinitializing lonc pid='.$loncpid
427: ."</font>");
428: kill("USR2", $loncpid);
429: } elsif ($process eq 'lond') {
430: logthis('<font color="red"> Reinitializing self (lond) </font>');
431: &UpdateHosts; # Lond is us!!
432: } else {
433: &logthis('<font color="yellow" Invalid reinit request for '.$process
434: ."</font>");
435: return "error:Invalid process identifier $process";
436: }
437: return 'ok';
438: }
439:
440: #
441: # Convert an error return code from lcpasswd to a string value.
442: #
443: sub lcpasswdstrerror {
444: my $ErrorCode = shift;
445: if(($ErrorCode < 0) || ($ErrorCode > $lastpwderror)) {
446: return "lcpasswd Unrecognized error return value ".$ErrorCode;
447: } else {
448: return $passwderrors[$ErrorCode];
449: }
450: }
451:
452: #
453: # Convert an error return code from lcuseradd to a string value:
454: #
455: sub lcuseraddstrerror {
456: my $ErrorCode = shift;
457: if(($ErrorCode < 0) || ($ErrorCode > $lastadderror)) {
458: return "lcuseradd - Unrecognized error code: ".$ErrorCode;
459: } else {
460: return $adderrors[$ErrorCode];
461: }
462: }
463:
464: # grabs exception and records it to log before exiting
465: sub catchexception {
466: my ($error)=@_;
467: $SIG{'QUIT'}='DEFAULT';
468: $SIG{__DIE__}='DEFAULT';
469: &logthis("<font color=red>CRITICAL: "
470: ."ABNORMAL EXIT. Child $$ for server $thisserver died through "
471: ."a crash with this error msg->[$error]</font>");
472: &logthis('Famous last words: '.$status.' - '.$lastlog);
473: if ($client) { print $client "error: $error\n"; }
474: $server->close();
475: die($error);
476: }
477:
478: sub timeout {
479: &logthis("<font color=ref>CRITICAL: TIME OUT ".$$."</font>");
480: &catchexception('Timeout');
481: }
482: # -------------------------------- Set signal handlers to record abnormal exits
483:
484: $SIG{'QUIT'}=\&catchexception;
485: $SIG{__DIE__}=\&catchexception;
486:
487: # ---------------------------------- Read loncapa_apache.conf and loncapa.conf
488: &status("Read loncapa.conf and loncapa_apache.conf");
489: my $perlvarref=LONCAPA::Configuration::read_conf('loncapa.conf');
490: %perlvar=%{$perlvarref};
491: undef $perlvarref;
492:
493: # ----------------------------- Make sure this process is running from user=www
494: my $wwwid=getpwnam('www');
495: if ($wwwid!=$<) {
496: my $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
497: my $subj="LON: $currenthostid User ID mismatch";
498: system("echo 'User ID mismatch. lond must be run as user www.' |\
499: mailto $emailto -s '$subj' > /dev/null");
500: exit 1;
501: }
502:
503: # --------------------------------------------- Check if other instance running
504:
505: my $pidfile="$perlvar{'lonDaemons'}/logs/lond.pid";
506:
507: if (-e $pidfile) {
508: my $lfh=IO::File->new("$pidfile");
509: my $pide=<$lfh>;
510: chomp($pide);
511: if (kill 0 => $pide) { die "already running"; }
512: }
513:
514: # ------------------------------------------------------------- Read hosts file
515:
516:
517:
518: # establish SERVER socket, bind and listen.
519: $server = IO::Socket::INET->new(LocalPort => $perlvar{'londPort'},
520: Type => SOCK_STREAM,
521: Proto => 'tcp',
522: Reuse => 1,
523: Listen => 10 )
524: or die "making socket: $@\n";
525:
526: # --------------------------------------------------------- Do global variables
527:
528: # global variables
529:
530: my %children = (); # keys are current child process IDs
531: my $children = 0; # current number of children
532:
533: sub REAPER { # takes care of dead children
534: $SIG{CHLD} = \&REAPER;
535: my $pid = wait;
536: if (defined($children{$pid})) {
537: &logthis("Child $pid died");
538: $children --;
539: delete $children{$pid};
540: } else {
541: &logthis("Unknown Child $pid died");
542: }
543: }
544:
545: sub HUNTSMAN { # signal handler for SIGINT
546: local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children
547: kill 'INT' => keys %children;
548: &logthis("Free socket: ".shutdown($server,2)); # free up socket
549: my $execdir=$perlvar{'lonDaemons'};
550: unlink("$execdir/logs/lond.pid");
551: &logthis("<font color=red>CRITICAL: Shutting down</font>");
552: exit; # clean up with dignity
553: }
554:
555: sub HUPSMAN { # signal handler for SIGHUP
556: local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children
557: kill 'INT' => keys %children;
558: &logthis("Free socket: ".shutdown($server,2)); # free up socket
559: &logthis("<font color=red>CRITICAL: Restarting</font>");
560: my $execdir=$perlvar{'lonDaemons'};
561: unlink("$execdir/logs/lond.pid");
562: exec("$execdir/lond"); # here we go again
563: }
564:
565: #
566: # Kill off hashes that describe the host table prior to re-reading it.
567: # Hashes affected are:
568: # %hostid, %hostdom %hostip
569: #
570: sub KillHostHashes {
571: foreach my $key (keys %hostid) {
572: delete $hostid{$key};
573: }
574: foreach my $key (keys %hostdom) {
575: delete $hostdom{$key};
576: }
577: foreach my $key (keys %hostip) {
578: delete $hostip{$key};
579: }
580: }
581: #
582: # Read in the host table from file and distribute it into the various hashes:
583: #
584: # - %hostid - Indexed by IP, the loncapa hostname.
585: # - %hostdom - Indexed by loncapa hostname, the domain.
586: # - %hostip - Indexed by hostid, the Ip address of the host.
587: sub ReadHostTable {
588:
589: open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";
590:
591: while (my $configline=<CONFIG>) {
592: my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);
593: chomp($ip); $ip=~s/\D+$//;
594: $hostid{$ip}=$id;
595: $hostdom{$id}=$domain;
596: $hostip{$id}=$ip;
597: if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; }
598: }
599: close(CONFIG);
600: }
601: #
602: # Reload the Apache daemon's state.
603: # This is done by invoking /home/httpd/perl/apachereload
604: # a setuid perl script that can be root for us to do this job.
605: #
606: sub ReloadApache {
607: my $execdir = $perlvar{'lonDaemons'};
608: my $script = $execdir."/apachereload";
609: system($script);
610: }
611:
612: #
613: # Called in response to a USR2 signal.
614: # - Reread hosts.tab
615: # - All children connected to hosts that were removed from hosts.tab
616: # are killed via SIGINT
617: # - All children connected to previously existing hosts are sent SIGUSR1
618: # - Our internal hosts hash is updated to reflect the new contents of
619: # hosts.tab causing connections from hosts added to hosts.tab to
620: # now be honored.
621: #
622: sub UpdateHosts {
623: logthis('<font color="blue"> Updating connections </font>');
624: #
625: # The %children hash has the set of IP's we currently have children
626: # on. These need to be matched against records in the hosts.tab
627: # Any ip's no longer in the table get killed off they correspond to
628: # either dropped or changed hosts. Note that the re-read of the table
629: # will take care of new and changed hosts as connections come into being.
630:
631:
632: KillHostHashes;
633: ReadHostTable;
634:
635: foreach my $child (keys %children) {
636: my $childip = $children{$child};
637: if(!$hostid{$childip}) {
638: logthis('<font color="blue"> UpdateHosts killing child '
639: ." $child for ip $childip </font>");
640: kill('INT', $child);
641: } else {
642: logthis('<font color="green"> keeping child for ip '
643: ." $childip (pid=$child) </font>");
644: }
645: }
646: ReloadApache;
647: }
648:
649:
650: sub checkchildren {
651: &initnewstatus();
652: &logstatus();
653: &logthis('Going to check on the children');
654: my $docdir=$perlvar{'lonDocRoot'};
655: foreach (sort keys %children) {
656: sleep 1;
657: unless (kill 'USR1' => $_) {
658: &logthis ('Child '.$_.' is dead');
659: &logstatus($$.' is dead');
660: }
661: }
662: sleep 5;
663: $SIG{ALRM} = sub { die "timeout" };
664: $SIG{__DIE__} = 'DEFAULT';
665: foreach (sort keys %children) {
666: unless (-e "$docdir/lon-status/londchld/$_.txt") {
667: eval {
668: alarm(300);
669: &logthis('Child '.$_.' did not respond');
670: kill 9 => $_;
671: #$emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
672: #$subj="LON: $currenthostid killed lond process $_";
673: #my $result=`echo 'Killed lond process $_.' | mailto $emailto -s '$subj' > /dev/null`;
674: #$execdir=$perlvar{'lonDaemons'};
675: #$result=`/bin/cp $execdir/logs/lond.log $execdir/logs/lond.log.$_`;
676: alarm(0);
677: }
678: }
679: }
680: $SIG{ALRM} = 'DEFAULT';
681: $SIG{__DIE__} = \&catchcexception;
682: }
683:
684: # --------------------------------------------------------------------- Logging
685:
686: sub logthis {
687: my $message=shift;
688: my $execdir=$perlvar{'lonDaemons'};
689: my $fh=IO::File->new(">>$execdir/logs/lond.log");
690: my $now=time;
691: my $local=localtime($now);
692: $lastlog=$local.': '.$message;
693: print $fh "$local ($$): $message\n";
694: }
695:
696: # ------------------------- Conditional log if $DEBUG true.
697: sub Debug {
698: my $message = shift;
699: if($DEBUG) {
700: &logthis($message);
701: }
702: }
703: # ------------------------------------------------------------------ Log status
704:
705: sub logstatus {
706: my $docdir=$perlvar{'lonDocRoot'};
707: {
708: my $fh=IO::File->new(">>$docdir/lon-status/londstatus.txt");
709: print $fh $$."\t".$currenthostid."\t".$status."\t".$lastlog."\n";
710: $fh->close();
711: }
712: {
713: my $fh=IO::File->new(">$docdir/lon-status/londchld/$$.txt");
714: print $fh $status."\n".$lastlog."\n".time;
715: $fh->close();
716: }
717: }
718:
719: sub initnewstatus {
720: my $docdir=$perlvar{'lonDocRoot'};
721: my $fh=IO::File->new(">$docdir/lon-status/londstatus.txt");
722: my $now=time;
723: my $local=localtime($now);
724: print $fh "LOND status $local - parent $$\n\n";
725: opendir(DIR,"$docdir/lon-status/londchld");
726: while (my $filename=readdir(DIR)) {
727: unlink("$docdir/lon-status/londchld/$filename");
728: }
729: closedir(DIR);
730: }
731:
732: # -------------------------------------------------------------- Status setting
733:
734: sub status {
735: my $what=shift;
736: my $now=time;
737: my $local=localtime($now);
738: $status=$local.': '.$what;
739: $0='lond: '.$what.' '.$local;
740: }
741:
742: # -------------------------------------------------------- Escape Special Chars
743:
744: sub escape {
745: my $str=shift;
746: $str =~ s/(\W)/"%".unpack('H2',$1)/eg;
747: return $str;
748: }
749:
750: # ----------------------------------------------------- Un-Escape Special Chars
751:
752: sub unescape {
753: my $str=shift;
754: $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
755: return $str;
756: }
757:
758: # ----------------------------------------------------------- Send USR1 to lonc
759:
760: sub reconlonc {
761: my $peerfile=shift;
762: &logthis("Trying to reconnect for $peerfile");
763: my $loncfile="$perlvar{'lonDaemons'}/logs/lonc.pid";
764: if (my $fh=IO::File->new("$loncfile")) {
765: my $loncpid=<$fh>;
766: chomp($loncpid);
767: if (kill 0 => $loncpid) {
768: &logthis("lonc at pid $loncpid responding, sending USR1");
769: kill USR1 => $loncpid;
770: } else {
771: &logthis(
772: "<font color=red>CRITICAL: "
773: ."lonc at pid $loncpid not responding, giving up</font>");
774: }
775: } else {
776: &logthis('<font color=red>CRITICAL: lonc not running, giving up</font>');
777: }
778: }
779:
780: # -------------------------------------------------- Non-critical communication
781:
782: sub subreply {
783: my ($cmd,$server)=@_;
784: my $peerfile="$perlvar{'lonSockDir'}/$server";
785: my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile",
786: Type => SOCK_STREAM,
787: Timeout => 10)
788: or return "con_lost";
789: print $sclient "$cmd\n";
790: my $answer=<$sclient>;
791: chomp($answer);
792: if (!$answer) { $answer="con_lost"; }
793: return $answer;
794: }
795:
796: sub reply {
797: my ($cmd,$server)=@_;
798: my $answer;
799: if ($server ne $currenthostid) {
800: $answer=subreply($cmd,$server);
801: if ($answer eq 'con_lost') {
802: $answer=subreply("ping",$server);
803: if ($answer ne $server) {
804: &logthis("sub reply: answer != server answer is $answer, server is $server");
805: &reconlonc("$perlvar{'lonSockDir'}/$server");
806: }
807: $answer=subreply($cmd,$server);
808: }
809: } else {
810: $answer='self_reply';
811: }
812: return $answer;
813: }
814:
815: # -------------------------------------------------------------- Talk to lonsql
816:
817: sub sqlreply {
818: my ($cmd)=@_;
819: my $answer=subsqlreply($cmd);
820: if ($answer eq 'con_lost') { $answer=subsqlreply($cmd); }
821: return $answer;
822: }
823:
824: sub subsqlreply {
825: my ($cmd)=@_;
826: my $unixsock="mysqlsock";
827: my $peerfile="$perlvar{'lonSockDir'}/$unixsock";
828: my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile",
829: Type => SOCK_STREAM,
830: Timeout => 10)
831: or return "con_lost";
832: print $sclient "$cmd\n";
833: my $answer=<$sclient>;
834: chomp($answer);
835: if (!$answer) { $answer="con_lost"; }
836: return $answer;
837: }
838:
839: # -------------------------------------------- Return path to profile directory
840:
841: sub propath {
842: my ($udom,$uname)=@_;
843: $udom=~s/\W//g;
844: $uname=~s/\W//g;
845: my $subdir=$uname.'__';
846: $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
847: my $proname="$perlvar{'lonUsersDir'}/$udom/$subdir/$uname";
848: return $proname;
849: }
850:
851: # --------------------------------------- Is this the home server of an author?
852:
853: sub ishome {
854: my $author=shift;
855: $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
856: my ($udom,$uname)=split(/\//,$author);
857: my $proname=propath($udom,$uname);
858: if (-e $proname) {
859: return 'owner';
860: } else {
861: return 'not_owner';
862: }
863: }
864:
865: # ======================================================= Continue main program
866: # ---------------------------------------------------- Fork once and dissociate
867:
868: my $fpid=fork;
869: exit if $fpid;
870: die "Couldn't fork: $!" unless defined ($fpid);
871:
872: POSIX::setsid() or die "Can't start new session: $!";
873:
874: # ------------------------------------------------------- Write our PID on disk
875:
876: my $execdir=$perlvar{'lonDaemons'};
877: open (PIDSAVE,">$execdir/logs/lond.pid");
878: print PIDSAVE "$$\n";
879: close(PIDSAVE);
880: &logthis("<font color=red>CRITICAL: ---------- Starting ----------</font>");
881: &status('Starting');
882:
883:
884:
885: # ----------------------------------------------------- Install signal handlers
886:
887:
888: $SIG{CHLD} = \&REAPER;
889: $SIG{INT} = $SIG{TERM} = \&HUNTSMAN;
890: $SIG{HUP} = \&HUPSMAN;
891: $SIG{USR1} = \&checkchildren;
892: $SIG{USR2} = \&UpdateHosts;
893:
894: # Read the host hashes:
895:
896: ReadHostTable;
897:
898: # --------------------------------------------------------------
899: # Accept connections. When a connection comes in, it is validated
900: # and if good, a child process is created to process transactions
901: # along the connection.
902:
903: while (1) {
904: $client = $server->accept() or next;
905: make_new_child($client);
906: }
907:
908: sub make_new_child {
909: my $pid;
910: my $cipher;
911: my $sigset;
912:
913: $client = shift;
914: &logthis("Attempting to start child");
915: # block signal for fork
916: $sigset = POSIX::SigSet->new(SIGINT);
917: sigprocmask(SIG_BLOCK, $sigset)
918: or die "Can't block SIGINT for fork: $!\n";
919:
920: die "fork: $!" unless defined ($pid = fork);
921:
922: $client->sockopt(SO_KEEPALIVE, 1); # Enable monitoring of
923: # connection liveness.
924:
925: #
926: # Figure out who we're talking to so we can record the peer in
927: # the pid hash.
928: #
929: my $caller = getpeername($client);
930: my ($port,$iaddr)=unpack_sockaddr_in($caller);
931: $clientip=inet_ntoa($iaddr);
932:
933: if ($pid) {
934: # Parent records the child's birth and returns.
935: sigprocmask(SIG_UNBLOCK, $sigset)
936: or die "Can't unblock SIGINT for fork: $!\n";
937: $children{$pid} = $clientip;
938: $children++;
939: &status('Started child '.$pid);
940: return;
941: } else {
942: # Child can *not* return from this subroutine.
943: $SIG{INT} = 'DEFAULT'; # make SIGINT kill us as it did before
944: $SIG{CHLD} = 'DEFAULT'; #make this default so that pwauth returns
945: #don't get intercepted
946: $SIG{USR1}= \&logstatus;
947: $SIG{ALRM}= \&timeout;
948: $lastlog='Forked ';
949: $status='Forked';
950:
951: # unblock signals
952: sigprocmask(SIG_UNBLOCK, $sigset)
953: or die "Can't unblock SIGINT for fork: $!\n";
954:
955: my $tmpsnum=0;
956: #---------------------------------------------------- kerberos 5 initialization
957: &Authen::Krb5::init_context();
958: &Authen::Krb5::init_ets();
959:
960: &status('Accepted connection');
961: # =============================================================================
962: # do something with the connection
963: # -----------------------------------------------------------------------------
964: # see if we know client and check for spoof IP by challenge
965:
966: my $clientrec=($hostid{$clientip} ne undef);
967: &logthis(
968: "<font color=yellow>INFO: Connection, $clientip ($hostid{$clientip})</font>"
969: );
970: &status("Connecting $clientip ($hostid{$clientip})");
971: my $clientok;
972: if ($clientrec) {
973: &status("Waiting for init from $clientip ($hostid{$clientip})");
974: my $remotereq=<$client>;
975: $remotereq=~s/[^\w:]//g;
976: if ($remotereq =~ /^init/) {
977: &sethost("sethost:$perlvar{'lonHostID'}");
978: my $challenge="$$".time;
979: print $client "$challenge\n";
980: &status(
981: "Waiting for challenge reply from $clientip ($hostid{$clientip})");
982: $remotereq=<$client>;
983: $remotereq=~s/\W//g;
984: if ($challenge eq $remotereq) {
985: $clientok=1;
986: print $client "ok\n";
987: } else {
988: &logthis(
989: "<font color=blue>WARNING: $clientip did not reply challenge</font>");
990: &status('No challenge reply '.$clientip);
991: }
992: } else {
993: &logthis(
994: "<font color=blue>WARNING: "
995: ."$clientip failed to initialize: >$remotereq< </font>");
996: &status('No init '.$clientip);
997: }
998: } else {
999: &logthis(
1000: "<font color=blue>WARNING: Unknown client $clientip</font>");
1001: &status('Hung up on '.$clientip);
1002: }
1003: if ($clientok) {
1004: # ---------------- New known client connecting, could mean machine online again
1005:
1006: foreach my $id (keys(%hostip)) {
1007: if ($hostip{$id} ne $clientip ||
1008: $hostip{$currenthostid} eq $clientip) {
1009: # no need to try to do recon's to myself
1010: next;
1011: }
1012: &reconlonc("$perlvar{'lonSockDir'}/$id");
1013: }
1014: &logthis("<font color=green>Established connection: $hostid{$clientip}</font>");
1015: &status('Will listen to '.$hostid{$clientip});
1016: # ------------------------------------------------------------ Process requests
1017: while (my $userinput=<$client>) {
1018: chomp($userinput);
1019: Debug("Request = $userinput\n");
1020: &status('Processing '.$hostid{$clientip}.': '.$userinput);
1021: my $wasenc=0;
1022: alarm(120);
1023: # ------------------------------------------------------------ See if encrypted
1024: if ($userinput =~ /^enc/) {
1025: if ($cipher) {
1026: my ($cmd,$cmdlength,$encinput)=split(/:/,$userinput);
1027: $userinput='';
1028: for (my $encidx=0;$encidx<length($encinput);$encidx+=16) {
1029: $userinput.=
1030: $cipher->decrypt(
1031: pack("H16",substr($encinput,$encidx,16))
1032: );
1033: }
1034: $userinput=substr($userinput,0,$cmdlength);
1035: $wasenc=1;
1036: }
1037: }
1038:
1039: # ------------------------------------------------------------- Normal commands
1040: # ------------------------------------------------------------------------ ping
1041: if ($userinput =~ /^ping/) {
1042: print $client "$currenthostid\n";
1043: # ------------------------------------------------------------------------ pong
1044: }elsif ($userinput =~ /^pong/) {
1045: my $reply=&reply("ping",$hostid{$clientip});
1046: print $client "$currenthostid:$reply\n";
1047: # ------------------------------------------------------------------------ ekey
1048: } elsif ($userinput =~ /^ekey/) {
1049: my $buildkey=time.$$.int(rand 100000);
1050: $buildkey=~tr/1-6/A-F/;
1051: $buildkey=int(rand 100000).$buildkey.int(rand 100000);
1052: my $key=$currenthostid.$hostid{$clientip};
1053: $key=~tr/a-z/A-Z/;
1054: $key=~tr/G-P/0-9/;
1055: $key=~tr/Q-Z/0-9/;
1056: $key=$key.$buildkey.$key.$buildkey.$key.$buildkey;
1057: $key=substr($key,0,32);
1058: my $cipherkey=pack("H32",$key);
1059: $cipher=new IDEA $cipherkey;
1060: print $client "$buildkey\n";
1061: # ------------------------------------------------------------------------ load
1062: } elsif ($userinput =~ /^load/) {
1063: my $loadavg;
1064: {
1065: my $loadfile=IO::File->new('/proc/loadavg');
1066: $loadavg=<$loadfile>;
1067: }
1068: $loadavg =~ s/\s.*//g;
1069: my $loadpercent=100*$loadavg/$perlvar{'lonLoadLim'};
1070: print $client "$loadpercent\n";
1071: # -------------------------------------------------------------------- userload
1072: } elsif ($userinput =~ /^userload/) {
1073: my $userloadpercent=&userload();
1074: print $client "$userloadpercent\n";
1075:
1076: #
1077: # Transactions requiring encryption:
1078: #
1079: # ----------------------------------------------------------------- currentauth
1080: } elsif ($userinput =~ /^currentauth/) {
1081: if ($wasenc==1) {
1082: my ($cmd,$udom,$uname)=split(/:/,$userinput);
1083: my $result = GetAuthType($udom, $uname);
1084: if($result eq "nouser") {
1085: print $client "unknown_user\n";
1086: }
1087: else {
1088: print $client "$result\n"
1089: }
1090: } else {
1091: print $client "refused\n";
1092: }
1093: #--------------------------------------------------------------------- pushfile
1094: } elsif($userinput =~ /^pushfile/) {
1095: if($wasenc == 1) {
1096: my $cert = GetCertificate($userinput);
1097: if(ValidManager($cert)) {
1098: my $reply = PushFile($userinput);
1099: print $client "$reply\n";
1100: } else {
1101: print $client "refused\n";
1102: }
1103: } else {
1104: print $client "refused\n";
1105: }
1106: #--------------------------------------------------------------------- reinit
1107: } elsif($userinput =~ /^reinit/) {
1108: if ($wasenc == 1) {
1109: my $cert = GetCertificate($userinput);
1110: if(ValidManager($cert)) {
1111: chomp($userinput);
1112: my $reply = ReinitProcess($userinput);
1113: print $client "$reply\n";
1114: } else {
1115: print $client "refused\n";
1116: }
1117: } else {
1118: print $client "refused\n";
1119: }
1120: # ------------------------------------------------------------------------ auth
1121: } elsif ($userinput =~ /^auth/) {
1122: if ($wasenc==1) {
1123: my ($cmd,$udom,$uname,$upass)=split(/:/,$userinput);
1124: chomp($upass);
1125: $upass=unescape($upass);
1126: my $proname=propath($udom,$uname);
1127: my $passfilename="$proname/passwd";
1128: if (-e $passfilename) {
1129: my $pf = IO::File->new($passfilename);
1130: my $realpasswd=<$pf>;
1131: chomp($realpasswd);
1132: my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
1133: my $pwdcorrect=0;
1134: if ($howpwd eq 'internal') {
1135: &Debug("Internal auth");
1136: $pwdcorrect=
1137: (crypt($upass,$contentpwd) eq $contentpwd);
1138: } elsif ($howpwd eq 'unix') {
1139: &Debug("Unix auth");
1140: if((getpwnam($uname))[1] eq "") { #no such user!
1141: $pwdcorrect = 0;
1142: } else {
1143: $contentpwd=(getpwnam($uname))[1];
1144: my $pwauth_path="/usr/local/sbin/pwauth";
1145: unless ($contentpwd eq 'x') {
1146: $pwdcorrect=
1147: (crypt($upass,$contentpwd) eq
1148: $contentpwd);
1149: }
1150:
1151: elsif (-e $pwauth_path) {
1152: open PWAUTH, "|$pwauth_path" or
1153: die "Cannot invoke authentication";
1154: print PWAUTH "$uname\n$upass\n";
1155: close PWAUTH;
1156: $pwdcorrect=!$?;
1157: }
1158: }
1159: } elsif ($howpwd eq 'krb4') {
1160: my $null=pack("C",0);
1161: unless ($upass=~/$null/) {
1162: my $krb4_error = &Authen::Krb4::get_pw_in_tkt
1163: ($uname,"",$contentpwd,'krbtgt',
1164: $contentpwd,1,$upass);
1165: if (!$krb4_error) {
1166: $pwdcorrect = 1;
1167: } else {
1168: $pwdcorrect=0;
1169: # log error if it is not a bad password
1170: if ($krb4_error != 62) {
1171: &logthis('krb4:'.$uname.','.$contentpwd.','.
1172: &Authen::Krb4::get_err_txt($Authen::Krb4::error));
1173: }
1174: }
1175: }
1176: } elsif ($howpwd eq 'krb5') {
1177: my $null=pack("C",0);
1178: unless ($upass=~/$null/) {
1179: my $krbclient=&Authen::Krb5::parse_name($uname.'@'.$contentpwd);
1180: my $krbservice="krbtgt/".$contentpwd."\@".$contentpwd;
1181: my $krbserver=&Authen::Krb5::parse_name($krbservice);
1182: my $credentials=&Authen::Krb5::cc_default();
1183: $credentials->initialize($krbclient);
1184: my $krbreturn =
1185: &Authen::Krb5::get_in_tkt_with_password(
1186: $krbclient,$krbserver,$upass,$credentials);
1187: # unless ($krbreturn) {
1188: # &logthis("Krb5 Error: ".
1189: # &Authen::Krb5::error());
1190: # }
1191: $pwdcorrect = ($krbreturn == 1);
1192: } else { $pwdcorrect=0; }
1193: } elsif ($howpwd eq 'localauth') {
1194: $pwdcorrect=&localauth::localauth($uname,$upass,
1195: $contentpwd);
1196: }
1197: if ($pwdcorrect) {
1198: print $client "authorized\n";
1199: } else {
1200: print $client "non_authorized\n";
1201: }
1202: } else {
1203: print $client "unknown_user\n";
1204: }
1205: } else {
1206: print $client "refused\n";
1207: }
1208: # ---------------------------------------------------------------------- passwd
1209: } elsif ($userinput =~ /^passwd/) {
1210: if ($wasenc==1) {
1211: my
1212: ($cmd,$udom,$uname,$upass,$npass)=split(/:/,$userinput);
1213: chomp($npass);
1214: $upass=&unescape($upass);
1215: $npass=&unescape($npass);
1216: &Debug("Trying to change password for $uname");
1217: my $proname=propath($udom,$uname);
1218: my $passfilename="$proname/passwd";
1219: if (-e $passfilename) {
1220: my $realpasswd;
1221: { my $pf = IO::File->new($passfilename);
1222: $realpasswd=<$pf>; }
1223: chomp($realpasswd);
1224: my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
1225: if ($howpwd eq 'internal') {
1226: &Debug("internal auth");
1227: if (crypt($upass,$contentpwd) eq $contentpwd) {
1228: my $salt=time;
1229: $salt=substr($salt,6,2);
1230: my $ncpass=crypt($npass,$salt);
1231: {
1232: my $pf;
1233: if ($pf = IO::File->new(">$passfilename")) {
1234: print $pf "internal:$ncpass\n";
1235: &logthis("Result of password change for $uname: pwchange_success");
1236: print $client "ok\n";
1237: } else {
1238: &logthis("Unable to open $uname passwd to change password");
1239: print $client "non_authorized\n";
1240: }
1241: }
1242:
1243: } else {
1244: print $client "non_authorized\n";
1245: }
1246: } elsif ($howpwd eq 'unix') {
1247: # Unix means we have to access /etc/password
1248: # one way or another.
1249: # First: Make sure the current password is
1250: # correct
1251: &Debug("auth is unix");
1252: $contentpwd=(getpwnam($uname))[1];
1253: my $pwdcorrect = "0";
1254: my $pwauth_path="/usr/local/sbin/pwauth";
1255: unless ($contentpwd eq 'x') {
1256: $pwdcorrect=
1257: (crypt($upass,$contentpwd) eq $contentpwd);
1258: } elsif (-e $pwauth_path) {
1259: open PWAUTH, "|$pwauth_path" or
1260: die "Cannot invoke authentication";
1261: print PWAUTH "$uname\n$upass\n";
1262: close PWAUTH;
1263: &Debug("exited pwauth with $? ($uname,$upass) ");
1264: $pwdcorrect=($? == 0);
1265: }
1266: if ($pwdcorrect) {
1267: my $execdir=$perlvar{'lonDaemons'};
1268: &Debug("Opening lcpasswd pipeline");
1269: my $pf = IO::File->new("|$execdir/lcpasswd > $perlvar{'lonDaemons'}/logs/lcpasswd.log");
1270: print $pf "$uname\n$npass\n$npass\n";
1271: close $pf;
1272: my $err = $?;
1273: my $result = ($err>0 ? 'pwchange_failure'
1274: : 'ok');
1275: &logthis("Result of password change for $uname: ".
1276: &lcpasswdstrerror($?));
1277: print $client "$result\n";
1278: } else {
1279: print $client "non_authorized\n";
1280: }
1281: } else {
1282: print $client "auth_mode_error\n";
1283: }
1284: } else {
1285: print $client "unknown_user\n";
1286: }
1287: } else {
1288: print $client "refused\n";
1289: }
1290: # -------------------------------------------------------------------- makeuser
1291: } elsif ($userinput =~ /^makeuser/) {
1292: &Debug("Make user received");
1293: my $oldumask=umask(0077);
1294: if ($wasenc==1) {
1295: my
1296: ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
1297: &Debug("cmd =".$cmd." $udom =".$udom.
1298: " uname=".$uname);
1299: chomp($npass);
1300: $npass=&unescape($npass);
1301: my $proname=propath($udom,$uname);
1302: my $passfilename="$proname/passwd";
1303: &Debug("Password file created will be:".
1304: $passfilename);
1305: if (-e $passfilename) {
1306: print $client "already_exists\n";
1307: } elsif ($udom ne $currentdomainid) {
1308: print $client "not_right_domain\n";
1309: } else {
1310: my @fpparts=split(/\//,$proname);
1311: my $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2];
1312: my $fperror='';
1313: for (my $i=3;$i<=$#fpparts;$i++) {
1314: $fpnow.='/'.$fpparts[$i];
1315: unless (-e $fpnow) {
1316: unless (mkdir($fpnow,0777)) {
1317: $fperror="error: ".($!+0)
1318: ." mkdir failed while attempting "
1319: ."makeuser\n";
1320: }
1321: }
1322: }
1323: unless ($fperror) {
1324: my $result=&make_passwd_file($uname, $umode,$npass,
1325: $passfilename);
1326: print $client $result;
1327: } else {
1328: print $client "$fperror\n";
1329: }
1330: }
1331: } else {
1332: print $client "refused\n";
1333: }
1334: umask($oldumask);
1335: # -------------------------------------------------------------- changeuserauth
1336: } elsif ($userinput =~ /^changeuserauth/) {
1337: &Debug("Changing authorization");
1338: if ($wasenc==1) {
1339: my
1340: ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
1341: chomp($npass);
1342: &Debug("cmd = ".$cmd." domain= ".$udom.
1343: "uname =".$uname." umode= ".$umode);
1344: $npass=&unescape($npass);
1345: my $proname=&propath($udom,$uname);
1346: my $passfilename="$proname/passwd";
1347: if ($udom ne $currentdomainid) {
1348: print $client "not_right_domain\n";
1349: } else {
1350: my $result=&make_passwd_file($uname, $umode,$npass,
1351: $passfilename);
1352: print $client $result;
1353: }
1354: } else {
1355: print $client "refused\n";
1356: }
1357: # ------------------------------------------------------------------------ home
1358: } elsif ($userinput =~ /^home/) {
1359: my ($cmd,$udom,$uname)=split(/:/,$userinput);
1360: chomp($uname);
1361: my $proname=propath($udom,$uname);
1362: if (-e $proname) {
1363: print $client "found\n";
1364: } else {
1365: print $client "not_found\n";
1366: }
1367: # ---------------------------------------------------------------------- update
1368: } elsif ($userinput =~ /^update/) {
1369: my ($cmd,$fname)=split(/:/,$userinput);
1370: my $ownership=ishome($fname);
1371: if ($ownership eq 'not_owner') {
1372: if (-e $fname) {
1373: my ($dev,$ino,$mode,$nlink,
1374: $uid,$gid,$rdev,$size,
1375: $atime,$mtime,$ctime,
1376: $blksize,$blocks)=stat($fname);
1377: my $now=time;
1378: my $since=$now-$atime;
1379: if ($since>$perlvar{'lonExpire'}) {
1380: my $reply=
1381: &reply("unsub:$fname","$hostid{$clientip}");
1382: unlink("$fname");
1383: } else {
1384: my $transname="$fname.in.transfer";
1385: my $remoteurl=
1386: reply("sub:$fname","$hostid{$clientip}");
1387: my $response;
1388: {
1389: my $ua=new LWP::UserAgent;
1390: my $request=new HTTP::Request('GET',"$remoteurl");
1391: $response=$ua->request($request,$transname);
1392: }
1393: if ($response->is_error()) {
1394: unlink($transname);
1395: my $message=$response->status_line;
1396: &logthis(
1397: "LWP GET: $message for $fname ($remoteurl)");
1398: } else {
1399: if ($remoteurl!~/\.meta$/) {
1400: my $ua=new LWP::UserAgent;
1401: my $mrequest=
1402: new HTTP::Request('GET',$remoteurl.'.meta');
1403: my $mresponse=
1404: $ua->request($mrequest,$fname.'.meta');
1405: if ($mresponse->is_error()) {
1406: unlink($fname.'.meta');
1407: }
1408: }
1409: rename($transname,$fname);
1410: }
1411: }
1412: print $client "ok\n";
1413: } else {
1414: print $client "not_found\n";
1415: }
1416: } else {
1417: print $client "rejected\n";
1418: }
1419: # -------------------------------------- fetch a user file from a remote server
1420: } elsif ($userinput =~ /^fetchuserfile/) {
1421: my ($cmd,$fname)=split(/:/,$userinput);
1422: my ($udom,$uname,$ufile)=split(/\//,$fname);
1423: my $udir=propath($udom,$uname).'/userfiles';
1424: unless (-e $udir) { mkdir($udir,0770); }
1425: if (-e $udir) {
1426: $ufile=~s/^[\.\~]+//;
1427: $ufile=~s/\///g;
1428: my $destname=$udir.'/'.$ufile;
1429: my $transname=$udir.'/'.$ufile.'.in.transit';
1430: my $remoteurl='http://'.$clientip.'/userfiles/'.$fname;
1431: my $response;
1432: {
1433: my $ua=new LWP::UserAgent;
1434: my $request=new HTTP::Request('GET',"$remoteurl");
1435: $response=$ua->request($request,$transname);
1436: }
1437: if ($response->is_error()) {
1438: unlink($transname);
1439: my $message=$response->status_line;
1440: &logthis("LWP GET: $message for $fname ($remoteurl)");
1441: print $client "failed\n";
1442: } else {
1443: if (!rename($transname,$destname)) {
1444: &logthis("Unable to move $transname to $destname");
1445: unlink($transname);
1446: print $client "failed\n";
1447: } else {
1448: print $client "ok\n";
1449: }
1450: }
1451: } else {
1452: print $client "not_home\n";
1453: }
1454: # ------------------------------------------ authenticate access to a user file
1455: } elsif ($userinput =~ /^tokenauthuserfile/) {
1456: my ($cmd,$fname,$session)=split(/:/,$userinput);
1457: chomp($session);
1458: my $reply='non_auth';
1459: if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'.
1460: $session.'.id')) {
1461: while (my $line=<ENVIN>) {
1462: if ($line=~/userfile\.$fname\=/) { $reply='ok'; }
1463: }
1464: close(ENVIN);
1465: print $client $reply."\n";
1466: } else {
1467: print $client "invalid_token\n";
1468: }
1469: # ----------------------------------------------------------------- unsubscribe
1470: } elsif ($userinput =~ /^unsub/) {
1471: my ($cmd,$fname)=split(/:/,$userinput);
1472: if (-e $fname) {
1473: print $client &unsub($client,$fname,$clientip);
1474: } else {
1475: print $client "not_found\n";
1476: }
1477: # ------------------------------------------------------------------- subscribe
1478: } elsif ($userinput =~ /^sub/) {
1479: print $client &subscribe($userinput,$clientip);
1480: # ------------------------------------------------------------- current version
1481: } elsif ($userinput =~ /^currentversion/) {
1482: my ($cmd,$fname)=split(/:/,$userinput);
1483: print $client ¤tversion($fname)."\n";
1484: # ------------------------------------------------------------------------- log
1485: } elsif ($userinput =~ /^log/) {
1486: my ($cmd,$udom,$uname,$what)=split(/:/,$userinput);
1487: chomp($what);
1488: my $proname=propath($udom,$uname);
1489: my $now=time;
1490: {
1491: my $hfh;
1492: if ($hfh=IO::File->new(">>$proname/activity.log")) {
1493: print $hfh "$now:$hostid{$clientip}:$what\n";
1494: print $client "ok\n";
1495: } else {
1496: print $client "error: ".($!+0)
1497: ." IO::File->new Failed "
1498: ."while attempting log\n";
1499: }
1500: }
1501: # ------------------------------------------------------------------------- put
1502: } elsif ($userinput =~ /^put/) {
1503: my ($cmd,$udom,$uname,$namespace,$what)
1504: =split(/:/,$userinput);
1505: $namespace=~s/\//\_/g;
1506: $namespace=~s/\W//g;
1507: if ($namespace ne 'roles') {
1508: chomp($what);
1509: my $proname=propath($udom,$uname);
1510: my $now=time;
1511: unless ($namespace=~/^nohist\_/) {
1512: my $hfh;
1513: if (
1514: $hfh=IO::File->new(">>$proname/$namespace.hist")
1515: ) { print $hfh "P:$now:$what\n"; }
1516: }
1517: my @pairs=split(/\&/,$what);
1518: my %hash;
1519: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
1520: foreach my $pair (@pairs) {
1521: my ($key,$value)=split(/=/,$pair);
1522: $hash{$key}=$value;
1523: }
1524: if (untie(%hash)) {
1525: print $client "ok\n";
1526: } else {
1527: print $client "error: ".($!+0)
1528: ." untie(GDBM) failed ".
1529: "while attempting put\n";
1530: }
1531: } else {
1532: print $client "error: ".($!)
1533: ." tie(GDBM) Failed ".
1534: "while attempting put\n";
1535: }
1536: } else {
1537: print $client "refused\n";
1538: }
1539: # -------------------------------------------------------------------- rolesput
1540: } elsif ($userinput =~ /^rolesput/) {
1541: &Debug("rolesput");
1542: if ($wasenc==1) {
1543: my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
1544: =split(/:/,$userinput);
1545: &Debug("cmd = ".$cmd." exedom= ".$exedom.
1546: "user = ".$exeuser." udom=".$udom.
1547: "what = ".$what);
1548: my $namespace='roles';
1549: chomp($what);
1550: my $proname=propath($udom,$uname);
1551: my $now=time;
1552: {
1553: my $hfh;
1554: if (
1555: $hfh=IO::File->new(">>$proname/$namespace.hist")
1556: ) {
1557: print $hfh "P:$now:$exedom:$exeuser:$what\n";
1558: }
1559: }
1560: my @pairs=split(/\&/,$what);
1561: my %hash;
1562: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
1563: foreach my $pair (@pairs) {
1564: my ($key,$value)=split(/=/,$pair);
1565: &ManagePermissions($key, $udom, $uname,
1566: &GetAuthType( $udom,
1567: $uname));
1568: $hash{$key}=$value;
1569: }
1570: if (untie(%hash)) {
1571: print $client "ok\n";
1572: } else {
1573: print $client "error: ".($!+0)
1574: ." untie(GDBM) Failed ".
1575: "while attempting rolesput\n";
1576: }
1577: } else {
1578: print $client "error: ".($!+0)
1579: ." tie(GDBM) Failed ".
1580: "while attempting rolesput\n";
1581: }
1582: } else {
1583: print $client "refused\n";
1584: }
1585: # -------------------------------------------------------------------- rolesdel
1586: } elsif ($userinput =~ /^rolesdel/) {
1587: &Debug("rolesdel");
1588: if ($wasenc==1) {
1589: my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
1590: =split(/:/,$userinput);
1591: &Debug("cmd = ".$cmd." exedom= ".$exedom.
1592: "user = ".$exeuser." udom=".$udom.
1593: "what = ".$what);
1594: my $namespace='roles';
1595: chomp($what);
1596: my $proname=propath($udom,$uname);
1597: my $now=time;
1598: {
1599: my $hfh;
1600: if (
1601: $hfh=IO::File->new(">>$proname/$namespace.hist")
1602: ) {
1603: print $hfh "D:$now:$exedom:$exeuser:$what\n";
1604: }
1605: }
1606: my @rolekeys=split(/\&/,$what);
1607: my %hash;
1608: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
1609: foreach my $key (@rolekeys) {
1610: delete $hash{$key};
1611: }
1612: if (untie(%hash)) {
1613: print $client "ok\n";
1614: } else {
1615: print $client "error: ".($!+0)
1616: ." untie(GDBM) Failed ".
1617: "while attempting rolesdel\n";
1618: }
1619: } else {
1620: print $client "error: ".($!+0)
1621: ." tie(GDBM) Failed ".
1622: "while attempting rolesdel\n";
1623: }
1624: } else {
1625: print $client "refused\n";
1626: }
1627: # ------------------------------------------------------------------------- get
1628: } elsif ($userinput =~ /^get/) {
1629: my ($cmd,$udom,$uname,$namespace,$what)
1630: =split(/:/,$userinput);
1631: $namespace=~s/\//\_/g;
1632: $namespace=~s/\W//g;
1633: chomp($what);
1634: my @queries=split(/\&/,$what);
1635: my $proname=propath($udom,$uname);
1636: my $qresult='';
1637: my %hash;
1638: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
1639: for (my $i=0;$i<=$#queries;$i++) {
1640: $qresult.="$hash{$queries[$i]}&";
1641: }
1642: if (untie(%hash)) {
1643: $qresult=~s/\&$//;
1644: print $client "$qresult\n";
1645: } else {
1646: print $client "error: ".($!+0)
1647: ." untie(GDBM) Failed ".
1648: "while attempting get\n";
1649: }
1650: } else {
1651: if ($!+0 == 2) {
1652: print $client "error:No such file or ".
1653: "GDBM reported bad block error\n";
1654: } else {
1655: print $client "error: ".($!+0)
1656: ." tie(GDBM) Failed ".
1657: "while attempting get\n";
1658: }
1659: }
1660: # ------------------------------------------------------------------------ eget
1661: } elsif ($userinput =~ /^eget/) {
1662: my ($cmd,$udom,$uname,$namespace,$what)
1663: =split(/:/,$userinput);
1664: $namespace=~s/\//\_/g;
1665: $namespace=~s/\W//g;
1666: chomp($what);
1667: my @queries=split(/\&/,$what);
1668: my $proname=propath($udom,$uname);
1669: my $qresult='';
1670: my %hash;
1671: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
1672: for (my $i=0;$i<=$#queries;$i++) {
1673: $qresult.="$hash{$queries[$i]}&";
1674: }
1675: if (untie(%hash)) {
1676: $qresult=~s/\&$//;
1677: if ($cipher) {
1678: my $cmdlength=length($qresult);
1679: $qresult.=" ";
1680: my $encqresult='';
1681: for
1682: (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
1683: $encqresult.=
1684: unpack("H16",
1685: $cipher->encrypt(substr($qresult,$encidx,8)));
1686: }
1687: print $client "enc:$cmdlength:$encqresult\n";
1688: } else {
1689: print $client "error:no_key\n";
1690: }
1691: } else {
1692: print $client "error: ".($!+0)
1693: ." untie(GDBM) Failed ".
1694: "while attempting eget\n";
1695: }
1696: } else {
1697: print $client "error: ".($!+0)
1698: ." tie(GDBM) Failed ".
1699: "while attempting eget\n";
1700: }
1701: # ------------------------------------------------------------------------- del
1702: } elsif ($userinput =~ /^del/) {
1703: my ($cmd,$udom,$uname,$namespace,$what)
1704: =split(/:/,$userinput);
1705: $namespace=~s/\//\_/g;
1706: $namespace=~s/\W//g;
1707: chomp($what);
1708: my $proname=propath($udom,$uname);
1709: my $now=time;
1710: unless ($namespace=~/^nohist\_/) {
1711: my $hfh;
1712: if (
1713: $hfh=IO::File->new(">>$proname/$namespace.hist")
1714: ) { print $hfh "D:$now:$what\n"; }
1715: }
1716: my @keys=split(/\&/,$what);
1717: my %hash;
1718: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
1719: foreach my $key (@keys) {
1720: delete($hash{$key});
1721: }
1722: if (untie(%hash)) {
1723: print $client "ok\n";
1724: } else {
1725: print $client "error: ".($!+0)
1726: ." untie(GDBM) Failed ".
1727: "while attempting del\n";
1728: }
1729: } else {
1730: print $client "error: ".($!+0)
1731: ." tie(GDBM) Failed ".
1732: "while attempting del\n";
1733: }
1734: # ------------------------------------------------------------------------ keys
1735: } elsif ($userinput =~ /^keys/) {
1736: my ($cmd,$udom,$uname,$namespace)
1737: =split(/:/,$userinput);
1738: $namespace=~s/\//\_/g;
1739: $namespace=~s/\W//g;
1740: my $proname=propath($udom,$uname);
1741: my $qresult='';
1742: my %hash;
1743: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
1744: foreach my $key (keys %hash) {
1745: $qresult.="$key&";
1746: }
1747: if (untie(%hash)) {
1748: $qresult=~s/\&$//;
1749: print $client "$qresult\n";
1750: } else {
1751: print $client "error: ".($!+0)
1752: ." untie(GDBM) Failed ".
1753: "while attempting keys\n";
1754: }
1755: } else {
1756: print $client "error: ".($!+0)
1757: ." tie(GDBM) Failed ".
1758: "while attempting keys\n";
1759: }
1760: # ----------------------------------------------------------------- dumpcurrent
1761: } elsif ($userinput =~ /^currentdump/) {
1762: my ($cmd,$udom,$uname,$namespace)
1763: =split(/:/,$userinput);
1764: $namespace=~s/\//\_/g;
1765: $namespace=~s/\W//g;
1766: my $qresult='';
1767: my $proname=propath($udom,$uname);
1768: my %hash;
1769: if (tie(%hash,'GDBM_File',
1770: "$proname/$namespace.db",
1771: &GDBM_READER(),0640)) {
1772: # Structure of %data:
1773: # $data{$symb}->{$parameter}=$value;
1774: # $data{$symb}->{'v.'.$parameter}=$version;
1775: # since $parameter will be unescaped, we do not
1776: # have to worry about silly parameter names...
1777: my %data = ();
1778: while (my ($key,$value) = each(%hash)) {
1779: my ($v,$symb,$param) = split(/:/,$key);
1780: next if ($v eq 'version' || $symb eq 'keys');
1781: next if (exists($data{$symb}) &&
1782: exists($data{$symb}->{$param}) &&
1783: $data{$symb}->{'v.'.$param} > $v);
1784: $data{$symb}->{$param}=$value;
1785: $data{$symb}->{'v.'.$param}=$v;
1786: }
1787: if (untie(%hash)) {
1788: while (my ($symb,$param_hash) = each(%data)) {
1789: while(my ($param,$value) = each (%$param_hash)){
1790: next if ($param =~ /^v\./);
1791: $qresult.=$symb.':'.$param.'='.$value.'&';
1792: }
1793: }
1794: chop($qresult);
1795: print $client "$qresult\n";
1796: } else {
1797: print $client "error: ".($!+0)
1798: ." untie(GDBM) Failed ".
1799: "while attempting currentdump\n";
1800: }
1801: } else {
1802: print $client "error: ".($!+0)
1803: ." tie(GDBM) Failed ".
1804: "while attempting currentdump\n";
1805: }
1806: # ------------------------------------------------------------------------ dump
1807: } elsif ($userinput =~ /^dump/) {
1808: my ($cmd,$udom,$uname,$namespace,$regexp)
1809: =split(/:/,$userinput);
1810: $namespace=~s/\//\_/g;
1811: $namespace=~s/\W//g;
1812: if (defined($regexp)) {
1813: $regexp=&unescape($regexp);
1814: } else {
1815: $regexp='.';
1816: }
1817: my $qresult='';
1818: my $proname=propath($udom,$uname);
1819: my %hash;
1820: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
1821: study($regexp);
1822: while (my ($key,$value) = each(%hash)) {
1823: if ($regexp eq '.') {
1824: $qresult.=$key.'='.$value.'&';
1825: } else {
1826: my $unescapeKey = &unescape($key);
1827: if (eval('$unescapeKey=~/$regexp/')) {
1828: $qresult.="$key=$value&";
1829: }
1830: }
1831: }
1832: if (untie(%hash)) {
1833: chop($qresult);
1834: print $client "$qresult\n";
1835: } else {
1836: print $client "error: ".($!+0)
1837: ." untie(GDBM) Failed ".
1838: "while attempting dump\n";
1839: }
1840: } else {
1841: print $client "error: ".($!+0)
1842: ." tie(GDBM) Failed ".
1843: "while attempting dump\n";
1844: }
1845: # ----------------------------------------------------------------------- store
1846: } elsif ($userinput =~ /^store/) {
1847: my ($cmd,$udom,$uname,$namespace,$rid,$what)
1848: =split(/:/,$userinput);
1849: $namespace=~s/\//\_/g;
1850: $namespace=~s/\W//g;
1851: if ($namespace ne 'roles') {
1852: chomp($what);
1853: my $proname=propath($udom,$uname);
1854: my $now=time;
1855: unless ($namespace=~/^nohist\_/) {
1856: my $hfh;
1857: if (
1858: $hfh=IO::File->new(">>$proname/$namespace.hist")
1859: ) { print $hfh "P:$now:$rid:$what\n"; }
1860: }
1861: my @pairs=split(/\&/,$what);
1862: my %hash;
1863: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
1864: my @previouskeys=split(/&/,$hash{"keys:$rid"});
1865: my $key;
1866: $hash{"version:$rid"}++;
1867: my $version=$hash{"version:$rid"};
1868: my $allkeys='';
1869: foreach my $pair (@pairs) {
1870: my ($key,$value)=split(/=/,$pair);
1871: $allkeys.=$key.':';
1872: $hash{"$version:$rid:$key"}=$value;
1873: }
1874: $hash{"$version:$rid:timestamp"}=$now;
1875: $allkeys.='timestamp';
1876: $hash{"$version:keys:$rid"}=$allkeys;
1877: if (untie(%hash)) {
1878: print $client "ok\n";
1879: } else {
1880: print $client "error: ".($!+0)
1881: ." untie(GDBM) Failed ".
1882: "while attempting store\n";
1883: }
1884: } else {
1885: print $client "error: ".($!+0)
1886: ." tie(GDBM) Failed ".
1887: "while attempting store\n";
1888: }
1889: } else {
1890: print $client "refused\n";
1891: }
1892: # --------------------------------------------------------------------- restore
1893: } elsif ($userinput =~ /^restore/) {
1894: my ($cmd,$udom,$uname,$namespace,$rid)
1895: =split(/:/,$userinput);
1896: $namespace=~s/\//\_/g;
1897: $namespace=~s/\W//g;
1898: chomp($rid);
1899: my $proname=propath($udom,$uname);
1900: my $qresult='';
1901: my %hash;
1902: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
1903: my $version=$hash{"version:$rid"};
1904: $qresult.="version=$version&";
1905: my $scope;
1906: for ($scope=1;$scope<=$version;$scope++) {
1907: my $vkeys=$hash{"$scope:keys:$rid"};
1908: my @keys=split(/:/,$vkeys);
1909: my $key;
1910: $qresult.="$scope:keys=$vkeys&";
1911: foreach $key (@keys) {
1912: $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&";
1913: }
1914: }
1915: if (untie(%hash)) {
1916: $qresult=~s/\&$//;
1917: print $client "$qresult\n";
1918: } else {
1919: print $client "error: ".($!+0)
1920: ." untie(GDBM) Failed ".
1921: "while attempting restore\n";
1922: }
1923: } else {
1924: print $client "error: ".($!+0)
1925: ." tie(GDBM) Failed ".
1926: "while attempting restore\n";
1927: }
1928: # -------------------------------------------------------------------- chatsend
1929: } elsif ($userinput =~ /^chatsend/) {
1930: my ($cmd,$cdom,$cnum,$newpost)=split(/\:/,$userinput);
1931: &chatadd($cdom,$cnum,$newpost);
1932: print $client "ok\n";
1933: # -------------------------------------------------------------------- chatretr
1934: } elsif ($userinput =~ /^chatretr/) {
1935: my
1936: ($cmd,$cdom,$cnum,$udom,$uname)=split(/\:/,$userinput);
1937: my $reply='';
1938: foreach (&getchat($cdom,$cnum,$udom,$uname)) {
1939: $reply.=&escape($_).':';
1940: }
1941: $reply=~s/\:$//;
1942: print $client $reply."\n";
1943: # ------------------------------------------------------------------- querysend
1944: } elsif ($userinput =~ /^querysend/) {
1945: my ($cmd,$query,
1946: $arg1,$arg2,$arg3)=split(/\:/,$userinput);
1947: $query=~s/\n*$//g;
1948: print $client "".
1949: sqlreply("$hostid{$clientip}\&$query".
1950: "\&$arg1"."\&$arg2"."\&$arg3")."\n";
1951: # ------------------------------------------------------------------ queryreply
1952: } elsif ($userinput =~ /^queryreply/) {
1953: my ($cmd,$id,$reply)=split(/:/,$userinput);
1954: my $store;
1955: my $execdir=$perlvar{'lonDaemons'};
1956: if ($store=IO::File->new(">$execdir/tmp/$id")) {
1957: $reply=~s/\&/\n/g;
1958: print $store $reply;
1959: close $store;
1960: my $store2=IO::File->new(">$execdir/tmp/$id.end");
1961: print $store2 "done\n";
1962: close $store2;
1963: print $client "ok\n";
1964: }
1965: else {
1966: print $client "error: ".($!+0)
1967: ." IO::File->new Failed ".
1968: "while attempting queryreply\n";
1969: }
1970: # ----------------------------------------------------------------- courseidput
1971: } elsif ($userinput =~ /^courseidput/) {
1972: my ($cmd,$udom,$what)=split(/:/,$userinput);
1973: chomp($what);
1974: $udom=~s/\W//g;
1975: my $proname=
1976: "$perlvar{'lonUsersDir'}/$udom/nohist_courseids";
1977: my $now=time;
1978: my @pairs=split(/\&/,$what);
1979: my %hash;
1980: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) {
1981: foreach my $pair (@pairs) {
1982: my ($key,$value)=split(/=/,$pair);
1983: $hash{$key}=$value.':'.$now;
1984: }
1985: if (untie(%hash)) {
1986: print $client "ok\n";
1987: } else {
1988: print $client "error: ".($!+0)
1989: ." untie(GDBM) Failed ".
1990: "while attempting courseidput\n";
1991: }
1992: } else {
1993: print $client "error: ".($!+0)
1994: ." tie(GDBM) Failed ".
1995: "while attempting courseidput\n";
1996: }
1997: # ---------------------------------------------------------------- courseiddump
1998: } elsif ($userinput =~ /^courseiddump/) {
1999: my ($cmd,$udom,$since,$description)
2000: =split(/:/,$userinput);
2001: if (defined($description)) {
2002: $description=&unescape($description);
2003: } else {
2004: $description='.';
2005: }
2006: unless (defined($since)) { $since=0; }
2007: my $qresult='';
2008: my $proname=
2009: "$perlvar{'lonUsersDir'}/$udom/nohist_courseids";
2010: my %hash;
2011: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) {
2012: while (my ($key,$value) = each(%hash)) {
2013: my ($descr,$lasttime)=split(/\:/,$value);
2014: if ($lasttime<$since) { next; }
2015: if ($description eq '.') {
2016: $qresult.=$key.'='.$descr.'&';
2017: } else {
2018: my $unescapeVal = &unescape($descr);
2019: if (eval('$unescapeVal=~/$description/i')) {
2020: $qresult.="$key=$descr&";
2021: }
2022: }
2023: }
2024: if (untie(%hash)) {
2025: chop($qresult);
2026: print $client "$qresult\n";
2027: } else {
2028: print $client "error: ".($!+0)
2029: ." untie(GDBM) Failed ".
2030: "while attempting courseiddump\n";
2031: }
2032: } else {
2033: print $client "error: ".($!+0)
2034: ." tie(GDBM) Failed ".
2035: "while attempting courseiddump\n";
2036: }
2037: # ----------------------------------------------------------------------- idput
2038: } elsif ($userinput =~ /^idput/) {
2039: my ($cmd,$udom,$what)=split(/:/,$userinput);
2040: chomp($what);
2041: $udom=~s/\W//g;
2042: my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
2043: my $now=time;
2044: {
2045: my $hfh;
2046: if (
2047: $hfh=IO::File->new(">>$proname.hist")
2048: ) { print $hfh "P:$now:$what\n"; }
2049: }
2050: my @pairs=split(/\&/,$what);
2051: my %hash;
2052: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) {
2053: foreach my $pair (@pairs) {
2054: my ($key,$value)=split(/=/,$pair);
2055: $hash{$key}=$value;
2056: }
2057: if (untie(%hash)) {
2058: print $client "ok\n";
2059: } else {
2060: print $client "error: ".($!+0)
2061: ." untie(GDBM) Failed ".
2062: "while attempting idput\n";
2063: }
2064: } else {
2065: print $client "error: ".($!+0)
2066: ." tie(GDBM) Failed ".
2067: "while attempting idput\n";
2068: }
2069: # ----------------------------------------------------------------------- idget
2070: } elsif ($userinput =~ /^idget/) {
2071: my ($cmd,$udom,$what)=split(/:/,$userinput);
2072: chomp($what);
2073: $udom=~s/\W//g;
2074: my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
2075: my @queries=split(/\&/,$what);
2076: my $qresult='';
2077: my %hash;
2078: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) {
2079: for (my $i=0;$i<=$#queries;$i++) {
2080: $qresult.="$hash{$queries[$i]}&";
2081: }
2082: if (untie(%hash)) {
2083: $qresult=~s/\&$//;
2084: print $client "$qresult\n";
2085: } else {
2086: print $client "error: ".($!+0)
2087: ." untie(GDBM) Failed ".
2088: "while attempting idget\n";
2089: }
2090: } else {
2091: print $client "error: ".($!+0)
2092: ." tie(GDBM) Failed ".
2093: "while attempting idget\n";
2094: }
2095: # ---------------------------------------------------------------------- tmpput
2096: } elsif ($userinput =~ /^tmpput/) {
2097: my ($cmd,$what)=split(/:/,$userinput);
2098: my $store;
2099: $tmpsnum++;
2100: my $id=$$.'_'.$clientip.'_'.$tmpsnum;
2101: $id=~s/\W/\_/g;
2102: $what=~s/\n//g;
2103: my $execdir=$perlvar{'lonDaemons'};
2104: if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) {
2105: print $store $what;
2106: close $store;
2107: print $client "$id\n";
2108: }
2109: else {
2110: print $client "error: ".($!+0)
2111: ."IO::File->new Failed ".
2112: "while attempting tmpput\n";
2113: }
2114:
2115: # ---------------------------------------------------------------------- tmpget
2116: } elsif ($userinput =~ /^tmpget/) {
2117: my ($cmd,$id)=split(/:/,$userinput);
2118: chomp($id);
2119: $id=~s/\W/\_/g;
2120: my $store;
2121: my $execdir=$perlvar{'lonDaemons'};
2122: if ($store=IO::File->new("$execdir/tmp/$id.tmp")) {
2123: my $reply=<$store>;
2124: print $client "$reply\n";
2125: close $store;
2126: }
2127: else {
2128: print $client "error: ".($!+0)
2129: ."IO::File->new Failed ".
2130: "while attempting tmpget\n";
2131: }
2132:
2133: # ---------------------------------------------------------------------- tmpdel
2134: } elsif ($userinput =~ /^tmpdel/) {
2135: my ($cmd,$id)=split(/:/,$userinput);
2136: chomp($id);
2137: $id=~s/\W/\_/g;
2138: my $execdir=$perlvar{'lonDaemons'};
2139: if (unlink("$execdir/tmp/$id.tmp")) {
2140: print $client "ok\n";
2141: } else {
2142: print $client "error: ".($!+0)
2143: ."Unlink tmp Failed ".
2144: "while attempting tmpdel\n";
2145: }
2146: # -------------------------------------------------------------------------- ls
2147: } elsif ($userinput =~ /^ls/) {
2148: my ($cmd,$ulsdir)=split(/:/,$userinput);
2149: my $ulsout='';
2150: my $ulsfn;
2151: if (-e $ulsdir) {
2152: if(-d $ulsdir) {
2153: if (opendir(LSDIR,$ulsdir)) {
2154: while ($ulsfn=readdir(LSDIR)) {
2155: my @ulsstats=stat($ulsdir.'/'.$ulsfn);
2156: $ulsout.=$ulsfn.'&'.
2157: join('&',@ulsstats).':';
2158: }
2159: closedir(LSDIR);
2160: }
2161: } else {
2162: my @ulsstats=stat($ulsdir);
2163: $ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
2164: }
2165: } else {
2166: $ulsout='no_such_dir';
2167: }
2168: if ($ulsout eq '') { $ulsout='empty'; }
2169: print $client "$ulsout\n";
2170: # ----------------------------------------------------------------- setannounce
2171: } elsif ($userinput =~ /^setannounce/) {
2172: my ($cmd,$announcement)=split(/:/,$userinput);
2173: chomp($announcement);
2174: $announcement=&unescape($announcement);
2175: if (my $store=IO::File->new('>'.$perlvar{'lonDocRoot'}.
2176: '/announcement.txt')) {
2177: print $store $announcement;
2178: close $store;
2179: print $client "ok\n";
2180: } else {
2181: print $client "error: ".($!+0)."\n";
2182: }
2183: # ------------------------------------------------------------------ Hanging up
2184: } elsif (($userinput =~ /^exit/) ||
2185: ($userinput =~ /^init/)) {
2186: &logthis(
2187: "Client $clientip ($hostid{$clientip}) hanging up: $userinput");
2188: print $client "bye\n";
2189: $client->close();
2190: last;
2191: # ------------------------------------------------------------- unknown command
2192: } elsif ($userinput =~ /^sethost:/) {
2193: print $client &sethost($userinput)."\n";
2194: } elsif ($userinput =~/^version:/) {
2195: print $client &version($userinput)."\n";
2196: } else {
2197: # unknown command
2198: print $client "unknown_cmd\n";
2199: }
2200: # -------------------------------------------------------------------- complete
2201: alarm(0);
2202: &status('Listening to '.$hostid{$clientip});
2203: }
2204: # --------------------------------------------- client unknown or fishy, refuse
2205: } else {
2206: print $client "refused\n";
2207: $client->close();
2208: &logthis("<font color=blue>WARNING: "
2209: ."Rejected client $clientip, closing connection</font>");
2210: }
2211: }
2212:
2213: # =============================================================================
2214:
2215: &logthis("<font color=red>CRITICAL: "
2216: ."Disconnect from $clientip ($hostid{$clientip})</font>");
2217:
2218:
2219: # this exit is VERY important, otherwise the child will become
2220: # a producer of more and more children, forking yourself into
2221: # process death.
2222: exit;
2223:
2224: }
2225:
2226:
2227: #
2228: # Checks to see if the input roleput request was to set
2229: # an author role. If so, invokes the lchtmldir script to set
2230: # up a correct public_html
2231: # Parameters:
2232: # request - The request sent to the rolesput subchunk.
2233: # We're looking for /domain/_au
2234: # domain - The domain in which the user is having roles doctored.
2235: # user - Name of the user for which the role is being put.
2236: # authtype - The authentication type associated with the user.
2237: #
2238: sub ManagePermissions
2239: {
2240: my $request = shift;
2241: my $domain = shift;
2242: my $user = shift;
2243: my $authtype= shift;
2244:
2245: # See if the request is of the form /$domain/_au
2246: &logthis("ruequest is $request");
2247: if($request =~ /^(\/$domain\/_au)$/) { # It's an author rolesput...
2248: my $execdir = $perlvar{'lonDaemons'};
2249: my $userhome= "/home/$user" ;
2250: &logthis("system $execdir/lchtmldir $userhome $user $authtype");
2251: system("$execdir/lchtmldir $userhome $user $authtype");
2252: }
2253: }
2254: #
2255: # GetAuthType - Determines the authorization type of a user in a domain.
2256:
2257: # Returns the authorization type or nouser if there is no such user.
2258: #
2259: sub GetAuthType
2260: {
2261: my $domain = shift;
2262: my $user = shift;
2263:
2264: Debug("GetAuthType( $domain, $user ) \n");
2265: my $proname = &propath($domain, $user);
2266: my $passwdfile = "$proname/passwd";
2267: if( -e $passwdfile ) {
2268: my $pf = IO::File->new($passwdfile);
2269: my $realpassword = <$pf>;
2270: chomp($realpassword);
2271: Debug("Password info = $realpassword\n");
2272: my ($authtype, $contentpwd) = split(/:/, $realpassword);
2273: Debug("Authtype = $authtype, content = $contentpwd\n");
2274: my $availinfo = '';
2275: if($authtype eq 'krb4' or $authtype eq 'krb5') {
2276: $availinfo = $contentpwd;
2277: }
2278:
2279: return "$authtype:$availinfo";
2280: }
2281: else {
2282: Debug("Returning nouser");
2283: return "nouser";
2284: }
2285: }
2286:
2287: sub addline {
2288: my ($fname,$hostid,$ip,$newline)=@_;
2289: my $contents;
2290: my $found=0;
2291: my $expr='^'.$hostid.':'.$ip.':';
2292: $expr =~ s/\./\\\./g;
2293: my $sh;
2294: if ($sh=IO::File->new("$fname.subscription")) {
2295: while (my $subline=<$sh>) {
2296: if ($subline !~ /$expr/) {$contents.= $subline;} else {$found=1;}
2297: }
2298: $sh->close();
2299: }
2300: $sh=IO::File->new(">$fname.subscription");
2301: if ($contents) { print $sh $contents; }
2302: if ($newline) { print $sh $newline; }
2303: $sh->close();
2304: return $found;
2305: }
2306:
2307: sub getchat {
2308: my ($cdom,$cname,$udom,$uname)=@_;
2309: my %hash;
2310: my $proname=&propath($cdom,$cname);
2311: my @entries=();
2312: if (tie(%hash,'GDBM_File',"$proname/nohist_chatroom.db",
2313: &GDBM_READER(),0640)) {
2314: @entries=map { $_.':'.$hash{$_} } sort keys %hash;
2315: untie %hash;
2316: }
2317: my @participants=();
2318: my $cutoff=time-60;
2319: if (tie(%hash,'GDBM_File',"$proname/nohist_inchatroom.db",
2320: &GDBM_WRCREAT(),0640)) {
2321: $hash{$uname.':'.$udom}=time;
2322: foreach (sort keys %hash) {
2323: if ($hash{$_}>$cutoff) {
2324: $participants[$#participants+1]='active_participant:'.$_;
2325: }
2326: }
2327: untie %hash;
2328: }
2329: return (@participants,@entries);
2330: }
2331:
2332: sub chatadd {
2333: my ($cdom,$cname,$newchat)=@_;
2334: my %hash;
2335: my $proname=&propath($cdom,$cname);
2336: my @entries=();
2337: my $time=time;
2338: if (tie(%hash,'GDBM_File',"$proname/nohist_chatroom.db",
2339: &GDBM_WRCREAT(),0640)) {
2340: @entries=map { $_.':'.$hash{$_} } sort keys %hash;
2341: my ($lastid)=($entries[$#entries]=~/^(\w+)\:/);
2342: my ($thentime,$idnum)=split(/\_/,$lastid);
2343: my $newid=$time.'_000000';
2344: if ($thentime==$time) {
2345: $idnum=~s/^0+//;
2346: $idnum++;
2347: $idnum=substr('000000'.$idnum,-6,6);
2348: $newid=$time.'_'.$idnum;
2349: }
2350: $hash{$newid}=$newchat;
2351: my $expired=$time-3600;
2352: foreach (keys %hash) {
2353: my ($thistime)=($_=~/(\d+)\_/);
2354: if ($thistime<$expired) {
2355: delete $hash{$_};
2356: }
2357: }
2358: untie %hash;
2359: }
2360: {
2361: my $hfh;
2362: if ($hfh=IO::File->new(">>$proname/chatroom.log")) {
2363: print $hfh "$time:".&unescape($newchat)."\n";
2364: }
2365: }
2366: }
2367:
2368: sub unsub {
2369: my ($fname,$clientip)=@_;
2370: my $result;
2371: if (unlink("$fname.$hostid{$clientip}")) {
2372: $result="ok\n";
2373: } else {
2374: $result="not_subscribed\n";
2375: }
2376: if (-e "$fname.subscription") {
2377: my $found=&addline($fname,$hostid{$clientip},$clientip,'');
2378: if ($found) { $result="ok\n"; }
2379: } else {
2380: if ($result != "ok\n") { $result="not_subscribed\n"; }
2381: }
2382: return $result;
2383: }
2384:
2385: sub currentversion {
2386: my $fname=shift;
2387: my $version=-1;
2388: my $ulsdir='';
2389: if ($fname=~/^(.+)\/[^\/]+$/) {
2390: $ulsdir=$1;
2391: }
2392: my ($fnamere1,$fnamere2);
2393: # remove version if already specified
2394: $fname=~s/\.\d+\.(\w+(?:\.meta)*)$/\.$1/;
2395: # get the bits that go before and after the version number
2396: if ( $fname=~/^(.*\.)(\w+(?:\.meta)*)$/ ) {
2397: $fnamere1=$1;
2398: $fnamere2='.'.$2;
2399: }
2400: if (-e $fname) { $version=1; }
2401: if (-e $ulsdir) {
2402: if(-d $ulsdir) {
2403: if (opendir(LSDIR,$ulsdir)) {
2404: my $ulsfn;
2405: while ($ulsfn=readdir(LSDIR)) {
2406: # see if this is a regular file (ignore links produced earlier)
2407: my $thisfile=$ulsdir.'/'.$ulsfn;
2408: unless (-l $thisfile) {
2409: if ($thisfile=~/\Q$fnamere1\E(\d+)\Q$fnamere2\E/) {
2410: if ($1>$version) { $version=$1; }
2411: }
2412: }
2413: }
2414: closedir(LSDIR);
2415: $version++;
2416: }
2417: }
2418: }
2419: return $version;
2420: }
2421:
2422: sub thisversion {
2423: my $fname=shift;
2424: my $version=-1;
2425: if ($fname=~/\.(\d+)\.\w+(?:\.meta)*$/) {
2426: $version=$1;
2427: }
2428: return $version;
2429: }
2430:
2431: sub subscribe {
2432: my ($userinput,$clientip)=@_;
2433: my $result;
2434: my ($cmd,$fname)=split(/:/,$userinput);
2435: my $ownership=&ishome($fname);
2436: if ($ownership eq 'owner') {
2437: # explitly asking for the current version?
2438: unless (-e $fname) {
2439: my $currentversion=¤tversion($fname);
2440: if (&thisversion($fname)==$currentversion) {
2441: if ($fname=~/^(.+)\.\d+\.(\w+(?:\.meta)*)$/) {
2442: my $root=$1;
2443: my $extension=$2;
2444: symlink($root.'.'.$extension,
2445: $root.'.'.$currentversion.'.'.$extension);
2446: unless ($extension=~/\.meta$/) {
2447: symlink($root.'.'.$extension.'.meta',
2448: $root.'.'.$currentversion.'.'.$extension.'.meta');
2449: }
2450: }
2451: }
2452: }
2453: if (-e $fname) {
2454: if (-d $fname) {
2455: $result="directory\n";
2456: } else {
2457: if (-e "$fname.$hostid{$clientip}") {&unsub($fname,$clientip);}
2458: my $now=time;
2459: my $found=&addline($fname,$hostid{$clientip},$clientip,
2460: "$hostid{$clientip}:$clientip:$now\n");
2461: if ($found) { $result="$fname\n"; }
2462: # if they were subscribed to only meta data, delete that
2463: # subscription, when you subscribe to a file you also get
2464: # the metadata
2465: unless ($fname=~/\.meta$/) { &unsub("$fname.meta",$clientip); }
2466: $fname=~s/\/home\/httpd\/html\/res/raw/;
2467: $fname="http://$thisserver/".$fname;
2468: $result="$fname\n";
2469: }
2470: } else {
2471: $result="not_found\n";
2472: }
2473: } else {
2474: $result="rejected\n";
2475: }
2476: return $result;
2477: }
2478:
2479: sub make_passwd_file {
2480: my ($uname, $umode,$npass,$passfilename)=@_;
2481: my $result="ok\n";
2482: if ($umode eq 'krb4' or $umode eq 'krb5') {
2483: {
2484: my $pf = IO::File->new(">$passfilename");
2485: print $pf "$umode:$npass\n";
2486: }
2487: } elsif ($umode eq 'internal') {
2488: my $salt=time;
2489: $salt=substr($salt,6,2);
2490: my $ncpass=crypt($npass,$salt);
2491: {
2492: &Debug("Creating internal auth");
2493: my $pf = IO::File->new(">$passfilename");
2494: print $pf "internal:$ncpass\n";
2495: }
2496: } elsif ($umode eq 'localauth') {
2497: {
2498: my $pf = IO::File->new(">$passfilename");
2499: print $pf "localauth:$npass\n";
2500: }
2501: } elsif ($umode eq 'unix') {
2502: {
2503: my $execpath="$perlvar{'lonDaemons'}/"."lcuseradd";
2504: {
2505: &Debug("Executing external: ".$execpath);
2506: &Debug("user = ".$uname.", Password =". $npass);
2507: my $se = IO::File->new("|$execpath > $perlvar{'lonDaemons'}/logs/lcuseradd.log");
2508: print $se "$uname\n";
2509: print $se "$npass\n";
2510: print $se "$npass\n";
2511: }
2512: my $useraddok = $?;
2513: if($useraddok > 0) {
2514: &logthis("Failed lcuseradd: ".&lcuseraddstrerror($useraddok));
2515: }
2516: my $pf = IO::File->new(">$passfilename");
2517: print $pf "unix:\n";
2518: }
2519: } elsif ($umode eq 'none') {
2520: {
2521: my $pf = IO::File->new(">$passfilename");
2522: print $pf "none:\n";
2523: }
2524: } else {
2525: $result="auth_mode_error\n";
2526: }
2527: return $result;
2528: }
2529:
2530: sub sethost {
2531: my ($remotereq) = @_;
2532: my (undef,$hostid)=split(/:/,$remotereq);
2533: if (!defined($hostid)) { $hostid=$perlvar{'lonHostID'}; }
2534: if ($hostip{$perlvar{'lonHostID'}} eq $hostip{$hostid}) {
2535: $currenthostid=$hostid;
2536: $currentdomainid=$hostdom{$hostid};
2537: &logthis("Setting hostid to $hostid, and domain to $currentdomainid");
2538: } else {
2539: &logthis("Requested host id $hostid not an alias of ".
2540: $perlvar{'lonHostID'}." refusing connection");
2541: return 'unable_to_set';
2542: }
2543: return 'ok';
2544: }
2545:
2546: sub version {
2547: my ($userinput)=@_;
2548: $remoteVERSION=(split(/:/,$userinput))[1];
2549: return "version:$VERSION";
2550: }
2551:
2552: #There is a copy of this in lonnet.pm
2553: sub userload {
2554: my $numusers=0;
2555: {
2556: opendir(LONIDS,$perlvar{'lonIDsDir'});
2557: my $filename;
2558: my $curtime=time;
2559: while ($filename=readdir(LONIDS)) {
2560: if ($filename eq '.' || $filename eq '..') {next;}
2561: my ($mtime)=(stat($perlvar{'lonIDsDir'}.'/'.$filename))[9];
2562: if ($curtime-$mtime < 3600) { $numusers++; }
2563: }
2564: closedir(LONIDS);
2565: }
2566: my $userloadpercent=0;
2567: my $maxuserload=$perlvar{'lonUserLoadLim'};
2568: if ($maxuserload) {
2569: $userloadpercent=100*$numusers/$maxuserload;
2570: }
2571: $userloadpercent=sprintf("%.2f",$userloadpercent);
2572: return $userloadpercent;
2573: }
2574:
2575: # ----------------------------------- POD (plain old documentation, CPAN style)
2576:
2577: =head1 NAME
2578:
2579: lond - "LON Daemon" Server (port "LOND" 5663)
2580:
2581: =head1 SYNOPSIS
2582:
2583: Usage: B<lond>
2584:
2585: Should only be run as user=www. This is a command-line script which
2586: is invoked by B<loncron>. There is no expectation that a typical user
2587: will manually start B<lond> from the command-line. (In other words,
2588: DO NOT START B<lond> YOURSELF.)
2589:
2590: =head1 DESCRIPTION
2591:
2592: There are two characteristics associated with the running of B<lond>,
2593: PROCESS MANAGEMENT (starting, stopping, handling child processes)
2594: and SERVER-SIDE ACTIVITIES (password authentication, user creation,
2595: subscriptions, etc). These are described in two large
2596: sections below.
2597:
2598: B<PROCESS MANAGEMENT>
2599:
2600: Preforker - server who forks first. Runs as a daemon. HUPs.
2601: Uses IDEA encryption
2602:
2603: B<lond> forks off children processes that correspond to the other servers
2604: in the network. Management of these processes can be done at the
2605: parent process level or the child process level.
2606:
2607: B<logs/lond.log> is the location of log messages.
2608:
2609: The process management is now explained in terms of linux shell commands,
2610: subroutines internal to this code, and signal assignments:
2611:
2612: =over 4
2613:
2614: =item *
2615:
2616: PID is stored in B<logs/lond.pid>
2617:
2618: This is the process id number of the parent B<lond> process.
2619:
2620: =item *
2621:
2622: SIGTERM and SIGINT
2623:
2624: Parent signal assignment:
2625: $SIG{INT} = $SIG{TERM} = \&HUNTSMAN;
2626:
2627: Child signal assignment:
2628: $SIG{INT} = 'DEFAULT'; (and SIGTERM is DEFAULT also)
2629: (The child dies and a SIGALRM is sent to parent, awaking parent from slumber
2630: to restart a new child.)
2631:
2632: Command-line invocations:
2633: B<kill> B<-s> SIGTERM I<PID>
2634: B<kill> B<-s> SIGINT I<PID>
2635:
2636: Subroutine B<HUNTSMAN>:
2637: This is only invoked for the B<lond> parent I<PID>.
2638: This kills all the children, and then the parent.
2639: The B<lonc.pid> file is cleared.
2640:
2641: =item *
2642:
2643: SIGHUP
2644:
2645: Current bug:
2646: This signal can only be processed the first time
2647: on the parent process. Subsequent SIGHUP signals
2648: have no effect.
2649:
2650: Parent signal assignment:
2651: $SIG{HUP} = \&HUPSMAN;
2652:
2653: Child signal assignment:
2654: none (nothing happens)
2655:
2656: Command-line invocations:
2657: B<kill> B<-s> SIGHUP I<PID>
2658:
2659: Subroutine B<HUPSMAN>:
2660: This is only invoked for the B<lond> parent I<PID>,
2661: This kills all the children, and then the parent.
2662: The B<lond.pid> file is cleared.
2663:
2664: =item *
2665:
2666: SIGUSR1
2667:
2668: Parent signal assignment:
2669: $SIG{USR1} = \&USRMAN;
2670:
2671: Child signal assignment:
2672: $SIG{USR1}= \&logstatus;
2673:
2674: Command-line invocations:
2675: B<kill> B<-s> SIGUSR1 I<PID>
2676:
2677: Subroutine B<USRMAN>:
2678: When invoked for the B<lond> parent I<PID>,
2679: SIGUSR1 is sent to all the children, and the status of
2680: each connection is logged.
2681:
2682: =item *
2683:
2684: SIGUSR2
2685:
2686: Parent Signal assignment:
2687: $SIG{USR2} = \&UpdateHosts
2688:
2689: Child signal assignment:
2690: NONE
2691:
2692:
2693: =item *
2694:
2695: SIGCHLD
2696:
2697: Parent signal assignment:
2698: $SIG{CHLD} = \&REAPER;
2699:
2700: Child signal assignment:
2701: none
2702:
2703: Command-line invocations:
2704: B<kill> B<-s> SIGCHLD I<PID>
2705:
2706: Subroutine B<REAPER>:
2707: This is only invoked for the B<lond> parent I<PID>.
2708: Information pertaining to the child is removed.
2709: The socket port is cleaned up.
2710:
2711: =back
2712:
2713: B<SERVER-SIDE ACTIVITIES>
2714:
2715: Server-side information can be accepted in an encrypted or non-encrypted
2716: method.
2717:
2718: =over 4
2719:
2720: =item ping
2721:
2722: Query a client in the hosts.tab table; "Are you there?"
2723:
2724: =item pong
2725:
2726: Respond to a ping query.
2727:
2728: =item ekey
2729:
2730: Read in encrypted key, make cipher. Respond with a buildkey.
2731:
2732: =item load
2733:
2734: Respond with CPU load based on a computation upon /proc/loadavg.
2735:
2736: =item currentauth
2737:
2738: Reply with current authentication information (only over an
2739: encrypted channel).
2740:
2741: =item auth
2742:
2743: Only over an encrypted channel, reply as to whether a user's
2744: authentication information can be validated.
2745:
2746: =item passwd
2747:
2748: Allow for a password to be set.
2749:
2750: =item makeuser
2751:
2752: Make a user.
2753:
2754: =item passwd
2755:
2756: Allow for authentication mechanism and password to be changed.
2757:
2758: =item home
2759:
2760: Respond to a question "are you the home for a given user?"
2761:
2762: =item update
2763:
2764: Update contents of a subscribed resource.
2765:
2766: =item unsubscribe
2767:
2768: The server is unsubscribing from a resource.
2769:
2770: =item subscribe
2771:
2772: The server is subscribing to a resource.
2773:
2774: =item log
2775:
2776: Place in B<logs/lond.log>
2777:
2778: =item put
2779:
2780: stores hash in namespace
2781:
2782: =item rolesput
2783:
2784: put a role into a user's environment
2785:
2786: =item get
2787:
2788: returns hash with keys from array
2789: reference filled in from namespace
2790:
2791: =item eget
2792:
2793: returns hash with keys from array
2794: reference filled in from namesp (encrypts the return communication)
2795:
2796: =item rolesget
2797:
2798: get a role from a user's environment
2799:
2800: =item del
2801:
2802: deletes keys out of array from namespace
2803:
2804: =item keys
2805:
2806: returns namespace keys
2807:
2808: =item dump
2809:
2810: dumps the complete (or key matching regexp) namespace into a hash
2811:
2812: =item store
2813:
2814: stores hash permanently
2815: for this url; hashref needs to be given and should be a \%hashname; the
2816: remaining args aren't required and if they aren't passed or are '' they will
2817: be derived from the ENV
2818:
2819: =item restore
2820:
2821: returns a hash for a given url
2822:
2823: =item querysend
2824:
2825: Tells client about the lonsql process that has been launched in response
2826: to a sent query.
2827:
2828: =item queryreply
2829:
2830: Accept information from lonsql and make appropriate storage in temporary
2831: file space.
2832:
2833: =item idput
2834:
2835: Defines usernames as corresponding to IDs. (These "IDs" are unique identifiers
2836: for each student, defined perhaps by the institutional Registrar.)
2837:
2838: =item idget
2839:
2840: Returns usernames corresponding to IDs. (These "IDs" are unique identifiers
2841: for each student, defined perhaps by the institutional Registrar.)
2842:
2843: =item tmpput
2844:
2845: Accept and store information in temporary space.
2846:
2847: =item tmpget
2848:
2849: Send along temporarily stored information.
2850:
2851: =item ls
2852:
2853: List part of a user's directory.
2854:
2855: =item pushtable
2856:
2857: Pushes a file in /home/httpd/lonTab directory. Currently limited to:
2858: hosts.tab and domain.tab. The old file is copied to *.tab.backup but
2859: must be restored manually in case of a problem with the new table file.
2860: pushtable requires that the request be encrypted and validated via
2861: ValidateManager. The form of the command is:
2862: enc:pushtable tablename <tablecontents> \n
2863: where pushtable, tablename and <tablecontents> will be encrypted, but \n is a
2864: cleartext newline.
2865:
2866: =item Hanging up (exit or init)
2867:
2868: What to do when a client tells the server that they (the client)
2869: are leaving the network.
2870:
2871: =item unknown command
2872:
2873: If B<lond> is sent an unknown command (not in the list above),
2874: it replys to the client "unknown_cmd".
2875:
2876:
2877: =item UNKNOWN CLIENT
2878:
2879: If the anti-spoofing algorithm cannot verify the client,
2880: the client is rejected (with a "refused" message sent
2881: to the client, and the connection is closed.
2882:
2883: =back
2884:
2885: =head1 PREREQUISITES
2886:
2887: IO::Socket
2888: IO::File
2889: Apache::File
2890: Symbol
2891: POSIX
2892: Crypt::IDEA
2893: LWP::UserAgent()
2894: GDBM_File
2895: Authen::Krb4
2896: Authen::Krb5
2897:
2898: =head1 COREQUISITES
2899:
2900: =head1 OSNAMES
2901:
2902: linux
2903:
2904: =head1 SCRIPT CATEGORIES
2905:
2906: Server/Process
2907:
2908: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>