]> git.proxmox.com Git - pve-access-control.git/blobdiff - PVE/RPCEnvironment.pm
add support for delayed parameter parsing
[pve-access-control.git] / PVE / RPCEnvironment.pm
index 007c3c4f813769cab75e6844be80302e240519d1..a0c0c3328f2313ce0f73d1e453808ea6b4f968ee 100644 (file)
@@ -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
 
@@ -222,6 +225,51 @@ 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) = @_;
@@ -232,6 +280,11 @@ sub init_request {
     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'";
        }
@@ -316,8 +369,10 @@ sub get_user {
 # read/update list of active workers 
 # we move all finished tasks to the archive index,
 # but keep aktive and most recent task in the active file.
+# $nocheck ... consider $new_upid still running (avoid that
+# we try to read the reult to early.
 sub active_workers  {
-    my ($new_upid) = @_;
+    my ($new_upid, $nocheck) = @_;
 
     my $lkfn = "/var/log/pve/tasks/.active.lock";
 
@@ -332,10 +387,9 @@ sub active_workers  {
        my $thash = {}; # only list task once
 
        my $check_task = sub {
-           my ($task) = @_;
+           my ($task, $running) = @_;
 
-           my $pstart = PVE::ProcFSTools::read_proc_starttime($task->{pid});
-           if ($pstart && ($pstart == $task->{pstart})) {
+           if ($running || PVE::ProcFSTools::check_process_running($task->{pid}, $task->{pstart})) {
                push @$tlist, $task;
            } else {
                delete $task->{pid};
@@ -355,7 +409,7 @@ sub active_workers  {
            $task = PVE::Tools::upid_decode($new_upid);
            $task->{upid} = $new_upid;
            $thash->{$new_upid} = $task;
-           &$check_task($task);
+           &$check_task($task, $nocheck);
        }
 
 
@@ -427,6 +481,42 @@ sub active_workers  {
     return $res;
 }
 
+my $kill_process_group = sub {
+    my ($pid, $pstart) = @_;
+
+    # send kill to process group (negative pid)
+    my $kpid = -$pid;
+
+    # always send signal to all pgrp members
+    kill(15, $kpid); # send TERM signal
+
+    # give max 5 seconds to shut down
+    for (my $i = 0; $i < 5; $i++) {
+       return if !PVE::ProcFSTools::check_process_running($pid, $pstart);
+       sleep (1);
+    }
+       
+    # to be sure
+    kill(9, $kpid); 
+};
+
+sub check_worker {
+    my ($upid, $killit) = @_;
+
+    my $task = PVE::Tools::upid_decode($upid);
+
+    my $running = PVE::ProcFSTools::check_process_running($task->{pid}, $task->{pstart});
+
+    return 0 if !$running;
+
+    if ($killit) {
+       &$kill_process_group($task->{pid});
+       return 0;
+    }
+
+    return 1;
+}
+
 # start long running workers
 # STDIN is redirected to /dev/null
 # STDOUT,STDERR are redirected to the filename returned by upid_decode
@@ -494,13 +584,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, "</dev/null");
+           if (!$sync) {
+               close STDIN;
+               POSIX::close(0) if $fd != 0;
+
+               die "unable to redirect STDIN - $!" 
+                   if !open(STDIN, "</dev/null");
+
+               $outfh = PVE::Tools::upid_open($upid);
+           }
 
-           $outfh = PVE::Tools::upid_open($upid) if !$sync;
 
            # redirect STDOUT
            $fd = fileno(STDOUT);
@@ -527,7 +621,7 @@ sub fork_worker {
            POSIX::write($psync[1], $msg, length ($msg));
            POSIX::close($psync[1]);
            POSIX::_exit(1); 
-           kill('KILL', $$); 
+           kill(-9, $$); 
        }
 
        # sync with parent (signal that we are ready)
@@ -553,9 +647,9 @@ sub fork_worker {
            POSIX::_exit(-1); 
        } else {
            print STDERR "TASK OK\n";
-           POSIX::_exit (0);
+           POSIX::_exit(0);
        } 
-       kill('KILL', $$); 
+       kill(-9, $$); 
     }
 
     # parent
@@ -598,13 +692,13 @@ sub fork_worker {
        
     } else {
        POSIX::close($csync[1]);
-       kill (9, $cpid); # make sure it gets killed
+       kill(-9, $cpid); # make sure it gets killed
        die $err;
     }
 
     PVE::Cluster::log_msg('info', $user, "starting task $upid");
 
-    my $tlist = active_workers($upid);
+    my $tlist = active_workers($upid, $sync);
     PVE::Cluster::broadcast_tasklist($tlist);
    
     my $res = 0;
@@ -612,28 +706,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);
                }
            }
        };
@@ -654,15 +773,13 @@ sub fork_worker {
            if ($outfh) {
                print $outfh "TASK ERROR: $err\n";
            }
-           kill (-15, $cpid);
-
-       } else {
-           kill (-9, $cpid); # make sure it gets killed
        }
 
+       &$kill_process_group($cpid, $pstart); # make sure it gets killed
+
        close($outfh);
 
-       waitpid ($cpid, 0);
+       waitpid($cpid, 0);
        $res = $?;
        &$log_task_result($upid, $user, $res);
     }