]>
git.proxmox.com Git - pve-manager.git/blob - lib/PVE/APIDaemon.pm
1 package PVE
::APIDaemon
;
8 use PVE
::RPCEnvironment
;
11 use POSIX
":sys_wait_h";
15 use HTTP
::Status
qw(:constants);
17 use Data
::Dumper
; # fixme: remove
21 # This is a quite simple pre-fork server - only listens to local port
23 @ISA = qw(HTTP::Daemon);
25 my $documentroot = "/usr/share/pve-api/root";
29 my $max_workers = 3; # pre-forked worker processes
30 my $max_requests = 500; # max requests per worker
34 my $child_terminate = 0;
35 my $child_reload_config = 0;
40 syslog
('info', "worker $cpid finished");
45 foreach my $cpid (keys %$workers) {
46 my $waitpid = waitpid ($cpid, WNOHANG
);
47 if (defined($waitpid) && ($waitpid == $cpid)) {
48 delete ($workers->{$cpid});
49 worker_finished
($cpid);
55 foreach my $cpid (keys %$workers) {
56 if (!kill(0, $cpid)) {
57 waitpid($cpid, POSIX
::WNOHANG
());
58 delete $workers->{$cpid};
59 worker_finished
($cpid);
65 my ($self, $rpcenv) = @_;
68 foreach my $cpid (keys %$workers) {
72 my $need = $max_workers - $count;
76 syslog
('info', "starting $need worker(s)");
81 if (!defined ($pid)) {
82 syslog
('err', "can't fork worker");
84 } elsif ($pid) { #parent
86 $0 = 'pvedaemon worker';
87 syslog
('info', "worker $pid started");
90 $SIG{TERM
} = $SIG{QUIT
} = sub {
95 $child_reload_config = 1;
100 PVE
::INotify
::inotify_init
();
102 $self->handle_requests($rpcenv);
104 syslog
('err', $@) if $@;
111 sub terminate_server
{
113 syslog
('info', "received terminate request");
115 foreach my $cpid (keys %$workers) {
116 kill (15, $cpid); # TERM childs
119 # nicely shutdown childs (give them max 10 seconds to shut down)
120 my $previous_alarm = alarm (10);
122 local $SIG{ALRM
} = sub { die "Timed Out!\n" };
124 while ((my $pid = waitpid (-1, 0)) > 0) {
125 if (defined($workers->{$pid})) {
126 delete ($workers->{$pid});
127 worker_finished
($pid);
132 alarm ($previous_alarm);
134 foreach my $cpid (keys %$workers) {
135 # KILL childs still alive!
136 if (kill (0, $cpid)) {
137 delete ($workers->{$cpid});
138 syslog
("err", "kill worker $cpid");
148 my $self = $class->SUPER::new
(@_) ||
149 die "unable to create socket - $@\n";
157 my $atfork = sub { close($self); };
158 my $rpcenv = PVE
::RPCEnvironment-
>init('priv', atfork
=> $atfork);
161 my $old_sig_chld = $SIG{CHLD
};
162 local $SIG{CHLD
} = sub {
164 &$old_sig_chld(@_) if $old_sig_chld;
167 my $old_sig_term = $SIG{TERM
};
168 local $SIG{TERM
} = sub {
170 &$old_sig_term(@_) if $old_sig_term;
172 local $SIG{QUIT
} = sub {
174 &$old_sig_term(@_) if $old_sig_term;
177 local $SIG{USR1
} = 'IGNORE';
179 local $SIG{HUP
} = sub {
180 syslog
("info", "received reload request");
181 foreach my $cpid (keys %$workers) {
182 kill (10, $cpid); # SIGUSR1 childs
187 $self->start_workers ($rpcenv);
189 $self->test_workers ();
195 syslog
('err', "ERROR: $err");
200 my ($c, $code, $msg) = @_;
202 $c->send_response(HTTP
::Response-
>new($code, $msg));
205 my $known_methods = {
212 my $extract_params = sub {
213 my ($r, $method) = @_;
215 # NOTE: HTTP::Request::Params return undef instead of ''
216 #my $parser = HTTP::Request::Params->new({req => $r});
217 #my $params = $parser->params;
219 my $post_params = {};
221 if ($method eq 'PUT' || $method eq 'POST') {
222 $post_params = CGI-
>new($r->content())->Vars;
225 my $query_params = CGI-
>new($r->url->query)->Vars;
227 my $params = $post_params || {};
229 foreach my $k (keys %{$query_params}) {
230 $params->{$k} = $query_params->{$k};
236 sub handle_requests
{
237 my ($self, $rpcenv) = @_;
241 my $sel = IO
::Select-
>new();
247 if (scalar (@ready = $sel->can_read($timeout))) {
250 while (($c = $self->accept) || ($! == EINTR
&& !$child_terminate)) {
253 if ($child_reload_config) {
254 $child_reload_config = 0;
255 syslog
('info', "child reload config");
256 # fixme: anything to do here?
261 # fixme: limit max request length somehow
264 while (my $r = $c->get_request) {
266 my $method = $r->method();
268 syslog
('info', "perl method $method");
270 if (!$known_methods->{$method}) {
271 $c->send_error(HTTP_NOT_IMPLEMENTED
);
275 my $uri = $r->uri->path();
276 syslog
('info', "start $method $uri");
278 my ($rel_uri, $format) = PVE
::REST
::split_abs_uri
($uri);
281 $c->send_error(HTTP_NOT_IMPLEMENTED
);
285 my $headers = $r->headers;
287 my $cookie = $headers->header('Cookie');
289 my $ticket = PVE
::REST
::extract_auth_cookie
($cookie);
291 my $params = &$extract_params($r, $method);
293 my $clientip = $headers->header('PVEClientIP');
295 my $res = PVE
::REST
::rest_handler
($clientip, $method, $uri, $rel_uri,
296 $ticket, undef, $params);
300 $res->{status
} = 500;
301 $c->send_error($res->{status
}, "proxy not allowed");
305 PVE
::REST
::prepare_response_data
($format, $res);
306 my ($raw, $ct) = PVE
::REST
::format_response_data
($format, $res, $uri);
308 my $response = HTTP
::Response-
>new($res->{status
}, $res->{message
});
309 $response->header("Content-Type" => $ct);
310 $response->header("Pragma", "no-cache");
312 if ($res->{ticket
}) {
313 my $cookie = PVE
::REST
::create_auth_cookie
($res->{ticket
});
314 $response->header("Set-Cookie" => $cookie);
316 $response->content($raw);
318 $c->send_response($response);
321 syslog
('info', "end $method $uri ($res->{status})");
326 # we only handle one request per connection, because
327 # we want to minimize the number of connections
334 last if $child_terminate || !$c || ($rcount >= $max_requests);
337 last if $child_terminate;
340 PVE
::INotify
::poll
(); # read inotify events