X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2FPVE%2FTools.pm;h=ae06c12b224b873dc7a27d5965078f54a8d9a851;hb=e4a1d8e240c18f363f9f91003d01ee07be24ee21;hp=3f3958e0e354f4f5f4cbe6fea92028fdd58f6e19;hpb=cd9bd2526a6c7a74d3f8ce3de32bc149d96324f3;p=pve-common.git diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm index 3f3958e..ae06c12 100644 --- a/src/PVE/Tools.pm +++ b/src/PVE/Tools.pm @@ -8,6 +8,8 @@ use Socket qw(AF_INET AF_INET6 AI_ALL AI_V4MAPPED); use IO::Select; use File::Basename; use File::Path qw(make_path); +use Filesys::Df (); # don't overwrite our df() +use IO::Pipe; use IO::File; use IO::Dir; use IPC::Open3; @@ -39,6 +41,7 @@ template_replace safe_print trim extract_param +file_copy ); my $pvelogdir = "/var/log/pve"; @@ -65,12 +68,12 @@ our $IPV6RE = "(?:" . our $IPRE = "(?:$IPV4RE|$IPV6RE)"; -use constant (CLONE_NEWNS => 0x00020000, +use constant {CLONE_NEWNS => 0x00020000, CLONE_NEWUTS => 0x04000000, CLONE_NEWIPC => 0x08000000, CLONE_NEWUSER => 0x10000000, CLONE_NEWPID => 0x20000000, - CLONE_NEWNET => 0x40000000); + CLONE_NEWNET => 0x40000000}; sub run_with_timeout { my ($timeout, $code, @param) = @_; @@ -218,6 +221,12 @@ sub file_get_contents { return $content; } +sub file_copy { + my ($filename, $dst, $max, $perm) = @_; + + file_set_contents ($dst, file_get_contents($filename, $max), $perm); +} + sub file_read_firstline { my ($filename) = @_; @@ -310,6 +319,7 @@ sub run_command { my $timeout; my $oldtimeout; my $pid; + my $exitcode; my $outfunc; my $errfunc; @@ -317,6 +327,7 @@ sub run_command { my $input; my $output; my $afterfork; + my $noerr; eval { @@ -339,6 +350,8 @@ sub run_command { $logfunc = $param{$p}; } elsif ($p eq 'afterfork') { $afterfork = $param{$p}; + } elsif ($p eq 'noerr') { + $noerr = $param{$p}; } else { die "got unknown parameter '$p' for run_command\n"; } @@ -484,14 +497,14 @@ sub run_command { die "failed to execute\n"; } elsif (my $sig = ($? & 127)) { die "got signal $sig\n"; - } elsif (my $ec = ($? >> 8)) { - if (!($ec == 24 && ($cmdstr =~ m|^(\S+/)?rsync\s|))) { + } elsif ($exitcode = ($? >> 8)) { + if (!($exitcode == 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 $exitcode\n"; } } @@ -520,12 +533,12 @@ sub run_command { if ($errmsg) { $err =~ s/^usermod:\s*// if $cmdstr =~ m|^(\S+/)?usermod\s|; die "$errmsg: $err"; - } else { + } elsif(!$noerr) { die "command '$cmdstr' failed: $err"; } } - return undef; + return $exitcode; } sub split_list { @@ -790,33 +803,51 @@ sub next_spice_port { # NOTE: NFS syscall can't be interrupted, so alarm does # not work to provide timeouts. # from 'man nfs': "Only SIGKILL can interrupt a pending NFS operation" -# So the spawn external 'df' process instead of using -# Filesys::Df (which uses statfs syscall) +# So fork() before using Filesys::Df sub df { my ($path, $timeout) = @_; - my $cmd = [ 'df', '-P', '-B', '1', $path]; - my $res = { total => 0, used => 0, avail => 0, }; - my $parser = sub { - my $line = shift; - if (my ($fsid, $total, $used, $avail) = $line =~ - m/^(\S+.*)\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%\s.*$/) { - $res = { - total => $total, - used => $used, - avail => $avail, - }; + my $pipe = IO::Pipe->new(); + my $child = fork(); + if (!defined($child)) { + warn "fork failed: $!\n"; + return $res; + } + + if (!$child) { + $pipe->writer(); + eval { + my $df = Filesys::Df::df($path, 1); + print {$pipe} "$df->{blocks}\n$df->{used}\n$df->{bavail}\n"; + $pipe->close(); + }; + if (my $err = $@) { + warn $err; + POSIX::_exit(1); } + POSIX::_exit(0); + } + + $pipe->reader(); + + my $readvalues = sub { + $res->{total} = int(<$pipe>); + $res->{used} = int(<$pipe>); + $res->{avail} = int(<$pipe>); + }; + eval { + run_with_timeout($timeout, $readvalues); }; - eval { run_command($cmd, timeout => $timeout, outfunc => $parser); }; warn $@ if $@; - + $pipe->close(); + kill('KILL', $child); + waitpid($child, 0); return $res; } @@ -940,23 +971,10 @@ sub random_ether_addr { my $rand = Digest::SHA::sha1_hex($$, rand(), $seconds, $microseconds); - 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"; - } - } + # clear multicast, set local id + vec($rand, 0, 8) = (vec($rand, 0, 8) & 0xfe) | 2; - return $mac; + return sprintf("%02X:%02X:%02X:%02X:%02X:%02X", unpack("C6", $rand)); } sub shellquote { @@ -1162,9 +1180,9 @@ sub parse_host_and_port { return; # nothing } -sub unshare { +sub unshare($) { my ($flags) = @_; - syscall 272, $flags; + return 0 == syscall(272, $flags); } 1;