X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=src%2FPVE%2FTools.pm;h=1d1f4b8429e951503fe71175159796f0dbacfee7;hp=32f36ac2d6ac9a2fa9fa5cd9ce6bb21bd60ef422;hb=53c255218ebec327f881a479a7be1e04646ae2e9;hpb=85d5625a15f23743c7a117d2784989bf1457efae diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm index 32f36ac..1d1f4b8 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,14 @@ 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}; + +use constant O_PATH => 0x10000000; sub run_with_timeout { my ($timeout, $code, @param) = @_; @@ -123,15 +128,16 @@ sub lock_file_full { my $lock_func = sub { if (!$lock_handles->{$$}->{$filename}) { - $lock_handles->{$$}->{$filename} = new IO::File (">>$filename") || - die "can't open file - $!\n"; + my $fh = new IO::File(">>$filename") || + die "can't open file - $!\n"; + $lock_handles->{$$}->{$filename} = { fh => $fh, refcount => 0}; } - if (!flock ($lock_handles->{$$}->{$filename}, $mode|LOCK_NB)) { - print STDERR "trying to aquire lock..."; + if (!flock($lock_handles->{$$}->{$filename}->{fh}, $mode|LOCK_NB)) { + print STDERR "trying to acquire lock..."; my $success; while(1) { - $success = flock($lock_handles->{$$}->{$filename}, $mode); + $success = flock($lock_handles->{$$}->{$filename}->{fh}, $mode); # try again on EINTR (see bug #273) if ($success || ($! != EINTR)) { last; @@ -139,10 +145,11 @@ sub lock_file_full { } if (!$success) { print STDERR " failed\n"; - die "can't aquire lock - $!\n"; + die "can't acquire lock '$filename' - $!\n"; } print STDERR " OK\n"; } + $lock_handles->{$$}->{$filename}->{refcount}++; }; my $res; @@ -156,9 +163,12 @@ sub lock_file_full { $err = $@; } - if (my $fh = $lock_handles->{$$}->{$filename}) { - $lock_handles->{$$}->{$filename} = undef; - close ($fh); + if (my $fh = $lock_handles->{$$}->{$filename}->{fh}) { + my $refcount = --$lock_handles->{$$}->{$filename}->{refcount}; + if ($refcount <= 0) { + $lock_handles->{$$}->{$filename} = undef; + close ($fh); + } } if ($err) { @@ -218,6 +228,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 +326,7 @@ sub run_command { my $timeout; my $oldtimeout; my $pid; + my $exitcode; my $outfunc; my $errfunc; @@ -317,6 +334,7 @@ sub run_command { my $input; my $output; my $afterfork; + my $noerr; eval { @@ -339,6 +357,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 +504,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 +540,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 +810,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; } @@ -1023,7 +1061,7 @@ sub dump_logfile { } sub dump_journal { - my ($start, $limit, $filter) = @_; + my ($start, $limit, $since, $until) = @_; my $lines = []; my $count = 0; @@ -1041,6 +1079,9 @@ sub dump_journal { }; my $cmd = ['journalctl', '-o', 'short', '--no-pager']; + + push @$cmd, '--since', $since if $since; + push @$cmd, '--until', $until if $until; run_command($cmd, outfunc => $parser); # HACK: ExtJS store.guaranteeRange() does not like empty array @@ -1149,9 +1190,27 @@ sub parse_host_and_port { return; # nothing } -sub unshare { +sub unshare($) { my ($flags) = @_; - syscall 272, $flags; + return 0 == syscall(272, $flags); +} + +sub setns($$) { + my ($fileno, $nstype) = @_; + return 0 == syscall(308, $fileno, $nstype); +} + +sub syncfs($) { + my ($fileno) = @_; + return 0 == syscall(306, $fileno); +} + +sub sync_mountpoint { + my ($path) = @_; + sysopen my $fd, $path, O_PATH or die "failed to open $path: $!\n"; + my $result = syncfs(fileno($fd)); + close($fd); + return $result; } 1;