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