[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