Annotation of loncom/lond, revision 1.34
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
1.34 ! www 12: # 12/05,12/13,12/29 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);
1.32 www 480: $upass=&unescape($upass);
481: $npass=&unescape($npass);
1.1 albertel 482: my $proname=propath($udom,$uname);
483: my $passfilename="$proname/passwd";
484: if (-e $passfilename) {
485: my $realpasswd;
486: { my $pf = IO::File->new($passfilename);
487: $realpasswd=<$pf>; }
488: chomp($realpasswd);
1.2 www 489: my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
490: if ($howpwd eq 'internal') {
491: if (crypt($upass,$contentpwd) eq $contentpwd) {
492: my $salt=time;
493: $salt=substr($salt,6,2);
494: my $ncpass=crypt($npass,$salt);
1.1 albertel 495: { my $pf = IO::File->new(">$passfilename");
1.31 www 496: print $pf "internal:$ncpass\n"; }
1.1 albertel 497: print $client "ok\n";
1.2 www 498: } else {
499: print $client "non_authorized\n";
500: }
1.1 albertel 501: } else {
1.2 www 502: print $client "auth_mode_error\n";
1.1 albertel 503: }
504: } else {
505: print $client "unknown_user\n";
1.31 www 506: }
507: } else {
508: print $client "refused\n";
509: }
510: # -------------------------------------------------------------------- makeuser
511: } elsif ($userinput =~ /^makeuser/) {
512: if ($wasenc==1) {
513: my
514: ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
515: chomp($npass);
1.32 www 516: $npass=&unescape($npass);
1.31 www 517: my $proname=propath($udom,$uname);
518: my $passfilename="$proname/passwd";
519: if (-e $passfilename) {
520: print $client "already_exists\n";
521: } elsif ($udom ne $perlvar{'lonDefDomain'}) {
522: print $client "not_right_domain\n";
523: } else {
524: @fpparts=split(/\//,$proname);
525: $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2];
526: $fperror='';
527: for ($i=3;$i<=$#fpparts;$i++) {
528: $fpnow.='/'.$fpparts[$i];
529: unless (-e $fpnow) {
530: unless (mkdir($fpnow,0777)) {
531: $fperror="error:$!\n";
532: }
533: }
534: }
535: unless ($fperror) {
1.34 ! www 536: if ($umode eq 'krb4') {
1.31 www 537: {
538: my $pf = IO::File->new(">$passfilename");
1.33 www 539: print $pf "krb4:$npass\n";
1.31 www 540: }
541: print $client "ok\n";
542: } elsif ($umode eq 'internal') {
543: my $salt=time;
544: $salt=substr($salt,6,2);
545: my $ncpass=crypt($npass,$salt);
546: {
547: my $pf = IO::File->new(">$passfilename");
548: print $pf "internal:$ncpass\n";
549: }
550: print $client "ok\n";
551: } elsif ($umode eq 'none') {
552: {
553: my $pf = IO::File->new(">$passfilename");
554: print $pf "none:\n";
555: }
556: print $client "ok\n";
557: } else {
558: print $client "auth_mode_error\n";
559: }
560: } else {
561: print $client "$fperror\n";
562: }
1.1 albertel 563: }
564: } else {
565: print $client "refused\n";
566: }
567: # ------------------------------------------------------------------------ home
568: } elsif ($userinput =~ /^home/) {
569: my ($cmd,$udom,$uname)=split(/:/,$userinput);
570: chomp($uname);
571: my $proname=propath($udom,$uname);
572: if (-e $proname) {
573: print $client "found\n";
574: } else {
575: print $client "not_found\n";
576: }
577: # ---------------------------------------------------------------------- update
578: } elsif ($userinput =~ /^update/) {
579: my ($cmd,$fname)=split(/:/,$userinput);
580: my $ownership=ishome($fname);
581: if ($ownership eq 'not_owner') {
582: if (-e $fname) {
583: my ($dev,$ino,$mode,$nlink,
584: $uid,$gid,$rdev,$size,
585: $atime,$mtime,$ctime,
586: $blksize,$blocks)=stat($fname);
587: $now=time;
588: $since=$now-$atime;
589: if ($since>$perlvar{'lonExpire'}) {
590: $reply=
591: reply("unsub:$fname","$hostid{$clientip}");
592: unlink("$fname");
593: } else {
594: my $transname="$fname.in.transfer";
595: my $remoteurl=
596: reply("sub:$fname","$hostid{$clientip}");
597: my $response;
598: {
599: my $ua=new LWP::UserAgent;
600: my $request=new HTTP::Request('GET',"$remoteurl");
601: $response=$ua->request($request,$transname);
602: }
603: if ($response->is_error()) {
1.24 albertel 604: unlink($transname);
1.1 albertel 605: my $message=$response->status_line;
606: &logthis(
607: "LWP GET: $message for $fname ($remoteurl)");
608: } else {
1.14 www 609: if ($remoteurl!~/\.meta$/) {
1.28 www 610: my $ua=new LWP::UserAgent;
1.14 www 611: my $mrequest=
612: new HTTP::Request('GET',$remoteurl.'.meta');
613: my $mresponse=
614: $ua->request($mrequest,$fname.'.meta');
615: if ($mresponse->is_error()) {
616: unlink($fname.'.meta');
617: }
618: }
1.1 albertel 619: rename($transname,$fname);
620: }
621: }
622: print $client "ok\n";
623: } else {
624: print $client "not_found\n";
625: }
626: } else {
627: print $client "rejected\n";
628: }
629: # ----------------------------------------------------------------- unsubscribe
630: } elsif ($userinput =~ /^unsub/) {
631: my ($cmd,$fname)=split(/:/,$userinput);
632: if (-e $fname) {
633: if (unlink("$fname.$hostid{$clientip}")) {
634: print $client "ok\n";
635: } else {
636: print $client "not_subscribed\n";
637: }
638: } else {
639: print $client "not_found\n";
640: }
641: # ------------------------------------------------------------------- subscribe
642: } elsif ($userinput =~ /^sub/) {
643: my ($cmd,$fname)=split(/:/,$userinput);
644: my $ownership=ishome($fname);
645: if ($ownership eq 'owner') {
646: if (-e $fname) {
1.18 www 647: if (-d $fname) {
648: print $client "directory\n";
649: } else {
1.1 albertel 650: $now=time;
651: {
1.26 www 652: my $sh;
1.25 www 653: if ($sh=
654: IO::File->new(">$fname.$hostid{$clientip}")) {
655: print $sh "$clientip:$now\n";
656: }
1.1 albertel 657: }
658: $fname=~s/\/home\/httpd\/html\/res/raw/;
659: $fname="http://$thisserver/".$fname;
660: print $client "$fname\n";
1.18 www 661: }
1.1 albertel 662: } else {
663: print $client "not_found\n";
664: }
665: } else {
666: print $client "rejected\n";
667: }
1.12 harris41 668: # ------------------------------------------------------------------------- log
669: } elsif ($userinput =~ /^log/) {
670: my ($cmd,$udom,$uname,$what)=split(/:/,$userinput);
671: chomp($what);
672: my $proname=propath($udom,$uname);
673: my $now=time;
674: {
675: my $hfh;
676: if ($hfh=IO::File->new(">>$proname/activity.log")) {
677: print $hfh "$now:$hostid{$clientip}:$what\n";
678: print $client "ok\n";
679: } else {
680: print $client "error:$!\n";
681: }
682: }
1.1 albertel 683: # ------------------------------------------------------------------------- put
684: } elsif ($userinput =~ /^put/) {
1.6 www 685: my ($cmd,$udom,$uname,$namespace,$what)
1.1 albertel 686: =split(/:/,$userinput);
1.8 www 687: $namespace=~s/\//\_/g;
1.6 www 688: $namespace=~s/\W//g;
689: if ($namespace ne 'roles') {
1.1 albertel 690: chomp($what);
691: my $proname=propath($udom,$uname);
692: my $now=time;
693: {
694: my $hfh;
695: if (
696: $hfh=IO::File->new(">>$proname/$namespace.hist")
697: ) { print $hfh "P:$now:$what\n"; }
698: }
699: my @pairs=split(/\&/,$what);
1.4 www 700: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
1.1 albertel 701: foreach $pair (@pairs) {
702: ($key,$value)=split(/=/,$pair);
703: $hash{$key}=$value;
704: }
1.4 www 705: if (untie(%hash)) {
1.1 albertel 706: print $client "ok\n";
707: } else {
708: print $client "error:$!\n";
709: }
710: } else {
711: print $client "error:$!\n";
712: }
1.6 www 713: } else {
714: print $client "refused\n";
715: }
716: # -------------------------------------------------------------------- rolesput
717: } elsif ($userinput =~ /^rolesput/) {
718: if ($wasenc==1) {
719: my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
720: =split(/:/,$userinput);
721: my $namespace='roles';
722: chomp($what);
723: my $proname=propath($udom,$uname);
724: my $now=time;
725: {
726: my $hfh;
727: if (
728: $hfh=IO::File->new(">>$proname/$namespace.hist")
729: ) {
730: print $hfh "P:$now:$exedom:$exeuser:$what\n";
731: }
732: }
733: my @pairs=split(/\&/,$what);
734: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
735: foreach $pair (@pairs) {
736: ($key,$value)=split(/=/,$pair);
737: $hash{$key}=$value;
738: }
739: if (untie(%hash)) {
740: print $client "ok\n";
741: } else {
742: print $client "error:$!\n";
743: }
744: } else {
745: print $client "error:$!\n";
746: }
747: } else {
748: print $client "refused\n";
749: }
1.1 albertel 750: # ------------------------------------------------------------------------- get
751: } elsif ($userinput =~ /^get/) {
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 @queries=split(/\&/,$what);
758: my $proname=propath($udom,$uname);
759: my $qresult='';
1.20 www 760: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 761: for ($i=0;$i<=$#queries;$i++) {
762: $qresult.="$hash{$queries[$i]}&";
763: }
1.4 www 764: if (untie(%hash)) {
1.1 albertel 765: $qresult=~s/\&$//;
766: print $client "$qresult\n";
767: } else {
768: print $client "error:$!\n";
769: }
770: } else {
771: print $client "error:$!\n";
772: }
773: # ------------------------------------------------------------------------ eget
774: } elsif ($userinput =~ /^eget/) {
775: my ($cmd,$udom,$uname,$namespace,$what)
776: =split(/:/,$userinput);
1.8 www 777: $namespace=~s/\//\_/g;
1.1 albertel 778: $namespace=~s/\W//g;
779: chomp($what);
780: my @queries=split(/\&/,$what);
781: my $proname=propath($udom,$uname);
782: my $qresult='';
1.20 www 783: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 784: for ($i=0;$i<=$#queries;$i++) {
785: $qresult.="$hash{$queries[$i]}&";
786: }
1.4 www 787: if (untie(%hash)) {
1.1 albertel 788: $qresult=~s/\&$//;
789: if ($cipher) {
790: my $cmdlength=length($qresult);
791: $qresult.=" ";
792: my $encqresult='';
793: for
794: (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
795: $encqresult.=
796: unpack("H16",
797: $cipher->encrypt(substr($qresult,$encidx,8)));
798: }
799: print $client "enc:$cmdlength:$encqresult\n";
800: } else {
801: print $client "error:no_key\n";
802: }
803: } else {
804: print $client "error:$!\n";
805: }
806: } else {
807: print $client "error:$!\n";
808: }
809: # ------------------------------------------------------------------------- del
810: } elsif ($userinput =~ /^del/) {
811: my ($cmd,$udom,$uname,$namespace,$what)
812: =split(/:/,$userinput);
1.8 www 813: $namespace=~s/\//\_/g;
1.1 albertel 814: $namespace=~s/\W//g;
815: chomp($what);
816: my $proname=propath($udom,$uname);
817: my $now=time;
818: {
819: my $hfh;
820: if (
821: $hfh=IO::File->new(">>$proname/$namespace.hist")
822: ) { print $hfh "D:$now:$what\n"; }
823: }
824: my @keys=split(/\&/,$what);
1.4 www 825: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
1.1 albertel 826: foreach $key (@keys) {
827: delete($hash{$key});
828: }
1.4 www 829: if (untie(%hash)) {
1.1 albertel 830: print $client "ok\n";
831: } else {
832: print $client "error:$!\n";
833: }
834: } else {
835: print $client "error:$!\n";
836: }
837: # ------------------------------------------------------------------------ keys
838: } elsif ($userinput =~ /^keys/) {
839: my ($cmd,$udom,$uname,$namespace)
840: =split(/:/,$userinput);
1.8 www 841: $namespace=~s/\//\_/g;
1.1 albertel 842: $namespace=~s/\W//g;
843: my $proname=propath($udom,$uname);
844: my $qresult='';
1.20 www 845: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 846: foreach $key (keys %hash) {
847: $qresult.="$key&";
848: }
1.4 www 849: if (untie(%hash)) {
1.1 albertel 850: $qresult=~s/\&$//;
851: print $client "$qresult\n";
852: } else {
853: print $client "error:$!\n";
854: }
855: } else {
856: print $client "error:$!\n";
857: }
858: # ------------------------------------------------------------------------ dump
859: } elsif ($userinput =~ /^dump/) {
860: my ($cmd,$udom,$uname,$namespace)
861: =split(/:/,$userinput);
1.8 www 862: $namespace=~s/\//\_/g;
1.1 albertel 863: $namespace=~s/\W//g;
864: my $proname=propath($udom,$uname);
865: my $qresult='';
1.20 www 866: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.1 albertel 867: foreach $key (keys %hash) {
868: $qresult.="$key=$hash{$key}&";
1.7 www 869: }
870: if (untie(%hash)) {
871: $qresult=~s/\&$//;
872: print $client "$qresult\n";
873: } else {
874: print $client "error:$!\n";
875: }
876: } else {
877: print $client "error:$!\n";
878: }
879: # ----------------------------------------------------------------------- store
880: } elsif ($userinput =~ /^store/) {
881: my ($cmd,$udom,$uname,$namespace,$rid,$what)
882: =split(/:/,$userinput);
1.8 www 883: $namespace=~s/\//\_/g;
1.7 www 884: $namespace=~s/\W//g;
885: if ($namespace ne 'roles') {
886: chomp($what);
887: my $proname=propath($udom,$uname);
888: my $now=time;
889: {
890: my $hfh;
891: if (
892: $hfh=IO::File->new(">>$proname/$namespace.hist")
893: ) { print $hfh "P:$now:$rid:$what\n"; }
894: }
895: my @pairs=split(/\&/,$what);
896:
897: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
898: my @previouskeys=split(/&/,$hash{"keys:$rid"});
899: my $key;
900: $hash{"version:$rid"}++;
901: my $version=$hash{"version:$rid"};
902: my $allkeys='';
903: foreach $pair (@pairs) {
904: ($key,$value)=split(/=/,$pair);
905: $allkeys.=$key.':';
906: $hash{"$version:$rid:$key"}=$value;
907: }
908: $allkeys=~s/:$//;
909: $hash{"$version:keys:$rid"}=$allkeys;
910: if (untie(%hash)) {
911: print $client "ok\n";
912: } else {
913: print $client "error:$!\n";
914: }
915: } else {
916: print $client "error:$!\n";
917: }
918: } else {
919: print $client "refused\n";
920: }
921: # --------------------------------------------------------------------- restore
922: } elsif ($userinput =~ /^restore/) {
923: my ($cmd,$udom,$uname,$namespace,$rid)
924: =split(/:/,$userinput);
1.8 www 925: $namespace=~s/\//\_/g;
1.7 www 926: $namespace=~s/\W//g;
927: chomp($rid);
928: my $proname=propath($udom,$uname);
929: my $qresult='';
1.20 www 930: if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
1.7 www 931: my $version=$hash{"version:$rid"};
932: $qresult.="version=$version&";
933: my $scope;
934: for ($scope=1;$scope<=$version;$scope++) {
935: my $vkeys=$hash{"$scope:keys:$rid"};
936: my @keys=split(/:/,$vkeys);
937: my $key;
938: $qresult.="$scope:keys=$vkeys&";
939: foreach $key (@keys) {
1.21 www 940: $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&";
1.7 www 941: }
1.1 albertel 942: }
1.4 www 943: if (untie(%hash)) {
1.1 albertel 944: $qresult=~s/\&$//;
945: print $client "$qresult\n";
946: } else {
947: print $client "error:$!\n";
948: }
949: } else {
950: print $client "error:$!\n";
951: }
1.12 harris41 952: # ------------------------------------------------------------------- querysend
953: } elsif ($userinput =~ /^querysend/) {
954: my ($cmd,$query)=split(/:/,$userinput);
955: $query=~s/\n*$//g;
1.13 www 956: print $client sqlreply("$hostid{$clientip}\&$query")."\n";
1.12 harris41 957: # ------------------------------------------------------------------ queryreply
958: } elsif ($userinput =~ /^queryreply/) {
959: my ($cmd,$id,$reply)=split(/:/,$userinput);
960: my $store;
1.13 www 961: my $execdir=$perlvar{'lonDaemons'};
962: if ($store=IO::File->new(">$execdir/tmp/$id")) {
1.12 harris41 963: print $store $reply;
964: close $store;
965: print $client "ok\n";
966: }
967: else {
968: print $client "error:$!\n";
969: }
1.1 albertel 970: # ----------------------------------------------------------------------- idput
971: } elsif ($userinput =~ /^idput/) {
972: my ($cmd,$udom,$what)=split(/:/,$userinput);
973: chomp($what);
974: $udom=~s/\W//g;
975: my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
976: my $now=time;
977: {
978: my $hfh;
979: if (
980: $hfh=IO::File->new(">>$proname.hist")
981: ) { print $hfh "P:$now:$what\n"; }
982: }
983: my @pairs=split(/\&/,$what);
1.4 www 984: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT,0640)) {
1.1 albertel 985: foreach $pair (@pairs) {
986: ($key,$value)=split(/=/,$pair);
987: $hash{$key}=$value;
988: }
1.4 www 989: if (untie(%hash)) {
1.1 albertel 990: print $client "ok\n";
991: } else {
992: print $client "error:$!\n";
993: }
994: } else {
995: print $client "error:$!\n";
996: }
997: # ----------------------------------------------------------------------- idget
998: } elsif ($userinput =~ /^idget/) {
999: my ($cmd,$udom,$what)=split(/:/,$userinput);
1000: chomp($what);
1001: $udom=~s/\W//g;
1002: my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
1003: my @queries=split(/\&/,$what);
1004: my $qresult='';
1.20 www 1005: if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER,0640)) {
1.1 albertel 1006: for ($i=0;$i<=$#queries;$i++) {
1007: $qresult.="$hash{$queries[$i]}&";
1008: }
1.4 www 1009: if (untie(%hash)) {
1.1 albertel 1010: $qresult=~s/\&$//;
1011: print $client "$qresult\n";
1012: } else {
1013: print $client "error:$!\n";
1014: }
1015: } else {
1016: print $client "error:$!\n";
1017: }
1.13 www 1018: # ---------------------------------------------------------------------- tmpput
1019: } elsif ($userinput =~ /^tmpput/) {
1020: my ($cmd,$what)=split(/:/,$userinput);
1021: my $store;
1022: $tmpsnum++;
1023: my $id=$$.'_'.$clientip.'_'.$tmpsnum;
1024: $id=~s/\W/\_/g;
1025: $what=~s/\n//g;
1026: my $execdir=$perlvar{'lonDaemons'};
1027: if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) {
1028: print $store $what;
1029: close $store;
1030: print $client "$id\n";
1031: }
1032: else {
1033: print $client "error:$!\n";
1034: }
1035:
1036: # ---------------------------------------------------------------------- tmpget
1037: } elsif ($userinput =~ /^tmpget/) {
1038: my ($cmd,$id)=split(/:/,$userinput);
1039: chomp($id);
1040: $id=~s/\W/\_/g;
1041: my $store;
1042: my $execdir=$perlvar{'lonDaemons'};
1043: if ($store=IO::File->new("$execdir/tmp/$id.tmp")) {
1044: my $reply=<$store>;
1045: print $client "$reply\n";
1046: close $store;
1047: }
1048: else {
1049: print $client "error:$!\n";
1050: }
1051:
1.5 www 1052: # -------------------------------------------------------------------------- ls
1053: } elsif ($userinput =~ /^ls/) {
1054: my ($cmd,$ulsdir)=split(/:/,$userinput);
1055: my $ulsout='';
1056: my $ulsfn;
1057: if (-e $ulsdir) {
1058: while ($ulsfn=<$ulsdir/*>) {
1059: my @ulsstats=stat($ulsfn);
1060: $ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
1061: }
1062: } else {
1063: $ulsout='no_such_dir';
1064: }
1.17 www 1065: if ($ulsout eq '') { $ulsout='empty'; }
1.5 www 1066: print $client "$ulsout\n";
1.1 albertel 1067: # ------------------------------------------------------------- unknown command
1068: } else {
1069: # unknown command
1070: print $client "unknown_cmd\n";
1071: }
1072: # ------------------------------------------------------ client unknown, refuse
1073: }
1074: } else {
1075: print $client "refused\n";
1.9 www 1076: &logthis("<font color=blue>WARNING: "
1077: ."Rejected client $clientip, closing connection</font>");
1.1 albertel 1078: }
1.9 www 1079: &logthis("<font color=red>CRITICAL: "
1.10 www 1080: ."Disconnect from $clientip ($hostid{$clientip})</font>");
1.1 albertel 1081: # =============================================================================
1082: }
1083:
1084: # tidy up gracefully and finish
1085:
1086: # this exit is VERY important, otherwise the child will become
1087: # a producer of more and more children, forking yourself into
1088: # process death.
1089: exit;
1090: }
1091: }
1092:
1093:
1094:
1095:
1096:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>