[pve-devel] r5152 - pve-common/trunk
svn-commits at proxmox.com
svn-commits at proxmox.com
Wed Sep 15 09:24:56 CEST 2010
Author: dietmar
Date: 2010-09-15 07:24:56 +0000 (Wed, 15 Sep 2010)
New Revision: 5152
Modified:
pve-common/trunk/ChangeLog
pve-common/trunk/ProcFSTools.pm
pve-common/trunk/RPCEnvironment.pm
Log:
* RPCEnvironment.pm (fork_worker): moved from PVE::Utils
* ProcFSTools.pm (read_proc_starttime): moved from PVE::Utils
Modified: pve-common/trunk/ChangeLog
===================================================================
--- pve-common/trunk/ChangeLog 2010-09-15 06:04:36 UTC (rev 5151)
+++ pve-common/trunk/ChangeLog 2010-09-15 07:24:56 UTC (rev 5152)
@@ -1,3 +1,9 @@
+2010-09-15 Proxmox Support Team <support at proxmox.com>
+
+ * RPCEnvironment.pm (fork_worker): moved from PVE::Utils
+
+ * ProcFSTools.pm (read_proc_starttime): moved from PVE::Utils
+
2010-09-14 Proxmox Support Team <support at proxmox.com>
* JSONSchema.pm (get_standard_option): allow to set defaults.
Modified: pve-common/trunk/ProcFSTools.pm
===================================================================
--- pve-common/trunk/ProcFSTools.pm 2010-09-15 06:04:36 UTC (rev 5151)
+++ pve-common/trunk/ProcFSTools.pm 2010-09-15 07:24:56 UTC (rev 5152)
@@ -70,4 +70,18 @@
return (0, 0);
}
+sub read_proc_starttime {
+ my $pid = shift;
+
+ my $statstr = PVE::Tools::file_read_firstline("/proc/$pid/stat");
+
+ if ($statstr && $statstr =~ m/^$pid \(.*\) \S (-?\d+) -?\d+ -?\d+ -?\d+ -?\d+ \d+ \d+ \d+ \d+ \d+ (\d+) (\d+) (-?\d+) (-?\d+) -?\d+ -?\d+ -?\d+ 0 (\d+) (\d+) (-?\d+) \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ -?\d+ -?\d+ \d+ \d+ \d+/) {
+ my $starttime = $6;
+
+ return $starttime;
+ }
+
+ return 0;
+}
+
1;
Modified: pve-common/trunk/RPCEnvironment.pm
===================================================================
--- pve-common/trunk/RPCEnvironment.pm 2010-09-15 06:04:36 UTC (rev 5151)
+++ pve-common/trunk/RPCEnvironment.pm 2010-09-15 07:24:56 UTC (rev 5152)
@@ -2,7 +2,12 @@
use strict;
use warnings;
+use Posix;
+use IO::File;
+use PVE::INotify;
+use PVE::ProcFSTools;
+
# we use this singleton class to pass RPC related environment value
my $pve_env;
@@ -23,6 +28,8 @@
die "unknown environment type" if !$type || $type !~ m/^(cli|pub|priv)$/;
+ $SIG{CHLD} = &worker_reaper;
+
# environment types
# cli ... command started fron command line
# pub ... access from public server (apache)
@@ -77,4 +84,234 @@
return $nodes;
}
+# UPID helper
+# WARN: $res->{filename} must not depend on PID, because we
+# use it before we know the PID
+
+sub upid_decode {
+ my $upid = shift;
+
+ my $res;
+
+ # "UPID:$pid:$start:$type:$data"
+ if ($upid =~ m/^UPID:(\d+)(-(\d+))?:(\d+):([^:\s]+):(.*)$/) {
+ $res->{pid} = $1;
+ $res->{pstart} = $3 || 0;
+ $res->{starttime} = $4;
+ $res->{type} = $5;
+ $res->{data} = $6;
+
+ if ($res->{type} eq 'vmops') {
+ if ($res->{data} =~ m/^([^:\s]+):(\d+):(\d+):(\S+)$/) {
+ $res->{command} = $1;
+ $res->{cid} = $2;
+ $res->{veid} = $3;
+ $res->{user} = $4;
+
+ $res->{filename} = "/tmp/vmops-$res->{veid}.out";
+ } else {
+ return undef;
+ }
+ } elsif ($res->{type} eq 'apldownload') {
+ if ($res->{data} =~ m/^([^:\s]+):(.+)$/) {
+ $res->{user} = $1;
+ $res->{apl} = $2;
+ $res->{filename} = "/tmp/apldownload-$res->{user}.out";
+ } else {
+ return undef;
+ }
+ }
+ }
+
+ return $res;
+}
+
+sub upid_encode {
+ my $uip_hash = shift;
+
+ my $d = $uip_hash; # shortcut
+
+ return "UPID:$d->{pid}-$d->{pstart}:$d->{starttime}:$d->{type}:$d->{data}";
+}
+
+# save $SIG{CHLD} handler implementation.
+# simply set $SIG{CHLD} = &worker_reaper;
+# and register forked processes with register_worker(pid)
+# Note: using $SIG{CHLD} = 'IGNORE' or $SIG{CHLD} = sub { wait (); } or ...
+# has serious side effects, because perls built in system() and open()
+# functions can't get the correct exit status of a child. So we cant use
+# that (also see perlipc)
+
+my $WORKER_PIDS;
+
+sub worker_reaper {
+ local $!; local $?;
+ foreach my $pid (keys %$WORKER_PIDS) {
+ my $waitpid = waitpid ($pid, WNOHANG);
+ if (defined($waitpid) && ($waitpid == $pid)) {
+ delete ($WORKER_PIDS->{$pid});
+ }
+ }
+}
+
+sub register_worker {
+ my $pid = shift;
+
+ return if !$pid;
+
+ # do not register if already finished
+ my $waitpid = waitpid ($pid, WNOHANG);
+ if (defined($waitpid) && ($waitpid == $pid)) {
+ delete ($WORKER_PIDS->{$pid});
+ return;
+ }
+
+ $WORKER_PIDS->{$pid} = 1;
+}
+
+# start long running workers
+# $data append to the returned uniquely identifier, which
+# has the following format: "UPID:$pid-$pstart:$startime:$dtype:$data"
+# STDIN is redirected to /dev/null
+# STDOUT,STDERR are redirected to the filename returned by upid_decode
+# that file is locked wit flock to make sure only one process
+# is writing it
+
+sub fork_worker {
+ my ($dtype, $data, $function) = @_;
+
+ my $cpid;
+
+ $dtype = 'unknown' if !defined ($dtype);
+
+ $data = '' if !defined ($data);
+
+ my $starttime = time ();
+
+ my @psync = POSIX::pipe();
+
+ # detect filename with faked PID
+ my $tmp = upid_decode ("UPID:0-0:0:$dtype:$data");
+ my $filename = $tmp->{filename};
+
+ my $lockfh;
+ # lock output file
+ if ($filename) {
+
+ $lockfh = IO::File->new ($filename, O_WRONLY|O_CREAT) ||
+ die "unable to open output file - $!\n";
+
+ my $wwwid = getpwnam('www-data');
+ chown $wwwid, $filename;
+
+ if (!flock ($lockfh, LOCK_EX|LOCK_NB)) {
+ undef $lockfh; # close
+ die "unable to lock output file\n";
+ }
+
+ if (!truncate ($lockfh, 0)) {
+ die "unable to truncate output file - $!\n";
+ }
+ }
+
+ if (($cpid = fork()) == 0) {
+
+ $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub { die "received interrupt\n"; };
+
+ $SIG{CHLD} = $SIG{PIPE} = 'DEFAULT';
+
+ # set sess/process group - we want to be able to kill the
+ # whole process group
+ POSIX::setsid();
+
+ POSIX::close ($psync[0]);
+
+ PVE::INotify::inotify_close();
+
+ # we close the socket
+ my $httpd = $pve_config_daemon->{_daemon};
+ $httpd->close();
+
+ # same algorythm as used inside SA
+
+ # STDIN = /dev/null
+ my $fd = fileno (STDIN);
+ close STDIN;
+ POSIX::close(0) if $fd != 0;
+
+ if (!open (STDIN, "</dev/null")) {
+ POSIX::_exit (1);
+ kill ('KILL', $$);
+ }
+
+ # redirect STDOUT
+ $fd = fileno(STDOUT);
+ close STDOUT;
+ POSIX::close (1) if $fd != 1;
+
+ if ($filename) {
+ if (!open (STDOUT, ">&", $lockfh)) {
+ POSIX::_exit (1);
+ kill ('KILL', $$);
+ }
+
+ STDOUT->autoflush (1);
+ } else {
+ if (!open (STDOUT, ">/dev/null")) {
+ POSIX::_exit (1);
+ kill ('KILL', $$);
+ }
+ }
+
+ # redirect STDERR to STDOUT
+ $fd = fileno (STDERR);
+ close STDERR;
+ POSIX::close(2) if $fd != 2;
+
+ if (!open (STDERR, ">&1")) {
+ POSIX::_exit (1);
+ kill ('KILL', $$);
+ }
+
+ STDERR->autoflush (1);
+
+ my $pstart = PVE::ProcFSTools::read_proc_starttime ($$) ||
+ die "unable to read process starttime";
+
+ my $upid = upid_encode ({
+ pid => $$, pstart => $pstart, starttime => $starttime,
+ type => $dtype, data => $data });
+
+ # sync with parent
+ POSIX::write ($psync[1], $upid, length ($upid));
+ POSIX::close ($psync[1]);
+
+ &$function ($upid);
+
+ die "should not be reached";
+ }
+
+ POSIX::close ($psync[1]);
+
+ # sync with child (wait until child starts)
+ my $upid = '';
+ POSIX::read($psync[0], $upid, 4096);
+ POSIX::close ($psync[0]);
+
+ if ($lockfh) {
+ undef $lockfh; # close
+ }
+
+ my $uh = upid_decode ($upid);
+ if (!$uh ||
+ !($uh->{pid} == $cpid && $uh->{starttime} == $starttime &&
+ $uh->{type} eq $dtype && $uh->{data} eq $data)) {
+ syslog ('err', "got strange upid - $upid\n");
+ }
+
+ register_worker ($cpid);
+
+ return $upid;
+}
+
1;
More information about the pve-devel
mailing list