Annotation of loncom/lond, revision 1.30
1.1 albertel 1: #!/usr/bin/perl
2: # The LearningOnline Network
3: # lond "LON Daemon" Server (port "LOND" 5663)
4: # 5/26/99,6/4,6/10,6/11,6/14,6/15,6/26,6/28,6/30,
1.2 www 5: # 7/8,7/9,7/10,7/12,7/17,7/19,9/21,
1.6 www 6: # 10/7,10/8,10/9,10/11,10/13,10/15,11/4,11/16,
1.11 www 7: # 12/7,12/15,01/06,01/11,01/12,01/14,2/8,
1.12 harris41 8: # 03/07,05/31 Gerd Kortemeyer
1.13 www 9: # 06/26 Scott Harrison
1.20 www 10: # 06/29,06/30,07/14,07/15,07/17,07/20,07/25,09/18 Gerd Kortemeyer
1.25 www 11: # 12/05 Scott Harrison
12: # 12/05 Gerd Kortemeyer
1.13 www 13: #
1.1 albertel 14: # based on "Perl Cookbook" ISBN 1-56592-243-3
15: # preforker - server who forks first
16: # runs as a daemon
17: # HUPs
18: # uses IDEA encryption
19:
20: use IO::Socket;
21: use IO::File;
22: use Apache::File;
23: use Symbol;
24: use POSIX;
25: use Crypt::IDEA;
26: use LWP::UserAgent();
1.3 www 27: use GDBM_File;
28: use Authen::Krb4;
1.1 albertel 29:
1.23 harris41 30: # grabs exception and records it to log before exiting
31: sub catchexception {
1.27 albertel 32: my ($error)=@_;
1.25 www 33: $SIG{'QUIT'}='DEFAULT';
34: $SIG{__DIE__}='DEFAULT';
1.23 harris41 35: &logthis("<font color=red>CRITICAL: "
36: ."ABNORMAL EXIT. Child $$ for server $wasserver died through "
1.27 albertel 37: ."a crash with this error msg->[$error]</font>");
38: if ($client) { print $client "error: $error\n"; }
39: die($error);
1.23 harris41 40: }
41:
1.22 harris41 42: # -------------------------------- Set signal handlers to record abnormal exits
43:
44: $SIG{'QUIT'}=\&catchexception;
45: $SIG{__DIE__}=\&catchexception;
46:
1.1 albertel 47: # ------------------------------------ Read httpd access.conf and get variables
48:
1.29 harris41 49: open (CONFIG,"/etc/httpd/conf/access.conf") || die "Can't read access.conf";
1.1 albertel 50:
51: while ($configline=<CONFIG>) {
52: if ($configline =~ /PerlSetVar/) {
53: my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);
1.7 www 54: chomp($varvalue);
1.1 albertel 55: $perlvar{$varname}=$varvalue;
56: }
57: }
58: close(CONFIG);
1.19 www 59:
60: # --------------------------------------------- Check if other instance running
61:
62: my $pidfile="$perlvar{'lonDaemons'}/logs/lond.pid";
63:
64: if (-e $pidfile) {
65: my $lfh=IO::File->new("$pidfile");
66: my $pide=<$lfh>;
67: chomp($pide);
1.29 harris41 68: if (kill 0 => $pide) { die "already running"; }
1.19 www 69: }
1.1 albertel 70:
71: $PREFORK=4; # number of children to maintain, at least four spare
72:
73: # ------------------------------------------------------------- Read hosts file
74:
1.29 harris41 75: open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";
1.1 albertel 76:
77: while ($configline=<CONFIG>) {
78: my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);
79: chomp($ip);
80: $hostid{$ip}=$id;
81: if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; }
82: $PREFORK++;
83: }
84: close(CONFIG);
85:
86: # establish SERVER socket, bind and listen.
87: $server = IO::Socket::INET->new(LocalPort => $perlvar{'londPort'},
88: Type => SOCK_STREAM,
89: Proto => 'tcp',
90: Reuse => 1,
91: Listen => 10 )
1.29 harris41 92: or die "making socket: $@\n";
1.1 albertel 93:
94: # --------------------------------------------------------- Do global variables
95:
96: # global variables
97:
98: $MAX_CLIENTS_PER_CHILD = 5; # number of clients each child should
99: # process
100: %children = (); # keys are current child process IDs
101: $children = 0; # current number of children
102:
103: sub REAPER { # takes care of dead children
104: $SIG{CHLD} = \&REAPER;
105: my $pid = wait;
106: $children --;
107: &logthis("Child $pid died");
108: delete $children{$pid};
109: }
110:
111: sub HUNTSMAN { # signal handler for SIGINT
112: local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children
113: kill 'INT' => keys %children;
114: my $execdir=$perlvar{'lonDaemons'};
115: unlink("$execdir/logs/lond.pid");
1.9 www 116: &logthis("<font color=red>CRITICAL: Shutting down</font>");
1.1 albertel 117: exit; # clean up with dignity
118: }
119:
120: sub HUPSMAN { # signal handler for SIGHUP
121: local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children
122: kill 'INT' => keys %children;
123: close($server); # free up socket
1.9 www 124: &logthis("<font color=red>CRITICAL: Restarting</font>");
1.30 ! harris41 125: unlink("$execdir/logs/lond.pid");
1.1 albertel 126: my $execdir=$perlvar{'lonDaemons'};
127: exec("$execdir/lond"); # here we go again
128: }
129:
130: # --------------------------------------------------------------------- Logging
131:
132: sub logthis {
133: my $message=shift;
134: my $execdir=$perlvar{'lonDaemons'};
135: my $fh=IO::File->new(">>$execdir/logs/lond.log");
136: my $now=time;
137: my $local=localtime($now);
138: print $fh "$local ($$): $message\n";
139: }
140:
1.11 www 141:
142: # -------------------------------------------------------- Escape Special Chars
143:
144: sub escape {
145: my $str=shift;
146: $str =~ s/(\W)/"%".unpack('H2',$1)/eg;
147: return $str;
148: }
149:
150: # ----------------------------------------------------- Un-Escape Special Chars
151:
152: sub unescape {
153: my $str=shift;
154: $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
155: return $str;
156: }
157:
1.1 albertel 158: # ----------------------------------------------------------- Send USR1 to lonc
159:
160: sub reconlonc {
161: my $peerfile=shift;
162: &logthis("Trying to reconnect for $peerfile");
163: my $loncfile="$perlvar{'lonDaemons'}/logs/lonc.pid";
164: if (my $fh=IO::File->new("$loncfile")) {
165: my $loncpid=<$fh>;
166: chomp($loncpid);
167: if (kill 0 => $loncpid) {
168: &logthis("lonc at pid $loncpid responding, sending USR1");
169: kill USR1 => $loncpid;
170: sleep 1;
171: if (-e "$peerfile") { return; }
172: &logthis("$peerfile still not there, give it another try");
173: sleep 5;
174: if (-e "$peerfile") { return; }
1.9 www 175: &logthis(
176: "<font color=blue>WARNING: $peerfile still not there, giving up</font>");
1.1 albertel 177: } else {
1.9 www 178: &logthis(
179: "<font color=red>CRITICAL: "
180: ."lonc at pid $loncpid not responding, giving up</font>");
1.1 albertel 181: }
182: } else {
1.9 www 183: &logthis('<font color=red>CRITICAL: lonc not running, giving up</font>');
1.1 albertel 184: }
185: }
186:
187: # -------------------------------------------------- Non-critical communication
1.11 www 188:
1.1 albertel 189: sub subreply {
190: my ($cmd,$server)=@_;
191: my $peerfile="$perlvar{'lonSockDir'}/$server";
192: my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile",
193: Type => SOCK_STREAM,
194: Timeout => 10)
195: or return "con_lost";
196: print $sclient "$cmd\n";
197: my $answer=<$sclient>;
198: chomp($answer);
199: if (!$answer) { $answer="con_lost"; }
200: return $answer;
201: }
202:
203: sub reply {
204: my ($cmd,$server)=@_;
205: my $answer;
206: if ($server ne $perlvar{'lonHostID'}) {
207: $answer=subreply($cmd,$server);
208: if ($answer eq 'con_lost') {
209: $answer=subreply("ping",$server);
210: if ($answer ne $server) {
211: &reconlonc("$perlvar{'lonSockDir'}/$server");
212: }
213: $answer=subreply($cmd,$server);
214: }
215: } else {
216: $answer='self_reply';
217: }
218: return $answer;
219: }
220:
1.13 www 221: # -------------------------------------------------------------- Talk to lonsql
222:
1.12 harris41 223: sub sqlreply {
224: my ($cmd)=@_;
225: my $answer=subsqlreply($cmd);
226: if ($answer eq 'con_lost') { $answer=subsqlreply($cmd); }
227: return $answer;
228: }
229:
230: sub subsqlreply {
231: my ($cmd)=@_;
232: my $unixsock="mysqlsock";
233: my $peerfile="$perlvar{'lonSockDir'}/$unixsock";
234: my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile",
235: Type => SOCK_STREAM,
236: Timeout => 10)
237: or return "con_lost";
238: print $sclient "$cmd\n";
239: my $answer=<$sclient>;
240: chomp($answer);
241: if (!$answer) { $answer="con_lost"; }
242: return $answer;
243: }
244:
1.1 albertel 245: # -------------------------------------------- Return path to profile directory
1.11 www 246:
1.1 albertel 247: sub propath {
248: my ($udom,$uname)=@_;
249: $udom=~s/\W//g;
250: $uname=~s/\W//g;
1.16 www 251: my $subdir=$uname.'__';
1.1 albertel 252: $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
253: my $proname="$perlvar{'lonUsersDir'}/$udom/$subdir/$uname";
254: return $proname;
255: }
256:
257: # --------------------------------------- Is this the home server of an author?
1.11 www 258:
1.1 albertel 259: sub ishome {
260: my $author=shift;
261: $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
262: my ($udom,$uname)=split(/\//,$author);
263: my $proname=propath($udom,$uname);
264: if (-e $proname) {
265: return 'owner';
266: } else {
267: return 'not_owner';
268: }
269: }
270:
271: # ======================================================= Continue main program
272: # ---------------------------------------------------- Fork once and dissociate
273:
274: $fpid=fork;
275: exit if $fpid;
1.29 harris41 276: die "Couldn't fork: $!" unless defined ($fpid);
1.1 albertel 277:
1.29 harris41 278: POSIX::setsid() or die "Can't start new session: $!";
1.1 albertel 279:
280: # ------------------------------------------------------- Write our PID on disk
281:
282: $execdir=$perlvar{'lonDaemons'};
283: open (PIDSAVE,">$execdir/logs/lond.pid");
284: print PIDSAVE "$$\n";
285: close(PIDSAVE);
1.9 www 286: &logthis("<font color=red>CRITICAL: ---------- Starting ----------</font>");
1.1 albertel 287:
288: # ------------------------------------------------------- Now we are on our own
289:
290: # Fork off our children.
291: for (1 .. $PREFORK) {
292: make_new_child();
293: }
294:
295: # ----------------------------------------------------- Install signal handlers
296:
297: $SIG{CHLD} = \&REAPER;
298: $SIG{INT} = $SIG{TERM} = \&HUNTSMAN;
299: $SIG{HUP} = \&HUPSMAN;
300:
301: # And maintain the population.
302: while (1) {
303: sleep; # wait for a signal (i.e., child's death)
304: for ($i = $children; $i < $PREFORK; $i++) {
305: make_new_child(); # top up the child pool
306: }
307: }
308:
309: sub make_new_child {
310: my $pid;
311: my $cipher;
312: my $sigset;
313: &logthis("Attempting to start child");
314: # block signal for fork
315: $sigset = POSIX::SigSet->new(SIGINT);
316: sigprocmask(SIG_BLOCK, $sigset)
1.29 harris41 317: or die "Can't block SIGINT for fork: $!\n";
1.1 albertel 318:
1.29 harris41 319: die "fork: $!" unless defined ($pid = fork);
1.1 albertel 320:
321: if ($pid) {
322: # Parent records the child's birth and returns.
323: sigprocmask(SIG_UNBLOCK, $sigset)
1.29 harris41 324: or die "Can't unblock SIGINT for fork: $!\n";
1.1 albertel 325: $children{$pid} = 1;
326: $children++;
327: return;
328: } else {
329: # Child can *not* return from this subroutine.
330: $SIG{INT} = 'DEFAULT'; # make SIGINT kill us as it did before
331:
332: # unblock signals
333: sigprocmask(SIG_UNBLOCK, $sigset)
1.29 harris41 334: or die "Can't unblock SIGINT for fork: $!\n";
1.13 www 335:
336: $tmpsnum=0;
1.1 albertel 337:
338: # handle connections until we've reached $MAX_CLIENTS_PER_CHILD
339: for ($i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) {
340: $client = $server->accept() or last;
341:
342: # =============================================================================
343: # do something with the connection
344: # -----------------------------------------------------------------------------
1.2 www 345: # see if we know client and check for spoof IP by challenge
1.1 albertel 346: my $caller=getpeername($client);
347: my ($port,$iaddr)=unpack_sockaddr_in($caller);
348: my $clientip=inet_ntoa($iaddr);
349: my $clientrec=($hostid{$clientip} ne undef);
1.9 www 350: &logthis(
351: "<font color=yellow>INFO: Connect from $clientip ($hostid{$clientip})</font>");
1.2 www 352: my $clientok;
1.1 albertel 353: if ($clientrec) {
1.2 www 354: my $remotereq=<$client>;
355: $remotereq=~s/\W//g;
356: if ($remotereq eq 'init') {
357: my $challenge="$$".time;
358: print $client "$challenge\n";
359: $remotereq=<$client>;
360: $remotereq=~s/\W//g;
361: if ($challenge eq $remotereq) {
362: $clientok=1;
363: print $client "ok\n";
364: } else {
1.9 www 365: &logthis(
366: "<font color=blue>WARNING: $clientip did not reply challenge</font>");
1.16 www 367: print $client "bye\n";
1.2 www 368: }
369: } else {
1.9 www 370: &logthis(
371: "<font color=blue>WARNING: "
372: ."$clientip failed to initialize: >$remotereq< </font>");
1.16 www 373: print $client "bye\n";
1.2 www 374: }
375: } else {
1.9 www 376: &logthis(
377: "<font color=blue>WARNING: Unknown client $clientip</font>");
1.16 www 378: print $client "bye\n";
1.2 www 379: }
380: if ($clientok) {
1.1 albertel 381: # ---------------- New known client connecting, could mean machine online again
382: &reconlonc("$perlvar{'lonSockDir'}/$hostid{$clientip}");
1.9 www 383: &logthis(
384: "<font color=green>Established connection: $hostid{$clientip}</font>");
1.1 albertel 385: # ------------------------------------------------------------ Process requests
386: while (my $userinput=<$client>) {
387: chomp($userinput);
388: my $wasenc=0;
389: # ------------------------------------------------------------ See if encrypted
390: if ($userinput =~ /^enc/) {
391: if ($cipher) {
392: my ($cmd,$cmdlength,$encinput)=split(/:/,$userinput);
393: $userinput='';
394: for (my $encidx=0;$encidx<length($encinput);$encidx+=16) {
395: $userinput.=
396: $cipher->decrypt(
397: pack("H16",substr($encinput,$encidx,16))
398: );
399: }
400: $userinput=substr($userinput,0,$cmdlength);
401: $wasenc=1;
402: }
403: }
404: # ------------------------------------------------------------- Normal commands
405: # ------------------------------------------------------------------------ ping
406: if ($userinput =~ /^ping/) {
407: print $client "$perlvar{'lonHostID'}\n";
408: # ------------------------------------------------------------------------ pong
409: } elsif ($userinput =~ /^pong/) {
410: $reply=reply("ping",$hostid{$clientip});
411: print $client "$perlvar{'lonHostID'}:$reply\n";
412: # ------------------------------------------------------------------------ ekey
413: } elsif ($userinput =~ /^ekey/) {
414: my $buildkey=time.$$.int(rand 100000);
415: $buildkey=~tr/1-6/A-F/;
416: $buildkey=int(rand 100000).$buildkey.int(rand 100000);
417: my $key=$perlvar{'lonHostID'}.$hostid{$clientip};
418: $key=~tr/a-z/A-Z/;
419: $key=~tr/G-P/0-9/;
420: $key=~tr/Q-Z/0-9/;
421: $key=$key.$buildkey.$key.$buildkey.$key.$buildkey;
422: $key=substr($key,0,32);
423: my $cipherkey=pack("H32",$key);
424: $cipher=new IDEA $cipherkey;
425: print $client "$buildkey\n";
426: # ------------------------------------------------------------------------ load
427: } elsif ($userinput =~ /^load/) {
428: my $loadavg;
429: {
430: my $loadfile=IO::File->new('/proc/loadavg');
431: $loadavg=<$loadfile>;
432: }
433: $loadavg =~ s/\s.*//g;
434: my $loadpercent=100*$loadavg/$perlvar{'lonLoadLim'};
435: print $client "$loadpercent\n";
436: # ------------------------------------------------------------------------ auth
437: } elsif ($userinput =~ /^auth/) {
438: if ($wasenc==1) {
439: my ($cmd,$udom,$uname,$upass)=split(/:/,$userinput);
440: chomp($upass);
1.11 www 441: $upass=unescape($upass);
1.1 albertel 442: my $proname=propath($udom,$uname);
443: my $passfilename="$proname/passwd";
444: if (-e $passfilename) {
445: my $pf = IO::File->new($passfilename);
446: my $realpasswd=<$pf>;
447: chomp($realpasswd);
1.2 www 448: my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
449: my $pwdcorrect=0;
450: if ($howpwd eq 'internal') {
451: $pwdcorrect=
452: (crypt($upass,$contentpwd) eq $contentpwd);
453: } elsif ($howpwd eq 'unix') {
454: $contentpwd=(getpwnam($uname))[1];
455: $pwdcorrect=
456: (crypt($upass,$contentpwd) eq $contentpwd);
1.3 www 457: } elsif ($howpwd eq 'krb4') {
458: $pwdcorrect=(
459: Authen::Krb4::get_pw_in_tkt($uname,"",
460: $contentpwd,'krbtgt',$contentpwd,1,
461: $upass) == 0);
1.2 www 462: }
463: if ($pwdcorrect) {
1.1 albertel 464: print $client "authorized\n";
465: } else {
466: print $client "non_authorized\n";
467: }
468: } else {
469: print $client "unknown_user\n";
470: }
471: } else {
472: print $client "refused\n";
473: }
474: # ---------------------------------------------------------------------- passwd
475: } elsif ($userinput =~ /^passwd/) {
476: if ($wasenc==1) {
477: my
478: ($cmd,$udom,$uname,$upass,$npass)=split(/:/,$userinput);
479: chomp($npass);
480: my $proname=propath($udom,$uname);
481: my $passfilename="$proname/passwd";
482: if (-e $passfilename) {
483: my $realpasswd;
484: { my $pf = IO::File->new($passfilename);
485: $realpasswd=<$pf>; }
486: chomp($realpasswd);
1.2 www 487: my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
488: if ($howpwd eq 'internal') {
489: if (crypt($upass,$contentpwd) eq $contentpwd) {
490: my $salt=time;
491: $salt=substr($salt,6,2);
492: my $ncpass=crypt($npass,$salt);
1.1 albertel 493: { my $pf = IO::File->new(">$passfilename");
1.2 www 494: print $pf "internal:$ncpass\n";; }
1.1 albertel 495: print $client "ok\n";
1.2 www 496: } else {
497: print $client "non_authorized\n";
498: }
1.1 albertel 499: } else {
1.2 www 500: print $client "auth_mode_error\n";
1.1 albertel 501: }
502: } else {
503: print $client "unknown_user\n";
504: }
505: } else {
506: print $client "refused\n";
507: }
508: # ------------------------------------------------------------------------ home
509: } elsif ($userinput =~ /^home/) {
510: my ($cmd,$udom,$uname)=split(/:/,$userinput);
511: chomp($uname);
512: my $proname=propath($udom,$uname);
513: if (-e $proname) {
514: print $client "found\n";
515: } else {
516: print $client "not_found\n";
517: }
518: # ---------------------------------------------------------------------- update
519: } elsif ($userinput =~ /^update/) {
520: my ($cmd,$fname)=split(/:/,$userinput);
521: my $ownership=ishome($fname);
522: if ($ownership eq 'not_owner') {
523: if (-e $fname) {
524: my ($dev,$ino,$mode,$nlink,
525: $uid,$gid,$rdev,$size,
526: $atime,$mtime,$ctime,
527: $blksize,$blocks)=stat($fname);
528: $now=time;
529: $since=$now-$atime;
530: if ($since>$perlvar{'lonExpire'}) {
531: $reply=
532: reply("unsub:$fname","$hostid{$clientip}");
533: unlink("$fname");
534: } else {
535: my $transname="$fname.in.transfer";
536: my $remoteurl=
537: reply("sub:$fname","$hostid{$clientip}");
538: my $response;
539: {
540: my $ua=new LWP::UserAgent;
541: my $request=new HTTP::Request('GET',"$remoteurl");
542: $response=$ua->request($request,$transname);
543: }
544: if ($response->is_error()) {
1.24 albertel 545: unlink($transname);
1.1 albertel 546: my $message=$response->status_line;
547: &logthis(
548: "LWP GET: $message for $fname ($remoteurl)");
549: } else {
1.14 www 550: if ($remoteurl!~/\.meta$/) {
1.28 www 551: my $ua=new LWP::UserAgent;
1.14 www 552: my $mrequest=
553: new HTTP::Request('GET',$remoteurl.'.meta');
554: my $mresponse=
555: $ua->request($mrequest,$fname.'.meta');
556: if ($mresponse->is_error()) {
557: unlink($fname.'.meta');
558: }
559: }
1.1 albertel 560: rename($transname,$fname);
561: }
562: }
563: print $client "ok\n";
564: } else {
565: print $client "not_found\n";
566: }
567: } else {
568: print $client "rejected\n";
569: }
570: # ----------------------------------------------------------------- unsubscribe
571: } elsif ($userinput =~ /^unsub/) {
572: my ($cmd,$fname)=split(/:/,$userinput);
573: if (-e $fname) {
574: if (unlink("$fname.$hostid{$clientip}")) {
575: print $client "ok\n";
576: } else {
577: print $client "not_subscribed\n";
578: }
579: } else {
580: print $client "not_found\n";
581: }
582: # ------------------------------------------------------------------- subscribe
583: } elsif ($userinput =~ /^sub/) {
584: my ($cmd,$fname)=split(/:/,$userinput);
585: my $ownership=ishome($fname);
586: if ($ownership eq 'owner') {
587: if (-e $fname) {
1.18 www 588: if (-d $fname) {
589: print $client "directory\n";
590: } else {
1.1 albertel 591: $now=time;
592: {
1.26 www 593: my $sh;
1.25 www 594: if ($sh=
595: IO::File->new(">$fname.$hostid{$clientip}")) {
596: print $sh "$clientip:$now\n";
597: }
1.1 albertel 598: }
599: $fname=~s/\/home\/httpd\/html\/res/raw/;
600: $fname="http://$thisserver/".$fname;
601: print $client "$fname\n";
1.18 www 602: }
1.1 albertel 603: } else {
604: print $client "not_found\n";
605: }
606: } else {
607: print $client "rejected\n";
608: }
1.12 harris41 609: # ------------------------------------------------------------------------- log
610: } elsif ($userinput =~ /^log/) {
611: my ($cmd,$udom,$uname,$what)=split(/:/,$userinput);
612: chomp($what);
613: my $proname=propath($udom,$uname);
614: my $now=time;
615: {
616: my $hfh;
617: if ($hfh=IO::File->new(">>$proname/activity.log")) {
618: print $hfh "$now:$hostid{$clientip}:$what\n";
619: print $client "ok\n";
620: } else {
621: print $client "error:$!\n";
622: }
623: }
1.1 albertel 624: # ------------------------------------------------------------------------- put
625: } elsif ($userinput =~ /^put/) {
1.6 www 626: my ($cmd,$udom,$uname,$namespace,$what)
1.1 albertel 627: =split(/:/,$userinput);
1.8 www 628: $namespace=~s/\//\_/g;
1.6 www 629: $namespace=~s/\W//g;
630: if ($namespace ne 'roles') {
1.1 albertel 631: chomp($what);
632: my $proname=propath($udom,$uname);
633: my $now=time;
634: {
635: my $hfh;
636: if (
637: $hfh=IO::File->new(">>$proname/$namespace.hist")
638: ) { print $hfh "P:$now:$what\n"; }
639: }
640: my @pairs=split(/\&/,$what);
1.4 www 641: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
1.1 albertel 642: foreach $pair (@pairs) {
643: ($key,$value)=split(/=/,$pair);
644: $hash{$key}=$value;
645: }
1.4 www 646: if (untie(%hash)) {
1.1 albertel 647: print $client "ok\n";
648: } else {
649: print $client "error:$!\n";
650: }
651: } else {
652: print $client "error:$!\n";
653: }
1.6 www 654: } else {
655: print $client "refused\n";
656: }
657: # -------------------------------------------------------------------- rolesput
658: } elsif ($userinput =~ /^rolesput/) {
659: if ($wasenc==1) {
660: my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
661: =split(/:/,$userinput);
662: my $namespace='roles';
663: chomp($what);
664: my $proname=propath($udom,$uname);
665: my $now=time;
666: {
667: my $hfh;
668: if (
669: $hfh=IO::File->new(">>$proname/$namespace.hist")
670: ) {
671: print $hfh "P:$now:$exedom:$exeuser:$what\n";
672: }
673: }
674: my @pairs=split(/\&/,$what);
675: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
676: foreach $pair (@pairs) {
677: ($key,$value)=split(/=/,$pair);
678: $hash{$key}=$value;
679: }
680: if (untie(%hash)) {
681: print $client "ok\n";
682: } else {
683: print $client "error:$!\n";
684: }
685: } else {
686: print $client "error:$!\n";
687: }
688: } else {
689: print $client "refused\n";
690: }
1.1 albertel 691: # ------------------------------------------------------------------------- get
692: } elsif ($userinput =~ /^get/) {
693: my ($cmd,$udom,$uname,$namespace,$what)
694: =split(/:/,$userinput);
1.8 www 695: $namespace=~s/\//\_/g;
1.1 albertel 696: $namespace=~s/\W//g;
697: chomp($what);
698: my @queries=split(/\&/,$what);
699: my $proname=propath($udom,$uname);
700: my $qresult='';
1.20 www 701: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 702: for ($i=0;$i<=$#queries;$i++) {
703: $qresult.="$hash{$queries[$i]}&";
704: }
1.4 www 705: if (untie(%hash)) {
1.1 albertel 706: $qresult=~s/\&$//;
707: print $client "$qresult\n";
708: } else {
709: print $client "error:$!\n";
710: }
711: } else {
712: print $client "error:$!\n";
713: }
714: # ------------------------------------------------------------------------ eget
715: } elsif ($userinput =~ /^eget/) {
716: my ($cmd,$udom,$uname,$namespace,$what)
717: =split(/:/,$userinput);
1.8 www 718: $namespace=~s/\//\_/g;
1.1 albertel 719: $namespace=~s/\W//g;
720: chomp($what);
721: my @queries=split(/\&/,$what);
722: my $proname=propath($udom,$uname);
723: my $qresult='';
1.20 www 724: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 725: for ($i=0;$i<=$#queries;$i++) {
726: $qresult.="$hash{$queries[$i]}&";
727: }
1.4 www 728: if (untie(%hash)) {
1.1 albertel 729: $qresult=~s/\&$//;
730: if ($cipher) {
731: my $cmdlength=length($qresult);
732: $qresult.=" ";
733: my $encqresult='';
734: for
735: (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
736: $encqresult.=
737: unpack("H16",
738: $cipher->encrypt(substr($qresult,$encidx,8)));
739: }
740: print $client "enc:$cmdlength:$encqresult\n";
741: } else {
742: print $client "error:no_key\n";
743: }
744: } else {
745: print $client "error:$!\n";
746: }
747: } else {
748: print $client "error:$!\n";
749: }
750: # ------------------------------------------------------------------------- del
751: } elsif ($userinput =~ /^del/) {
752: my ($cmd,$udom,$uname,$namespace,$what)
753: =split(/:/,$userinput);
1.8 www 754: $namespace=~s/\//\_/g;
1.1 albertel 755: $namespace=~s/\W//g;
756: chomp($what);
757: my $proname=propath($udom,$uname);
758: my $now=time;
759: {
760: my $hfh;
761: if (
762: $hfh=IO::File->new(">>$proname/$namespace.hist")
763: ) { print $hfh "D:$now:$what\n"; }
764: }
765: my @keys=split(/\&/,$what);
1.4 www 766: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
1.1 albertel 767: foreach $key (@keys) {
768: delete($hash{$key});
769: }
1.4 www 770: if (untie(%hash)) {
1.1 albertel 771: print $client "ok\n";
772: } else {
773: print $client "error:$!\n";
774: }
775: } else {
776: print $client "error:$!\n";
777: }
778: # ------------------------------------------------------------------------ keys
779: } elsif ($userinput =~ /^keys/) {
780: my ($cmd,$udom,$uname,$namespace)
781: =split(/:/,$userinput);
1.8 www 782: $namespace=~s/\//\_/g;
1.1 albertel 783: $namespace=~s/\W//g;
784: my $proname=propath($udom,$uname);
785: my $qresult='';
1.20 www 786: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 787: foreach $key (keys %hash) {
788: $qresult.="$key&";
789: }
1.4 www 790: if (untie(%hash)) {
1.1 albertel 791: $qresult=~s/\&$//;
792: print $client "$qresult\n";
793: } else {
794: print $client "error:$!\n";
795: }
796: } else {
797: print $client "error:$!\n";
798: }
799: # ------------------------------------------------------------------------ dump
800: } elsif ($userinput =~ /^dump/) {
801: my ($cmd,$udom,$uname,$namespace)
802: =split(/:/,$userinput);
1.8 www 803: $namespace=~s/\//\_/g;
1.1 albertel 804: $namespace=~s/\W//g;
805: my $proname=propath($udom,$uname);
806: my $qresult='';
1.20 www 807: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 808: foreach $key (keys %hash) {
809: $qresult.="$key=$hash{$key}&";
1.7 www 810: }
811: if (untie(%hash)) {
812: $qresult=~s/\&$//;
813: print $client "$qresult\n";
814: } else {
815: print $client "error:$!\n";
816: }
817: } else {
818: print $client "error:$!\n";
819: }
820: # ----------------------------------------------------------------------- store
821: } elsif ($userinput =~ /^store/) {
822: my ($cmd,$udom,$uname,$namespace,$rid,$what)
823: =split(/:/,$userinput);
1.8 www 824: $namespace=~s/\//\_/g;
1.7 www 825: $namespace=~s/\W//g;
826: if ($namespace ne 'roles') {
827: chomp($what);
828: my $proname=propath($udom,$uname);
829: my $now=time;
830: {
831: my $hfh;
832: if (
833: $hfh=IO::File->new(">>$proname/$namespace.hist")
834: ) { print $hfh "P:$now:$rid:$what\n"; }
835: }
836: my @pairs=split(/\&/,$what);
837:
838: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
839: my @previouskeys=split(/&/,$hash{"keys:$rid"});
840: my $key;
841: $hash{"version:$rid"}++;
842: my $version=$hash{"version:$rid"};
843: my $allkeys='';
844: foreach $pair (@pairs) {
845: ($key,$value)=split(/=/,$pair);
846: $allkeys.=$key.':';
847: $hash{"$version:$rid:$key"}=$value;
848: }
849: $allkeys=~s/:$//;
850: $hash{"$version:keys:$rid"}=$allkeys;
851: if (untie(%hash)) {
852: print $client "ok\n";
853: } else {
854: print $client "error:$!\n";
855: }
856: } else {
857: print $client "error:$!\n";
858: }
859: } else {
860: print $client "refused\n";
861: }
862: # --------------------------------------------------------------------- restore
863: } elsif ($userinput =~ /^restore/) {
864: my ($cmd,$udom,$uname,$namespace,$rid)
865: =split(/:/,$userinput);
1.8 www 866: $namespace=~s/\//\_/g;
1.7 www 867: $namespace=~s/\W//g;
868: chomp($rid);
869: my $proname=propath($udom,$uname);
870: my $qresult='';
1.20 www 871: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.7 www 872: my $version=$hash{"version:$rid"};
873: $qresult.="version=$version&";
874: my $scope;
875: for ($scope=1;$scope<=$version;$scope++) {
876: my $vkeys=$hash{"$scope:keys:$rid"};
877: my @keys=split(/:/,$vkeys);
878: my $key;
879: $qresult.="$scope:keys=$vkeys&";
880: foreach $key (@keys) {
1.21 www 881: $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&";
1.7 www 882: }
1.1 albertel 883: }
1.4 www 884: if (untie(%hash)) {
1.1 albertel 885: $qresult=~s/\&$//;
886: print $client "$qresult\n";
887: } else {
888: print $client "error:$!\n";
889: }
890: } else {
891: print $client "error:$!\n";
892: }
1.12 harris41 893: # ------------------------------------------------------------------- querysend
894: } elsif ($userinput =~ /^querysend/) {
895: my ($cmd,$query)=split(/:/,$userinput);
896: $query=~s/\n*$//g;
1.13 www 897: print $client sqlreply("$hostid{$clientip}\&$query")."\n";
1.12 harris41 898: # ------------------------------------------------------------------ queryreply
899: } elsif ($userinput =~ /^queryreply/) {
900: my ($cmd,$id,$reply)=split(/:/,$userinput);
901: my $store;
1.13 www 902: my $execdir=$perlvar{'lonDaemons'};
903: if ($store=IO::File->new(">$execdir/tmp/$id")) {
1.12 harris41 904: print $store $reply;
905: close $store;
906: print $client "ok\n";
907: }
908: else {
909: print $client "error:$!\n";
910: }
1.1 albertel 911: # ----------------------------------------------------------------------- idput
912: } elsif ($userinput =~ /^idput/) {
913: my ($cmd,$udom,$what)=split(/:/,$userinput);
914: chomp($what);
915: $udom=~s/\W//g;
916: my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
917: my $now=time;
918: {
919: my $hfh;
920: if (
921: $hfh=IO::File->new(">>$proname.hist")
922: ) { print $hfh "P:$now:$what\n"; }
923: }
924: my @pairs=split(/\&/,$what);
1.4 www 925: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT,0640)) {
1.1 albertel 926: foreach $pair (@pairs) {
927: ($key,$value)=split(/=/,$pair);
928: $hash{$key}=$value;
929: }
1.4 www 930: if (untie(%hash)) {
1.1 albertel 931: print $client "ok\n";
932: } else {
933: print $client "error:$!\n";
934: }
935: } else {
936: print $client "error:$!\n";
937: }
938: # ----------------------------------------------------------------------- idget
939: } elsif ($userinput =~ /^idget/) {
940: my ($cmd,$udom,$what)=split(/:/,$userinput);
941: chomp($what);
942: $udom=~s/\W//g;
943: my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
944: my @queries=split(/\&/,$what);
945: my $qresult='';
1.20 www 946: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER,0640)) {
1.1 albertel 947: for ($i=0;$i<=$#queries;$i++) {
948: $qresult.="$hash{$queries[$i]}&";
949: }
1.4 www 950: if (untie(%hash)) {
1.1 albertel 951: $qresult=~s/\&$//;
952: print $client "$qresult\n";
953: } else {
954: print $client "error:$!\n";
955: }
956: } else {
957: print $client "error:$!\n";
958: }
1.13 www 959: # ---------------------------------------------------------------------- tmpput
960: } elsif ($userinput =~ /^tmpput/) {
961: my ($cmd,$what)=split(/:/,$userinput);
962: my $store;
963: $tmpsnum++;
964: my $id=$$.'_'.$clientip.'_'.$tmpsnum;
965: $id=~s/\W/\_/g;
966: $what=~s/\n//g;
967: my $execdir=$perlvar{'lonDaemons'};
968: if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) {
969: print $store $what;
970: close $store;
971: print $client "$id\n";
972: }
973: else {
974: print $client "error:$!\n";
975: }
976:
977: # ---------------------------------------------------------------------- tmpget
978: } elsif ($userinput =~ /^tmpget/) {
979: my ($cmd,$id)=split(/:/,$userinput);
980: chomp($id);
981: $id=~s/\W/\_/g;
982: my $store;
983: my $execdir=$perlvar{'lonDaemons'};
984: if ($store=IO::File->new("$execdir/tmp/$id.tmp")) {
985: my $reply=<$store>;
986: print $client "$reply\n";
987: close $store;
988: }
989: else {
990: print $client "error:$!\n";
991: }
992:
1.5 www 993: # -------------------------------------------------------------------------- ls
994: } elsif ($userinput =~ /^ls/) {
995: my ($cmd,$ulsdir)=split(/:/,$userinput);
996: my $ulsout='';
997: my $ulsfn;
998: if (-e $ulsdir) {
999: while ($ulsfn=<$ulsdir/*>) {
1000: my @ulsstats=stat($ulsfn);
1001: $ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
1002: }
1003: } else {
1004: $ulsout='no_such_dir';
1005: }
1.17 www 1006: if ($ulsout eq '') { $ulsout='empty'; }
1.5 www 1007: print $client "$ulsout\n";
1.1 albertel 1008: # ------------------------------------------------------------- unknown command
1009: } else {
1010: # unknown command
1011: print $client "unknown_cmd\n";
1012: }
1013: # ------------------------------------------------------ client unknown, refuse
1014: }
1015: } else {
1016: print $client "refused\n";
1.9 www 1017: &logthis("<font color=blue>WARNING: "
1018: ."Rejected client $clientip, closing connection</font>");
1.1 albertel 1019: }
1.9 www 1020: &logthis("<font color=red>CRITICAL: "
1.10 www 1021: ."Disconnect from $clientip ($hostid{$clientip})</font>");
1.1 albertel 1022: # =============================================================================
1023: }
1024:
1025: # tidy up gracefully and finish
1026:
1027: # this exit is VERY important, otherwise the child will become
1028: # a producer of more and more children, forking yourself into
1029: # process death.
1030: exit;
1031: }
1032: }
1033:
1034:
1035:
1036:
1037:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>