Fixed eval of in modules with -
[webmin.git] / fastrpc.cgi
1 #!/usr/local/bin/perl
2 # Handles remote_* function calls by a faster method. When first called
3 # as a CGI, forks and starts listening on a port which is returned to the
4 # client. From then on, direct TCP connections can be made to this port
5 # to send requests and get replies.
6
7 do './web-lib.pl';
8 use POSIX;
9 use Socket;
10 $force_lang = $default_lang;
11 &init_config();
12 print "Content-type: text/plain\n\n";
13
14 # Can this user make remote calls?
15 %access = &get_module_acl();
16 if ($access{'rpc'} == 0 || $access{'rpc'} == 2 &&
17     $base_remote_user ne 'admin' && $base_remote_user ne 'root' &&
18     $base_remote_user ne 'sysadm') {
19         print "0 Invalid user for RPC\n";
20         exit;
21         }
22
23 # Find a free port
24 &get_miniserv_config(\%miniserv);
25 $port = $miniserv{'port'} || 10000;
26 $aerr = &allocate_socket(MAIN, \$port);
27 if ($aerr) {
28         print "0 $aerr\n";
29         exit;
30         }
31 if (open(RANDOM, "/dev/urandom")) {
32         local $tmpsid;
33         read(RANDOM, $tmpsid, 16);
34         $sid = lc(unpack('h*', $tmpsid));
35         close RANDOM;
36         }
37 else {
38         $sid = time()*$$;
39         }
40 $version = &get_webmin_version();
41 print "1 $port $sid $version\n";
42
43 # Fork and listen for calls ..
44 $pid = fork();
45 if ($pid < 0) {
46         die "fork() failed : $!";
47         }
48 elsif ($pid) {
49         exit;
50         }
51 untie(*STDIN);
52 untie(*STDOUT);
53
54 # Accept the TCP connection
55 $acptaddr = accept(SOCK, MAIN);
56 die "accept failed!" if (!$acptaddr);
57 select(SOCK); $| = 1;
58
59 while(1) {
60         # Wait for the request
61         local $rmask;
62         vec($rmask, fileno(SOCK), 1) = 1;
63         local $sel = select($rmask, undef, undef, 60);
64         last if ($sel <= 0);
65
66         local $line = <SOCK>;
67         last if (!$line);
68         local ($len, $auth) = split(/\s+/, $line);
69         die "Invalid session ID" if ($auth ne $sid);
70         local $rawarg;
71         while(length($rawarg) < $len) {
72                 local $got;
73                 local $rv = read(SOCK, $got, $len - length($rawarg));
74                 exit if ($rv <= 0);
75                 $rawarg .= $got;
76                 }
77         print STDERR "fastrpc: raw $rawarg\n" if ($gconfig{'rpcdebug'});
78         local $arg = &unserialise_variable($rawarg);
79
80         # Process it
81         local $rawrv;
82         if ($arg->{'action'} eq 'ping') {
83                 # Just respond with an OK
84                 print STDERR "fastrpc: ping\n" if ($gconfig{'rpcdebug'});
85                 $rawrv = &serialise_variable( { 'status' => 1 } );
86                 }
87         elsif ($arg->{'action'} eq 'check') {
88                 # Check if some module is supported
89                 print STDERR "fastrpc: check $arg->{'module'}\n" if ($gconfig{'rpcdebug'});
90                 $rawrv = &serialise_variable(
91                         { 'status' => 1,
92                           'rv' => &foreign_check($arg->{'module'}) } );
93                 }
94         elsif ($arg->{'action'} eq 'config') {
95                 # Get the config for some module
96                 print STDERR "fastrpc: config $arg->{'module'}\n" if ($gconfig{'rpcdebug'});
97                 local %config = &foreign_config($arg->{'module'});
98                 $rawrv = &serialise_variable(
99                         { 'status' => 1, 'rv' => \%config } );
100                 }
101         elsif ($arg->{'action'} eq 'write') {
102                 # Transfer data to a local temp file
103                 local $file = $arg->{'file'} ? $arg->{'file'} :
104                               $arg->{'name'} ? &tempname($arg->{'name'}) :
105                                                &tempname();
106                 print STDERR "fastrpc: write $file\n" if ($gconfig{'rpcdebug'});
107                 open(FILE, ">$file");
108                 binmode(FILE);
109                 print FILE $arg->{'data'};
110                 close(FILE);
111                 $rawrv = &serialise_variable(
112                         { 'status' => 1, 'rv' => $file } );
113                 }
114         elsif ($arg->{'action'} eq 'tcpwrite') {
115                 # Transfer data to a local temp file over TCP connection
116                 local $file = $arg->{'file'} ? $arg->{'file'} :
117                               $arg->{'name'} ? &tempname($arg->{'name'}) :
118                                                &tempname();
119                 print STDERR "fastrpc: tcpwrite $file\n" if ($gconfig{'rpcdebug'});
120                 local $tsock = time().$$;
121                 local $tport = $port + 1;
122                 &allocate_socket($tsock, \$tport);
123                 if (!fork()) {
124                         # Accept connection in separate process
125                         print STDERR "fastrpc: tcpwrite $file port $tport\n" if ($gconfig{'rpcdebug'});
126                         local $rmask;
127                         vec($rmask, fileno($tsock), 1) = 1;
128                         local $sel = select($rmask, undef, undef, 30);
129                         exit if ($sel <= 0);
130                         accept(TRANS, $tsock) || exit;
131                         print STDERR "fastrpc: tcpwrite $file accepted\n" if ($gconfig{'rpcdebug'});
132                         local $buf;
133                         local $err;
134                         if (open(FILE, ">$file")) {
135                                 binmode(FILE);
136                                 print STDERR "fastrpc: tcpwrite $file writing\n" if ($gconfig{'rpcdebug'});
137                                 while(read(TRANS, $buf, 1024) > 0) {
138                                         local $ok = (print FILE $buf);
139                                         if (!$ok) {
140                                                 $err = "Write to $file failed : $!";
141                                                 last;
142                                                 }
143                                         }
144                                 close(FILE);
145                                 print STDERR "fastrpc: tcpwrite $file written\n" if ($gconfig{'rpcdebug'});
146                                 }
147                         else {
148                                 print STDERR "fastrpc: tcpwrite $file open failed $!\n" if ($gconfig{'rpcdebug'});
149                                 $err = "Failed to open $file : $!";
150                                 }
151                         print TRANS $err ? "$err\n" : "OK\n";
152                         close(TRANS);
153                         exit;
154                         }
155                 close($tsock);
156                 print STDERR "fastrpc: tcpwrite $file done\n" if ($gconfig{'rpcdebug'});
157                 $rawrv = &serialise_variable(
158                         { 'status' => 1, 'rv' => [ $file, $tport ] } );
159                 }
160         elsif ($arg->{'action'} eq 'read') {
161                 # Transfer data from a file
162                 print STDERR "fastrpc: read $arg->{'file'}\n" if ($gconfig{'rpcdebug'});
163                 local ($data, $got);
164                 open(FILE, $arg->{'file'});
165                 binmode(FILE);
166                 while(read(FILE, $got, 1024) > 0) {
167                         $data .= $got;
168                         }
169                 close(FILE);
170                 $rawrv = &serialise_variable(
171                         { 'status' => 1, 'rv' => $data } );
172                 }
173         elsif ($arg->{'action'} eq 'tcpread') {
174                 # Transfer data from a file over TCP connection
175                 print STDERR "fastrpc: tcpread $arg->{'file'}\n" if ($gconfig{'rpcdebug'});
176                 if (!open(FILE, $arg->{'file'})) {
177                         $rawrv = &serialise_variable(
178                                 { 'status' => 1, 'rv' => [ undef, "Failed to open $arg->{'file'} : $!" ] } );
179                         }
180                 else {
181                         binmode(FILE);
182                         local $tsock = time().$$;
183                         local $tport = $port + 1;
184                         &allocate_socket($tsock, \$tport);
185                         if (!fork()) {
186                                 # Accept connection in separate process
187                                 local $rmask;
188                                 vec($rmask, fileno($tsock), 1) = 1;
189                                 local $sel = select($rmask, undef, undef, 30);
190                                 exit if ($sel <= 0);
191                                 accept(TRANS, $tsock) || exit;
192                                 local $buf;
193                                 while(read(FILE, $buf, 1024) > 0) {
194                                         print TRANS $buf;
195                                         }
196                                 close(FILE);
197                                 close(TRANS);
198                                 exit;
199                                 }
200                         close(FILE);
201                         close($tsock);
202                         print STDERR "fastrpc: tcpread $arg->{'file'} done\n" if ($gconfig{'rpcdebug'});
203                         $rawrv = &serialise_variable(
204                                 { 'status' => 1, 'rv' => [ $arg->{'file'}, $tport ] } );
205                         }
206                 }
207         elsif ($arg->{'action'} eq 'require') {
208                 # require a library
209                 print STDERR "fastrpc: require $arg->{'module'}/$arg->{'file'}\n" if ($gconfig{'rpcdebug'});
210                 &foreign_require($arg->{'module'},
211                                  $arg->{'file'});
212                 $rawrv = &serialise_variable( { 'status' => 1 });
213                 }
214         elsif ($arg->{'action'} eq 'call') {
215                 # execute a function
216                 print STDERR "fastrpc: call $arg->{'module'}::$arg->{'func'}(",join(",", @{$arg->{'args'}}),")\n" if ($gconfig{'rpcdebug'});
217                 local @rv = &foreign_call($arg->{'module'},
218                                     $arg->{'func'},
219                                     @{$arg->{'args'}});
220                 if (@rv == 1) {
221                         $rawrv = &serialise_variable(
222                                 { 'status' => 1, 'rv' => $rv[0] } );
223                         }
224                 else {
225                         $rawrv = &serialise_variable(
226                                 { 'status' => 1, 'arv' => \@rv } );
227                         }
228                 print STDERR "fastrpc: call $arg->{'module'}::$arg->{'func'} done = ",join(",", @rv),"\n" if ($gconfig{'rpcdebug'});
229                 }
230         elsif ($arg->{'action'} eq 'eval') {
231                 # eval some perl code
232                 print STDERR "fastrpc: eval $arg->{'module'} $arg->{'code'}\n" if ($gconfig{'rpcdebug'});
233                 local $rv;
234                 if ($arg->{'module'}) {
235                         local $pkg = $arg->{'module'};
236                         $pkg =~ s/[^A-Za-z0-9]/_/g;
237                         $rv = eval "package $pkg;\n".
238                                    $arg->{'code'}."\n";
239                         }
240                 else {
241                         $rv = eval $arg->{'code'};
242                         }
243                 print STDERR "fastrpc: eval $arg->{'module'} $arg->{'code'} done = $rv error = $@\n" if ($gconfig{'rpcdebug'});
244                 $rawrv = &serialise_variable(
245                         { 'status' => 1, 'rv' => $rv } );
246                 }
247         elsif ($arg->{'action'} eq 'quit') {
248                 print STDERR "fastrpc: quit\n" if ($gconfig{'rpcdebug'});
249                 $rawrv = &serialise_variable( { 'status' => 1 } );
250                 }
251         else {
252                 print STDERR "fastrpc: unknown $arg->{'action'}\n" if ($gconfig{'rpcdebug'});
253                 $rawrv = &serialise_variable( { 'status' => 0 } );
254                 }
255
256         # Send back to the client
257         print SOCK length($rawrv),"\n";
258         print SOCK $rawrv;
259         last if ($arg->{'action'} eq 'quit');
260         }
261
262 # allocate_socket(handle, &port)
263 sub allocate_socket
264 {
265 local ($fh, $port) = @_;
266 local $proto = getprotobyname('tcp');
267 if (!socket($fh, PF_INET, SOCK_STREAM, $proto)) {
268         return "socket failed : $!";
269         }
270 setsockopt($fh, SOL_SOCKET, SO_REUSEADDR, pack("l", 1));
271 while(1) {
272         $$port++;
273         last if (bind($fh, sockaddr_in($$port, INADDR_ANY)));
274         }
275 listen($fh, SOMAXCONN);
276 return undef;
277 }
278