Annotation of loncom/lond, revision 1.28
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:
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 "
1.27 albertel 53: ."\_\_DIE\_\_ with this error msg->[$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$/) {
1.28 ! www 568: my $ua=new LWP::UserAgent;
1.14 www 569: my $mrequest=
570: new HTTP::Request('GET',$remoteurl.'.meta');
571: my $mresponse=
572: $ua->request($mrequest,$fname.'.meta');
573: if ($mresponse->is_error()) {
574: unlink($fname.'.meta');
575: }
576: }
1.1 albertel 577: rename($transname,$fname);
578: }
579: }
580: print $client "ok\n";
581: } else {
582: print $client "not_found\n";
583: }
584: } else {
585: print $client "rejected\n";
586: }
587: # ----------------------------------------------------------------- unsubscribe
588: } elsif ($userinput =~ /^unsub/) {
589: my ($cmd,$fname)=split(/:/,$userinput);
590: if (-e $fname) {
591: if (unlink("$fname.$hostid{$clientip}")) {
592: print $client "ok\n";
593: } else {
594: print $client "not_subscribed\n";
595: }
596: } else {
597: print $client "not_found\n";
598: }
599: # ------------------------------------------------------------------- subscribe
600: } elsif ($userinput =~ /^sub/) {
601: my ($cmd,$fname)=split(/:/,$userinput);
602: my $ownership=ishome($fname);
603: if ($ownership eq 'owner') {
604: if (-e $fname) {
1.18 www 605: if (-d $fname) {
606: print $client "directory\n";
607: } else {
1.1 albertel 608: $now=time;
609: {
1.26 www 610: my $sh;
1.25 www 611: if ($sh=
612: IO::File->new(">$fname.$hostid{$clientip}")) {
613: print $sh "$clientip:$now\n";
614: }
1.1 albertel 615: }
616: $fname=~s/\/home\/httpd\/html\/res/raw/;
617: $fname="http://$thisserver/".$fname;
618: print $client "$fname\n";
1.18 www 619: }
1.1 albertel 620: } else {
621: print $client "not_found\n";
622: }
623: } else {
624: print $client "rejected\n";
625: }
1.12 harris41 626: # ------------------------------------------------------------------------- log
627: } elsif ($userinput =~ /^log/) {
628: my ($cmd,$udom,$uname,$what)=split(/:/,$userinput);
629: chomp($what);
630: my $proname=propath($udom,$uname);
631: my $now=time;
632: {
633: my $hfh;
634: if ($hfh=IO::File->new(">>$proname/activity.log")) {
635: print $hfh "$now:$hostid{$clientip}:$what\n";
636: print $client "ok\n";
637: } else {
638: print $client "error:$!\n";
639: }
640: }
1.1 albertel 641: # ------------------------------------------------------------------------- put
642: } elsif ($userinput =~ /^put/) {
1.6 www 643: my ($cmd,$udom,$uname,$namespace,$what)
1.1 albertel 644: =split(/:/,$userinput);
1.8 www 645: $namespace=~s/\//\_/g;
1.6 www 646: $namespace=~s/\W//g;
647: if ($namespace ne 'roles') {
1.1 albertel 648: chomp($what);
649: my $proname=propath($udom,$uname);
650: my $now=time;
651: {
652: my $hfh;
653: if (
654: $hfh=IO::File->new(">>$proname/$namespace.hist")
655: ) { print $hfh "P:$now:$what\n"; }
656: }
657: my @pairs=split(/\&/,$what);
1.4 www 658: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
1.1 albertel 659: foreach $pair (@pairs) {
660: ($key,$value)=split(/=/,$pair);
661: $hash{$key}=$value;
662: }
1.4 www 663: if (untie(%hash)) {
1.1 albertel 664: print $client "ok\n";
665: } else {
666: print $client "error:$!\n";
667: }
668: } else {
669: print $client "error:$!\n";
670: }
1.6 www 671: } else {
672: print $client "refused\n";
673: }
674: # -------------------------------------------------------------------- rolesput
675: } elsif ($userinput =~ /^rolesput/) {
676: if ($wasenc==1) {
677: my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
678: =split(/:/,$userinput);
679: my $namespace='roles';
680: chomp($what);
681: my $proname=propath($udom,$uname);
682: my $now=time;
683: {
684: my $hfh;
685: if (
686: $hfh=IO::File->new(">>$proname/$namespace.hist")
687: ) {
688: print $hfh "P:$now:$exedom:$exeuser:$what\n";
689: }
690: }
691: my @pairs=split(/\&/,$what);
692: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
693: foreach $pair (@pairs) {
694: ($key,$value)=split(/=/,$pair);
695: $hash{$key}=$value;
696: }
697: if (untie(%hash)) {
698: print $client "ok\n";
699: } else {
700: print $client "error:$!\n";
701: }
702: } else {
703: print $client "error:$!\n";
704: }
705: } else {
706: print $client "refused\n";
707: }
1.1 albertel 708: # ------------------------------------------------------------------------- get
709: } elsif ($userinput =~ /^get/) {
710: my ($cmd,$udom,$uname,$namespace,$what)
711: =split(/:/,$userinput);
1.8 www 712: $namespace=~s/\//\_/g;
1.1 albertel 713: $namespace=~s/\W//g;
714: chomp($what);
715: my @queries=split(/\&/,$what);
716: my $proname=propath($udom,$uname);
717: my $qresult='';
1.20 www 718: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 719: for ($i=0;$i<=$#queries;$i++) {
720: $qresult.="$hash{$queries[$i]}&";
721: }
1.4 www 722: if (untie(%hash)) {
1.1 albertel 723: $qresult=~s/\&$//;
724: print $client "$qresult\n";
725: } else {
726: print $client "error:$!\n";
727: }
728: } else {
729: print $client "error:$!\n";
730: }
731: # ------------------------------------------------------------------------ eget
732: } elsif ($userinput =~ /^eget/) {
733: my ($cmd,$udom,$uname,$namespace,$what)
734: =split(/:/,$userinput);
1.8 www 735: $namespace=~s/\//\_/g;
1.1 albertel 736: $namespace=~s/\W//g;
737: chomp($what);
738: my @queries=split(/\&/,$what);
739: my $proname=propath($udom,$uname);
740: my $qresult='';
1.20 www 741: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 742: for ($i=0;$i<=$#queries;$i++) {
743: $qresult.="$hash{$queries[$i]}&";
744: }
1.4 www 745: if (untie(%hash)) {
1.1 albertel 746: $qresult=~s/\&$//;
747: if ($cipher) {
748: my $cmdlength=length($qresult);
749: $qresult.=" ";
750: my $encqresult='';
751: for
752: (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
753: $encqresult.=
754: unpack("H16",
755: $cipher->encrypt(substr($qresult,$encidx,8)));
756: }
757: print $client "enc:$cmdlength:$encqresult\n";
758: } else {
759: print $client "error:no_key\n";
760: }
761: } else {
762: print $client "error:$!\n";
763: }
764: } else {
765: print $client "error:$!\n";
766: }
767: # ------------------------------------------------------------------------- del
768: } elsif ($userinput =~ /^del/) {
769: my ($cmd,$udom,$uname,$namespace,$what)
770: =split(/:/,$userinput);
1.8 www 771: $namespace=~s/\//\_/g;
1.1 albertel 772: $namespace=~s/\W//g;
773: chomp($what);
774: my $proname=propath($udom,$uname);
775: my $now=time;
776: {
777: my $hfh;
778: if (
779: $hfh=IO::File->new(">>$proname/$namespace.hist")
780: ) { print $hfh "D:$now:$what\n"; }
781: }
782: my @keys=split(/\&/,$what);
1.4 www 783: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
1.1 albertel 784: foreach $key (@keys) {
785: delete($hash{$key});
786: }
1.4 www 787: if (untie(%hash)) {
1.1 albertel 788: print $client "ok\n";
789: } else {
790: print $client "error:$!\n";
791: }
792: } else {
793: print $client "error:$!\n";
794: }
795: # ------------------------------------------------------------------------ keys
796: } elsif ($userinput =~ /^keys/) {
797: my ($cmd,$udom,$uname,$namespace)
798: =split(/:/,$userinput);
1.8 www 799: $namespace=~s/\//\_/g;
1.1 albertel 800: $namespace=~s/\W//g;
801: my $proname=propath($udom,$uname);
802: my $qresult='';
1.20 www 803: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 804: foreach $key (keys %hash) {
805: $qresult.="$key&";
806: }
1.4 www 807: if (untie(%hash)) {
1.1 albertel 808: $qresult=~s/\&$//;
809: print $client "$qresult\n";
810: } else {
811: print $client "error:$!\n";
812: }
813: } else {
814: print $client "error:$!\n";
815: }
816: # ------------------------------------------------------------------------ dump
817: } elsif ($userinput =~ /^dump/) {
818: my ($cmd,$udom,$uname,$namespace)
819: =split(/:/,$userinput);
1.8 www 820: $namespace=~s/\//\_/g;
1.1 albertel 821: $namespace=~s/\W//g;
822: my $proname=propath($udom,$uname);
823: my $qresult='';
1.20 www 824: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 825: foreach $key (keys %hash) {
826: $qresult.="$key=$hash{$key}&";
1.7 www 827: }
828: if (untie(%hash)) {
829: $qresult=~s/\&$//;
830: print $client "$qresult\n";
831: } else {
832: print $client "error:$!\n";
833: }
834: } else {
835: print $client "error:$!\n";
836: }
837: # ----------------------------------------------------------------------- store
838: } elsif ($userinput =~ /^store/) {
839: my ($cmd,$udom,$uname,$namespace,$rid,$what)
840: =split(/:/,$userinput);
1.8 www 841: $namespace=~s/\//\_/g;
1.7 www 842: $namespace=~s/\W//g;
843: if ($namespace ne 'roles') {
844: chomp($what);
845: my $proname=propath($udom,$uname);
846: my $now=time;
847: {
848: my $hfh;
849: if (
850: $hfh=IO::File->new(">>$proname/$namespace.hist")
851: ) { print $hfh "P:$now:$rid:$what\n"; }
852: }
853: my @pairs=split(/\&/,$what);
854:
855: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
856: my @previouskeys=split(/&/,$hash{"keys:$rid"});
857: my $key;
858: $hash{"version:$rid"}++;
859: my $version=$hash{"version:$rid"};
860: my $allkeys='';
861: foreach $pair (@pairs) {
862: ($key,$value)=split(/=/,$pair);
863: $allkeys.=$key.':';
864: $hash{"$version:$rid:$key"}=$value;
865: }
866: $allkeys=~s/:$//;
867: $hash{"$version:keys:$rid"}=$allkeys;
868: if (untie(%hash)) {
869: print $client "ok\n";
870: } else {
871: print $client "error:$!\n";
872: }
873: } else {
874: print $client "error:$!\n";
875: }
876: } else {
877: print $client "refused\n";
878: }
879: # --------------------------------------------------------------------- restore
880: } elsif ($userinput =~ /^restore/) {
881: my ($cmd,$udom,$uname,$namespace,$rid)
882: =split(/:/,$userinput);
1.8 www 883: $namespace=~s/\//\_/g;
1.7 www 884: $namespace=~s/\W//g;
885: chomp($rid);
886: my $proname=propath($udom,$uname);
887: my $qresult='';
1.20 www 888: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.7 www 889: my $version=$hash{"version:$rid"};
890: $qresult.="version=$version&";
891: my $scope;
892: for ($scope=1;$scope<=$version;$scope++) {
893: my $vkeys=$hash{"$scope:keys:$rid"};
894: my @keys=split(/:/,$vkeys);
895: my $key;
896: $qresult.="$scope:keys=$vkeys&";
897: foreach $key (@keys) {
1.21 www 898: $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&";
1.7 www 899: }
1.1 albertel 900: }
1.4 www 901: if (untie(%hash)) {
1.1 albertel 902: $qresult=~s/\&$//;
903: print $client "$qresult\n";
904: } else {
905: print $client "error:$!\n";
906: }
907: } else {
908: print $client "error:$!\n";
909: }
1.12 harris41 910: # ------------------------------------------------------------------- querysend
911: } elsif ($userinput =~ /^querysend/) {
912: my ($cmd,$query)=split(/:/,$userinput);
913: $query=~s/\n*$//g;
1.13 www 914: print $client sqlreply("$hostid{$clientip}\&$query")."\n";
1.12 harris41 915: # ------------------------------------------------------------------ queryreply
916: } elsif ($userinput =~ /^queryreply/) {
917: my ($cmd,$id,$reply)=split(/:/,$userinput);
918: my $store;
1.13 www 919: my $execdir=$perlvar{'lonDaemons'};
920: if ($store=IO::File->new(">$execdir/tmp/$id")) {
1.12 harris41 921: print $store $reply;
922: close $store;
923: print $client "ok\n";
924: }
925: else {
926: print $client "error:$!\n";
927: }
1.1 albertel 928: # ----------------------------------------------------------------------- idput
929: } elsif ($userinput =~ /^idput/) {
930: my ($cmd,$udom,$what)=split(/:/,$userinput);
931: chomp($what);
932: $udom=~s/\W//g;
933: my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
934: my $now=time;
935: {
936: my $hfh;
937: if (
938: $hfh=IO::File->new(">>$proname.hist")
939: ) { print $hfh "P:$now:$what\n"; }
940: }
941: my @pairs=split(/\&/,$what);
1.4 www 942: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT,0640)) {
1.1 albertel 943: foreach $pair (@pairs) {
944: ($key,$value)=split(/=/,$pair);
945: $hash{$key}=$value;
946: }
1.4 www 947: if (untie(%hash)) {
1.1 albertel 948: print $client "ok\n";
949: } else {
950: print $client "error:$!\n";
951: }
952: } else {
953: print $client "error:$!\n";
954: }
955: # ----------------------------------------------------------------------- idget
956: } elsif ($userinput =~ /^idget/) {
957: my ($cmd,$udom,$what)=split(/:/,$userinput);
958: chomp($what);
959: $udom=~s/\W//g;
960: my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
961: my @queries=split(/\&/,$what);
962: my $qresult='';
1.20 www 963: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER,0640)) {
1.1 albertel 964: for ($i=0;$i<=$#queries;$i++) {
965: $qresult.="$hash{$queries[$i]}&";
966: }
1.4 www 967: if (untie(%hash)) {
1.1 albertel 968: $qresult=~s/\&$//;
969: print $client "$qresult\n";
970: } else {
971: print $client "error:$!\n";
972: }
973: } else {
974: print $client "error:$!\n";
975: }
1.13 www 976: # ---------------------------------------------------------------------- tmpput
977: } elsif ($userinput =~ /^tmpput/) {
978: my ($cmd,$what)=split(/:/,$userinput);
979: my $store;
980: $tmpsnum++;
981: my $id=$$.'_'.$clientip.'_'.$tmpsnum;
982: $id=~s/\W/\_/g;
983: $what=~s/\n//g;
984: my $execdir=$perlvar{'lonDaemons'};
985: if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) {
986: print $store $what;
987: close $store;
988: print $client "$id\n";
989: }
990: else {
991: print $client "error:$!\n";
992: }
993:
994: # ---------------------------------------------------------------------- tmpget
995: } elsif ($userinput =~ /^tmpget/) {
996: my ($cmd,$id)=split(/:/,$userinput);
997: chomp($id);
998: $id=~s/\W/\_/g;
999: my $store;
1000: my $execdir=$perlvar{'lonDaemons'};
1001: if ($store=IO::File->new("$execdir/tmp/$id.tmp")) {
1002: my $reply=<$store>;
1003: print $client "$reply\n";
1004: close $store;
1005: }
1006: else {
1007: print $client "error:$!\n";
1008: }
1009:
1.5 www 1010: # -------------------------------------------------------------------------- ls
1011: } elsif ($userinput =~ /^ls/) {
1012: my ($cmd,$ulsdir)=split(/:/,$userinput);
1013: my $ulsout='';
1014: my $ulsfn;
1015: if (-e $ulsdir) {
1016: while ($ulsfn=<$ulsdir/*>) {
1017: my @ulsstats=stat($ulsfn);
1018: $ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
1019: }
1020: } else {
1021: $ulsout='no_such_dir';
1022: }
1.17 www 1023: if ($ulsout eq '') { $ulsout='empty'; }
1.5 www 1024: print $client "$ulsout\n";
1.1 albertel 1025: # ------------------------------------------------------------- unknown command
1026: } else {
1027: # unknown command
1028: print $client "unknown_cmd\n";
1029: }
1030: # ------------------------------------------------------ client unknown, refuse
1031: }
1032: } else {
1033: print $client "refused\n";
1.9 www 1034: &logthis("<font color=blue>WARNING: "
1035: ."Rejected client $clientip, closing connection</font>");
1.1 albertel 1036: }
1.9 www 1037: &logthis("<font color=red>CRITICAL: "
1.10 www 1038: ."Disconnect from $clientip ($hostid{$clientip})</font>");
1.1 albertel 1039: # =============================================================================
1040: }
1041:
1042: # tidy up gracefully and finish
1043:
1044: # this exit is VERY important, otherwise the child will become
1045: # a producer of more and more children, forking yourself into
1046: # process death.
1047: exit;
1048: }
1049: }
1050:
1051:
1052:
1053:
1054:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>