X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=data%2FPVE%2FTools.pm;h=b42a5bb5f603b4ff5b3d13a9503aabb9067f2a64;hp=6eaf68c7ac3422bd74db51f0f8899ffecf6e147b;hb=804b104122b78b07f81fdc259db4dd5ab82f016a;hpb=e143e9d86b489c57166b5c192bf41dcc3452471e diff --git a/data/PVE/Tools.pm b/data/PVE/Tools.pm index 6eaf68c..b42a5bb 100644 --- a/data/PVE/Tools.pm +++ b/data/PVE/Tools.pm @@ -12,6 +12,9 @@ use Fcntl qw(:DEFAULT :flock); use base 'Exporter'; use URI::Escape; use Encode; +use Digest::SHA1; +use Text::ParseWords; +use String::ShellQuote; our @EXPORT_OK = qw( lock_file @@ -176,13 +179,11 @@ sub run_command { my $pid; eval { - my $reader = IO::File->new(); - my $writer = IO::File->new(); - my $error = IO::File->new(); - my $input; + my $output; my $outfunc; my $errfunc; + my $logfunc; foreach my $p (keys %param) { if ($p eq 'timeout') { @@ -197,27 +198,44 @@ sub run_command { }; } elsif ($p eq 'input') { $input = $param{$p}; + } elsif ($p eq 'output') { + $output = $param{$p}; } elsif ($p eq 'outfunc') { $outfunc = $param{$p}; } elsif ($p eq 'errfunc') { $errfunc = $param{$p}; + } elsif ($p eq 'logfunc') { + $logfunc = $param{$p}; } else { die "got unknown parameter '$p' for run_command\n"; } } + my $reader = $output && $output =~ m/^>&/ ? $output : IO::File->new(); + my $writer = $input && $input =~ m/^<&/ ? $input : IO::File->new(); + my $error = IO::File->new(); + # try to avoid locale related issues/warnings my $lang = $param{lang} || 'C'; my $orig_pid = $$; eval { - local $ENV{LANG} = $lang; + local $ENV{LC_ALL} = $lang; # suppress LVM warnings like: "File descriptor 3 left open"; local $ENV{LVM_SUPPRESS_FD_WARNINGS} = "1"; $pid = open3($writer, $reader, $error, @$cmd) || die $!; + + # if we pipe fron STDIN, open3 closes STDIN, so we we + # a perl warning "Filehandle STDIN reopened as GENXYZ .. " + # as soon as we open a new file. + # to avoid that we open /dev/null + if (!ref($writer) && !defined(fileno(STDIN))) { + POSIX::close(0); + open(STDIN, "add($reader); + $select->add($reader) if ref($reader); $select->add($error); my $outlog = ''; @@ -260,12 +280,13 @@ sub run_command { } $select->remove ($h) if !$count; if ($h eq $reader) { - if ($outfunc) { + if ($outfunc || $logfunc) { eval { $outlog .= $buf; while ($outlog =~ s/^([^\010\r\n]*)(\r|\n|(\010)+|\r\n)//s) { my $line = $1; - &$outfunc($line); + &$outfunc($line) if $outfunc; + &$logfunc($line) if $logfunc; } }; my $err = $@; @@ -279,12 +300,13 @@ sub run_command { *STDOUT->flush(); } } elsif ($h eq $error) { - if ($errfunc) { + if ($errfunc || $logfunc) { eval { $errlog .= $buf; while ($errlog =~ s/^([^\010\r\n]*)(\r|\n|(\010)+|\r\n)//s) { my $line = $1; - &$errfunc($line); + &$errfunc($line) if $errfunc; + &$logfunc($line) if $logfunc; } }; my $err = $@; @@ -302,7 +324,10 @@ sub run_command { } &$outfunc($outlog) if $outfunc && $outlog; + &$logfunc($outlog) if $logfunc && $outlog; + &$errfunc($errlog) if $errfunc && $errlog; + &$logfunc($errlog) if $logfunc && $errlog; waitpid ($pid, 0); @@ -311,12 +336,14 @@ sub run_command { } elsif (my $sig = ($? & 127)) { die "got signal $sig\n"; } elsif (my $ec = ($? >> 8)) { - if ($errmsg && $laststderr) { - my $lerr = $laststderr; - $laststderr = undef; - die "$lerr\n"; + if (!($ec == 24 && ($cmdstr =~ m|^(\S+/)?rsync\s|))) { + if ($errmsg && $laststderr) { + my $lerr = $laststderr; + $laststderr = undef; + die "$lerr\n"; + } + die "exit code $ec\n"; } - die "exit code $ec\n"; } alarm(0); @@ -345,12 +372,16 @@ sub run_command { die "command '$cmdstr' failed: $err"; } } + + return undef; } sub split_list { my $listtxt = shift || ''; - $listtxt =~ s/[,;\0]/ /g; + return split (/\0/, $listtxt) if $listtxt =~ m/\0/; + + $listtxt =~ s/[,;]/ /g; $listtxt =~ s/^\s+//; my @data = split (/\s+/, $listtxt); @@ -589,7 +620,7 @@ sub upid_open { my $outfh = IO::File->new ($filename, O_WRONLY|O_CREAT|O_EXCL, $perm) || die "unable to create output file '$filename' - $!\n"; - chown $wwwid, $outfh; + chown $wwwid, -1, $outfh; return $outfh; }; @@ -635,5 +666,77 @@ sub decode_text { return Encode::decode("utf8", uri_unescape($data)); } +sub random_ether_addr { + + my $rand = Digest::SHA1::sha1_hex(rand(), time()); + + my $mac = ''; + for (my $i = 0; $i < 6; $i++) { + my $ss = hex(substr($rand, $i*2, 2)); + if (!$i) { + $ss &= 0xfe; # clear multicast + $ss |= 2; # set local id + } + $ss = sprintf("%02X", $ss); + + if (!$i) { + $mac .= "$ss"; + } else { + $mac .= ":$ss"; + } + } + + return $mac; +} + +sub shellquote { + my $str = shift; + + return String::ShellQuote::shell_quote($str); +} + +# split an shell argument string into an array, +sub split_args { + my ($str) = @_; + + return $str ? [ Text::ParseWords::shellwords($str) ] : []; +} + +sub dump_logfile { + my ($filename, $start, $limit) = @_; + + my $lines = []; + my $count = 0; + + my $fh = IO::File->new($filename, "r"); + if (!$fh) { + $count++; + push @$lines, { n => $count, t => "unable to open file - $!"}; + return ($count, $lines); + } + + $start = 0 if !$start; + $limit = 50 if !$limit; + + my $line; + while (defined($line = <$fh>)) { + next if $count++ < $start; + next if $limit <= 0; + chomp $line; + push @$lines, { n => $count, t => $line}; + $limit--; + } + + close($fh); + + # HACK: ExtJS store.guaranteeRange() does not like empty array + # so we add a line + if (!$count) { + $count++; + push @$lines, { n => $count, t => "no content"}; + } + + return ($count, $lines); +} 1;