#!/usr/local/bin/perl # rpc.cgi # Handles remote_foreign_require and remote_foreign_call requests from # other webmin servers. State is preserved by starting a process for each # session that listens for requests on a named pipe (and dies after a few # seconds of inactivity) # access{'rpc'} 0=not allowed 1=allowed 2=allowed if root or admin BEGIN { push(@INC, ".."); }; use WebminCore; use POSIX; &init_config(); if ($ENV{'REQUEST_METHOD'} eq 'POST') { local $got; local $left = $ENV{'CONTENT_LENGTH'} - length($rawarg); while($left > 0) { read(STDIN, $got, $left) > 0 || last; $rawarg .= $got; $left = $ENV{'CONTENT_LENGTH'} - length($rawarg); } } else { $rawarg = $ENV{'QUERY_STRING'}; } $arg = &unserialise_variable($rawarg); $| = 1; print "Content-type: text/plain\n\n"; # Can this user make remote calls? %access = &get_module_acl(); if ($access{'rpc'} == 0 || $access{'rpc'} == 2 && $base_remote_user ne 'admin' && $base_remote_user ne 'root' && $base_remote_user ne 'sysadm') { print &serialise_variable( { 'status' => 0 } ); exit; } if ($arg->{'newsession'}) { # Need to fork a new session-handler process $fifo1 = &tempname(); $fifo2 = &tempname(); mkfifo($fifo1, 0700); mkfifo($fifo2, 0700); if (!fork()) { # This is the subprocess where execution really happens $SIG{'ALRM'} = "fifo_timeout"; untie(*STDIN); untie(*STDOUT); close(STDIN); close(STDOUT); close(miniserv::SOCK); local $stime = time(); local $rcount = 0; while(1) { local ($rawcmd, $cmd, @rv); alarm($rcount ? 360 : 60); open(FIFO, $fifo1) || last; while() { $rawcmd .= $_; } close(FIFO); alarm(0); $cmd = &unserialise_variable($rawcmd); if ($cmd->{'action'} eq 'quit') { # time to end this session (after the reply) @rv = ( { 'time' => time() - $stime } ); } elsif ($cmd->{'action'} eq 'require') { # require a library &foreign_require($cmd->{'module'}, $cmd->{'file'}); @rv = ( { 'session' => [ $fifo1, $fifo2 ] } ); } elsif ($cmd->{'action'} eq 'call') { # execute a function @rv = &foreign_call($cmd->{'module'}, $cmd->{'func'}, @{$cmd->{'args'}}); } elsif ($cmd->{'action'} eq 'eval') { # eval some perl code if ($cmd->{'module'}) { @rv = eval <{'module'}; $cmd->{'code'} EOF } else { @rv = eval $cmd->{'code'}; } } open(FIFO, ">$fifo2"); if (@rv == 1) { print FIFO &serialise_variable( { 'status' => 1, 'rv' => $rv[0] } ); } else { print FIFO &serialise_variable( { 'status' => 1, 'arv' => \@rv } ); } close(FIFO); last if ($cmd->{'action'} eq 'quit'); $rcount++; } unlink($fifo1); unlink($fifo2); exit; } $session = [ $fifo1, $fifo2 ]; } else { # Use the provided session id $session = $arg->{'session'}; } if ($arg->{'action'} eq 'ping') { # Just respond with an OK print &serialise_variable( { 'status' => 1 } ); } elsif ($arg->{'action'} eq 'check') { # Check if some module is supported print &serialise_variable( { 'status' => 1, 'rv' => &foreign_check($arg->{'module'}, undef, undef, $arg->{'api'}) } ); } elsif ($arg->{'action'} eq 'config') { # Get the config for some module local %config = &foreign_config($arg->{'module'}); print &serialise_variable( { 'status' => 1, 'rv' => \%config } ); } elsif ($arg->{'action'} eq 'write') { # Transfer data to a local temp file local $file = $arg->{'file'} ? $arg->{'file'} : $arg->{'name'} ? &tempname($arg->{'name'}) : &tempname(); open(FILE, ">$file"); print FILE $arg->{'data'}; close(FILE); print &serialise_variable( { 'status' => 1, 'rv' => $file } ); } elsif ($arg->{'action'} eq 'read') { # Transfer data from a file local ($data, $got); open(FILE, $arg->{'file'}); while(read(FILE, $got, 1024) > 0) { $data .= $got; } close(FILE); print &serialise_variable( { 'status' => 1, 'rv' => $data } ); } else { # Pass the request on to the subprocess open(FIFO, ">$session->[0]"); print FIFO $rawarg; close(FIFO); open(FIFO, $session->[1]); while() { print; } close(FIFO); } sub fifo_timeout { unlink($fifo1); unlink($fifo2); exit; }