[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