X-Git-Url: https://git.proxmox.com/?p=pve-access-control.git;a=blobdiff_plain;f=PVE%2FRPCEnvironment.pm;h=f65a776134b271a8d6b0c8e3fd5c1e8280997280;hp=e6393578a7438ca1e7e44b709c02491d99f0bc21;hb=be6ea72391199dda073615881ddda229bc94ff92;hpb=d33d0735a9a49949d6e765143070e875165dd99d diff --git a/PVE/RPCEnvironment.pm b/PVE/RPCEnvironment.pm index e639357..f65a776 100644 --- a/PVE/RPCEnvironment.pm +++ b/PVE/RPCEnvironment.pm @@ -2,8 +2,10 @@ package PVE::RPCEnvironment; use strict; use warnings; -use POSIX ":sys_wait_h"; +use POSIX qw(:sys_wait_h EINTR); +use IO::Handle; use IO::File; +use IO::Select; use Fcntl qw(:flock); use PVE::SafeSyslog; use PVE::Tools; @@ -11,6 +13,7 @@ use PVE::INotify; use PVE::Cluster; use PVE::ProcFSTools; use PVE::AccessControl; +use CGI; # we use this singleton class to pass RPC related environment values @@ -177,7 +180,7 @@ sub init { die "already initialized" if $pve_env; - die "unknown environment type" if !$type || $type !~ m/^(cli|pub|priv)$/; + die "unknown environment type" if !$type || $type !~ m/^(cli|pub|priv|ha)$/; $SIG{CHLD} = $worker_reaper; @@ -185,6 +188,7 @@ sub init { # cli ... command started fron command line # pub ... access from public server (apache) # priv ... access from private server (pvedaemon) + # ha ... access from HA resource manager agent (rgmanager) my $self = { user_cfg => {}, @@ -222,16 +226,68 @@ sub get { return $pve_env; } +sub parse_params { + my ($self, $enable_upload) = @_; + + if ($self->{request_rec}) { + my $cgi; + if ($enable_upload) { + $cgi = CGI->new($self->{request_rec}); + } else { + # disable upload using empty upload_hook + $cgi = CGI->new($self->{request_rec}, sub {}, undef, 0); + } + $self->{cgi} = $cgi; + my $params = $cgi->Vars(); + return $params; + } elsif ($self->{params}) { + return $self->{params}; + } else { + die "no parameters registered"; + } +} + +sub get_upload_info { + my ($self, $param) = @_; + + my $cgi = $self->{cgi}; + die "CGI not initialized" if !$cgi; + + my $pd = $cgi->param($param); + die "unable to get cgi parameter info\n" if !$pd; + my $info = $cgi->uploadInfo($pd); + die "unable to get cgi upload info\n" if !$info; + + my $res = { %$info }; + + my $tmpfilename = $cgi->tmpFileName($pd); + die "unable to get cgi upload file name\n" if !$tmpfilename; + $res->{tmpfilename} = $tmpfilename; + + #my $hndl = $cgi->upload($param); + #die "unable to get cgi upload handle\n" if !$hndl; + #$res->{handle} = $hndl->handle; + + return $res; +} + # init_request - must be called before each RPC request sub init_request { my ($self, %params) = @_; PVE::Cluster::cfs_update(); + $self->{result_attributes} = {}; + my $userconfig; # we use this for regression tests foreach my $p (keys %params) { if ($p eq 'userconfig') { $userconfig = $params{$p}; + } elsif ($p eq 'request_rec') { + # pass Apache2::RequestRec + $self->{request_rec} = $params{$p}; + } elsif ($p eq 'params') { + $self->{params} = $params{$p}; } else { die "unknown parameter '$p'"; } @@ -271,16 +327,16 @@ sub get_client_ip { return $self->{client_ip}; } -sub set_result_count { - my ($self, $count) = @_; +sub set_result_attrib { + my ($self, $key, $value) = @_; - $self->{result_count} = $count; + $self->{result_attributes}->{$key} = $value; } -sub get_result_count { - my ($self) = @_; +sub get_result_attrib { + my ($self, $key) = @_; - return $self->{result_count}; + return $self->{result_attributes}->{$key}; } sub set_language { @@ -531,13 +587,17 @@ sub fork_worker { # same algorythm as used inside SA # STDIN = /dev/null my $fd = fileno (STDIN); - close STDIN; - POSIX::close(0) if $fd != 0; - die "unable to redirect STDIN - $!" - if !open(STDIN, "{type} eq 'ha') { + print "task started by HA resource agent\n"; + } eval { &$function($upid); }; my $err = $@; if ($err) { @@ -649,28 +712,53 @@ sub fork_worker { if ($sync) { my $count; my $outbuf = ''; + my $int_count = 0; eval { - local $SIG{INT} = - local $SIG{QUIT} = - local $SIG{TERM} = sub { die "got interrupt\n"; }; + local $SIG{INT} = local $SIG{QUIT} = local $SIG{TERM} = sub { + # always send signal to all pgrp members + my $kpid = -$cpid; + if ($int_count < 3) { + kill(15, $kpid); # send TERM signal + } else { + kill(9, $kpid); # send KILL signal + } + $int_count++; + }; local $SIG{PIPE} = sub { die "broken pipe\n"; }; - - while (($count = POSIX::read($psync[0], $readbuf, 4096)) && ($count > 0)) { - $outbuf .= $readbuf; - while ($outbuf =~ s/^(([^\010\r\n]*)(\r|\n|(\010)+|\r\n))//s) { - my $line = $1; - my $data = $2; - if ($data =~ m/^TASK OK$/) { - # skip - } elsif ($data =~ m/^TASK ERROR: (.+)$/) { - print STDERR "$1\n"; - } else { - print $line; + + my $select = new IO::Select; + my $fh = IO::Handle->new_from_fd($psync[0], 'r'); + $select->add($fh); + + while ($select->count) { + my @handles = $select->can_read(1); + if (scalar(@handles)) { + my $count = sysread ($handles[0], $readbuf, 4096); + if (!defined ($count)) { + my $err = $!; + die "sync pipe read error: $err\n"; } - if ($outfh) { - print $outfh $line; - $outfh->flush(); + last if $count == 0; # eof + + $outbuf .= $readbuf; + while ($outbuf =~ s/^(([^\010\r\n]*)(\r|\n|(\010)+|\r\n))//s) { + my $line = $1; + my $data = $2; + if ($data =~ m/^TASK OK$/) { + # skip + } elsif ($data =~ m/^TASK ERROR: (.+)$/) { + print STDERR "$1\n"; + } else { + print $line; + } + if ($outfh) { + print $outfh $line; + $outfh->flush(); + } } + } else { + # some commands daemonize without closing stdout + last if !PVE::ProcFSTools::check_process_running($cpid); } } };