[pve-devel] r4964 - pve-common/trunk

svn-commits at proxmox.com svn-commits at proxmox.com
Tue Aug 10 14:19:05 CEST 2010


Author: dietmar
Date: 2010-08-10 12:19:05 +0000 (Tue, 10 Aug 2010)
New Revision: 4964

Added:
   pve-common/trunk/Tools.pm
Modified:
   pve-common/trunk/Makefile
Log:


Modified: pve-common/trunk/Makefile
===================================================================
--- pve-common/trunk/Makefile	2010-08-10 12:06:16 UTC (rev 4963)
+++ pve-common/trunk/Makefile	2010-08-10 12:19:05 UTC (rev 4964)
@@ -22,6 +22,7 @@
 	SafeSyslog.pm		\
 	AtomicFile.pm		\
 	INotify.pm		\
+	Tools.pm		\
 	Exception.pm
 
 all: ${DEB}

Added: pve-common/trunk/Tools.pm
===================================================================
--- pve-common/trunk/Tools.pm	                        (rev 0)
+++ pve-common/trunk/Tools.pm	2010-08-10 12:19:05 UTC (rev 4964)
@@ -0,0 +1,308 @@
+package PVE::Tools;
+
+use strict;
+use POSIX;
+use IO::Select;
+use IO::File;
+use IPC::Open3;
+use Fcntl qw(:DEFAULT :flock);
+use base 'Exporter';
+
+our @EXPORT_OK = qw(lock_file run_command file_set_contents file_get_contents);
+
+# flock: we use one file handle per process, so lock file
+# can be called multiple times and succeeds for the same process.
+
+my $lock_handles =  {};
+
+sub lock_file {
+    my ($filename, $timeout, $text, $code, @param) = @_;
+
+    my $res;
+
+    $timeout = 10 if !$timeout;
+
+    eval {
+
+        local $SIG{ALRM} = sub { die "got timeout\n"; };
+
+        alarm ($timeout);
+
+        if (!$lock_handles->{$$}->{$filename}) {
+            $lock_handles->{$$}->{$filename} = new IO::File (">>$filename") ||
+                die "can't open lock for $text '$filename' - $!\n";
+        }
+
+        if (!flock ($lock_handles->{$$}->{$filename}, LOCK_EX|LOCK_NB)) {
+            print STDERR "trying to aquire lock...";
+            if (!flock ($lock_handles->{$$}->{$filename}, LOCK_EX)) {
+                print STDERR " failed\n";
+                die "can't aquire lock for $text '$filename' - $!\n";
+            }
+            print STDERR " OK\n";
+        }
+        alarm (0);
+
+        $res = &$code(@param);
+    };
+
+    my $err = $@;
+
+    alarm (0);
+
+    if ($lock_handles->{$$}->{$filename}) {
+        my $fh = $lock_handles->{$$}->{$filename};
+        $lock_handles->{$$}->{$filename} = undef;
+        close ($fh);
+    }
+
+    if ($err) {
+        $@ = $err;
+        return undef;
+    }
+
+    $@ = undef;
+
+    return $res;
+}
+
+sub file_set_contents {
+    my ($filename, $data, $perm)  = @_;
+
+    $perm = 0644 if !defined($perm);
+
+    my $tmpname = "$filename.tmp.$$";
+
+    eval {
+	my $fh = IO::File->new($tmpname, O_WRONLY|O_CREAT, $perm);
+	die "unable to open file '$tmpname' - $!\n" if !$fh;
+	die "unable to write '$tmpname' - $!\n" unless print $fh $data;
+	die "closing file '$tmpname' failed - $!\n" unless close $fh;
+    };
+    my $err = $@;
+
+    if ($err) {
+	unlink $tmpname;
+	die $err;
+    }
+
+    if (!rename($tmpname, $filename)) {
+	my $msg = "close (rename) atomic file '$filename' failed: $!\n";
+	unlink $tmpname;
+	die $msg;	
+    }
+}
+
+sub file_get_contents {
+    my ($filename, $max) = @_;
+
+    my $fh = IO::File->new($filename, "r") ||
+	die "can't open '$filename' - $!\n";
+
+    my $content = safe_read_from($fh, $max);
+
+    close $fh;
+
+    return $content;
+}
+
+sub safe_read_from {
+    my ($fh, $max, $oneline) = @_;
+
+    $max = 32768 if !$max;
+
+    my $br = 0;
+    my $input = '';
+    my $count;
+    while ($count = sysread($fh, $input, 8192, $br)) {
+	$br += $count;
+	die "input too long - aborting\n" if $br > $max;
+	if ($oneline && $input =~ m/^(.*)\n/) {
+	    $input = $1;
+	    last;
+	}
+    } 
+    die "unable to read input - $!\n" if !defined($count);
+
+    return $input;
+}
+
+sub run_command {
+    my ($cmd, %param) = @_;
+
+    my $old_umask;
+
+    $cmd = [ $cmd ] if !ref($cmd);
+
+    my $cmdstr = join (' ', @$cmd);
+
+    my $errmsg;
+    my $laststderr;
+
+    eval {
+	my $reader = IO::File->new();
+	my $writer = IO::File->new();
+	my $error  = IO::File->new();
+
+	my $timeout;
+	my $input;
+	my $ticket;
+	my $outfunc;
+	my $errfunc;
+
+	foreach my $p (keys %param) {
+	    if ($p eq 'timeout') {
+		$timeout = $param{$p};
+	    } elsif ($p eq 'umask') {
+		umask($param{$p});
+	    } elsif ($p eq 'errmsg') {
+		$errmsg = $param{$p};
+		$errfunc = sub {
+		    print STDERR "$laststderr\n" if $laststderr;
+		    $laststderr = shift; 
+		};
+	    } elsif ($p eq 'ticket') {
+		$ticket = $param{$p};
+	    } elsif ($p eq 'input') {
+		$input = $param{$p};
+	    } elsif ($p eq 'outfunc') {
+		$outfunc = $param{$p};
+	    } elsif ($p eq 'errfunc') {
+		$errfunc = $param{$p};
+	    } else {
+		die "got unknown parameter '$p' for run_command\n";
+	    }
+	}
+
+	# try to avoid locale related issues/warnings
+	my $lang = $param{lang} || 'C'; 
+ 
+	my $orig_pid = $$;
+
+	my $pid;
+	eval {
+	    local $ENV{LANG} = $lang;
+
+	    # suppress LVM warnings like: "File descriptor 3 left open";
+	    local $ENV{LVM_SUPPRESS_FD_WARNINGS} = "1";
+
+	    local $ENV{PVETICKET} = $ticket if $ticket;
+
+	    $pid = open3($writer, $reader, $error, @$cmd) || die $!;
+	};
+
+	my $err = $@;
+
+	# catch exec errors
+	if ($orig_pid != $$) {
+	    warn "ERROR: $err";
+	    POSIX::_exit (1); 
+	    kill ('KILL', $$); 
+	}
+
+	die $err if $err;
+
+	print $writer $input if defined $input;
+	close $writer;
+
+	my $select = new IO::Select;
+	$select->add($reader);
+	$select->add($error);
+
+	my $outlog = '';
+	my $errlog = '';
+
+	while ($select->count) {
+	    my @handles = $select->can_read($timeout);
+
+	    if (defined ($timeout) && (scalar (@handles) == 0)) {
+		kill (9, $pid);
+		waitpid ($pid, 0);
+		die "timeout\n";
+	    }
+
+	    foreach my $h (@handles) {
+		my $buf = '';
+		my $count = sysread ($h, $buf, 4096);
+		if (!defined ($count)) {
+		    my $err = $!;
+		    kill (9, $pid);
+		    waitpid ($pid, 0);
+		    die $err;
+		}
+		$select->remove ($h) if !$count;
+		if ($h eq $reader) {
+		    if ($outfunc) {
+			eval {
+			    $outlog .= $buf;
+			    while ($outlog =~ s/^([^\010\r\n]*)(\r|\n|(\010)+|\r\n)//s) {
+				my $line = $1;
+				&$outfunc($line);
+			    }
+			};
+			my $err = $@;
+			if ($err) {
+			    kill (9, $pid);
+			    waitpid ($pid, 0);
+			    die $err;
+			}
+		    } else {
+			print $buf;
+			*STDOUT->flush();
+		    }
+		} elsif ($h eq $error) {
+		    if ($errfunc) {
+			eval {
+			    $errlog .= $buf;
+			    while ($errlog =~ s/^([^\010\r\n]*)(\r|\n|(\010)+|\r\n)//s) {
+				my $line = $1;
+				&$errfunc($line);
+			    }
+			};
+			my $err = $@;
+			if ($err) {
+			    kill (9, $pid);
+			    waitpid ($pid, 0);
+			    die $err;
+			}
+		    } else {
+			print STDERR $buf;
+			*STDERR->flush();
+		    }
+		}
+	    }
+	}
+
+	&$outfunc($outlog) if $outfunc && $outlog;
+	&$errfunc($errlog) if $errfunc && $errlog;
+
+	waitpid ($pid, 0);
+  
+	if ($? == -1) {
+	    die "failed to execute\n";
+	} elsif (my $sig = ($? & 127)) {
+	    die "got signal $sig\n";
+	} elsif (my $ec = ($? >> 8)) {
+	    die "$laststderr\n" if ($errmsg && $laststderr);
+	    die "exit code $ec\n";
+	}
+
+	print STDERR "$laststderr\n" if $laststderr;
+
+    };
+
+    my $err = $@;
+
+    umask ($old_umask) if defined($old_umask);
+
+    if ($err) {
+	if ($errmsg) {
+	    die "$errmsg: $err";
+	} else {
+	    die "command '$cmdstr' failed: $err";
+	}
+    }
+}
+
+
+1;



More information about the pve-devel mailing list