Increase slow RPC timeout
[webmin.git] / rpc.cgi
1 #!/usr/local/bin/perl
2 # rpc.cgi
3 # Handles remote_foreign_require and remote_foreign_call requests from
4 # other webmin servers. State is preserved by starting a process for each
5 # session that listens for requests on a named pipe (and dies after a few
6 # seconds of inactivity)
7 # access{'rpc'}  0=not allowed 1=allowed 2=allowed if root or admin
8
9 BEGIN { push(@INC, ".."); };
10 use WebminCore;
11 use POSIX;
12
13 &init_config();
14 if ($ENV{'REQUEST_METHOD'} eq 'POST') {
15         local $got;
16         local $left = $ENV{'CONTENT_LENGTH'} - length($rawarg);
17         while($left > 0) {
18                 read(STDIN, $got, $left) > 0 || last;
19                 $rawarg .= $got;
20                 }
21         }
22 else {
23         $rawarg = $ENV{'QUERY_STRING'};
24         }
25 $arg = &unserialise_variable($rawarg);
26 $| = 1;
27 print "Content-type: text/plain\n\n";
28
29 # Can this user make remote calls?
30 %access = &get_module_acl();
31 if ($access{'rpc'} == 0 || $access{'rpc'} == 2 &&
32     $base_remote_user ne 'admin' && $base_remote_user ne 'root' &&
33     $base_remote_user ne 'sysadm') {
34         print &serialise_variable( { 'status' => 0 } );
35         exit;
36         }
37
38 if ($arg->{'newsession'}) {
39         # Need to fork a new session-handler process
40         $fifo1 = &tempname();
41         $fifo2 = &tempname();
42         mkfifo($fifo1, 0700);
43         mkfifo($fifo2, 0700);
44         if (!fork()) {
45                 # This is the subprocess where execution really happens
46                 $SIG{'ALRM'} = "fifo_timeout";
47                 untie(*STDIN);
48                 untie(*STDOUT);
49                 close(STDIN);
50                 close(STDOUT);
51                 close(miniserv::SOCK);
52                 local $stime = time();
53                 local $rcount = 0;
54                 while(1) {
55                         local ($rawcmd, $cmd, @rv);
56                         alarm($rcount ? 360 : 60);
57                         open(FIFO, $fifo1) || last;
58                         while(<FIFO>) {
59                                 $rawcmd .= $_;
60                                 }
61                         close(FIFO);
62                         alarm(0);
63                         $cmd = &unserialise_variable($rawcmd);
64                         if ($cmd->{'action'} eq 'quit') {
65                                 # time to end this session (after the reply)
66                                 @rv = ( { 'time' => time() - $stime } );
67                                 }
68                         elsif ($cmd->{'action'} eq 'require') {
69                                 # require a library
70                                 &foreign_require($cmd->{'module'},
71                                                  $cmd->{'file'});
72                                 @rv = ( { 'session' => [ $fifo1, $fifo2 ] } );
73                                 }
74                         elsif ($cmd->{'action'} eq 'call') {
75                                 # execute a function
76                                 @rv = &foreign_call($cmd->{'module'},
77                                                     $cmd->{'func'},
78                                                     @{$cmd->{'args'}});
79                                 }
80                         elsif ($cmd->{'action'} eq 'eval') {
81                                 # eval some perl code
82                                 if ($cmd->{'module'}) {
83                                         @rv = eval <<EOF;
84 package $cmd->{'module'};
85 $cmd->{'code'}
86 EOF
87                                         }
88                                 else {
89                                         @rv = eval $cmd->{'code'};
90                                         }
91                                 }
92                         open(FIFO, ">$fifo2");
93                         if (@rv == 1) {
94                                 print FIFO &serialise_variable(
95                                         { 'status' => 1, 'rv' => $rv[0] } );
96                                 }
97                         else {
98                                 print FIFO &serialise_variable(
99                                         { 'status' => 1, 'arv' => \@rv } );
100                                 }
101                         close(FIFO);
102                         last if ($cmd->{'action'} eq 'quit');
103                         $rcount++;
104                         }
105                 unlink($fifo1);
106                 unlink($fifo2);
107                 exit;
108                 }
109         $session = [ $fifo1, $fifo2 ];
110         }
111 else {
112         # Use the provided session id
113         $session = $arg->{'session'};
114         }
115
116 if ($arg->{'action'} eq 'ping') {
117         # Just respond with an OK
118         print &serialise_variable( { 'status' => 1 } );
119         }
120 elsif ($arg->{'action'} eq 'check') {
121         # Check if some module is supported
122         print &serialise_variable(
123                 { 'status' => 1,
124                   'rv' => &foreign_check($arg->{'module'}, undef, undef,
125                                          $arg->{'api'}) } );
126         }
127 elsif ($arg->{'action'} eq 'config') {
128         # Get the config for some module
129         local %config = &foreign_config($arg->{'module'});
130         print &serialise_variable(
131                 { 'status' => 1, 'rv' => \%config } );
132         }
133 elsif ($arg->{'action'} eq 'write') {
134         # Transfer data to a local temp file
135         local $file = $arg->{'file'} ? $arg->{'file'} :
136                       $arg->{'name'} ? &tempname($arg->{'name'}) :
137                                        &tempname();
138         open(FILE, ">$file");
139         print FILE $arg->{'data'};
140         close(FILE);
141         print &serialise_variable(
142                 { 'status' => 1, 'rv' => $file } );
143         }
144 elsif ($arg->{'action'} eq 'read') {
145         # Transfer data from a file
146         local ($data, $got);
147         open(FILE, $arg->{'file'});
148         while(read(FILE, $got, 1024) > 0) {
149                 $data .= $got;
150                 }
151         close(FILE);
152         print &serialise_variable(
153                 { 'status' => 1, 'rv' => $data } );
154         }
155 else {
156         # Pass the request on to the subprocess
157         open(FIFO, ">$session->[0]");
158         print FIFO $rawarg;
159         close(FIFO);
160         open(FIFO, $session->[1]);
161         while(<FIFO>) {
162                 print;
163                 }
164         close(FIFO);
165         }
166
167 sub fifo_timeout
168 {
169 unlink($fifo1);
170 unlink($fifo2);
171 exit;
172 }
173