]> git.proxmox.com Git - pve-common.git/blobdiff - src/PVE/Tools.pm
tools: unbless errors in run_fork_with_timeout
[pve-common.git] / src / PVE / Tools.pm
index 2c64f6ac33206791a5e70b038b49b76fa71b13dd..53ac83fd2b744366706d6354c4c4434aa183f614 100644 (file)
@@ -369,6 +369,7 @@ sub run_command {
     my $afterfork;
     my $noerr;
     my $keeplocale;
+    my $quiet;
 
     eval {
 
@@ -395,6 +396,8 @@ sub run_command {
                $noerr = $param{$p};
            } elsif ($p eq 'keeplocale') {
                $keeplocale = $param{$p};
+           } elsif ($p eq 'quiet') {
+               $quiet = $param{$p};
            } else {
                die "got unknown parameter '$p' for run_command\n";
            }
@@ -497,7 +500,7 @@ sub run_command {
                            waitpid ($pid, 0);
                            die $err;
                        }
-                   } else {
+                   } elsif (!$quiet) {
                        print $buf;
                        *STDOUT->flush();
                    }
@@ -517,7 +520,7 @@ sub run_command {
                            waitpid ($pid, 0);
                            die $err;
                        }
-                   } else {
+                   } elsif (!$quiet) {
                        print STDERR $buf;
                        *STDERR->flush();
                    }
@@ -791,17 +794,20 @@ sub extract_param {
 
 # Note: we use this to wait until vncterm/spiceterm is ready
 sub wait_for_vnc_port {
-    my ($port, $timeout) = @_;
+    my ($port, $family, $timeout) = @_;
 
     $timeout = 5 if !$timeout;
     my $sleeptime = 0;
     my $starttime = [gettimeofday];
     my $elapsed;
 
+    my $cmd = ['/bin/ss', '-Htln', "sport = :$port"];
+    push @$cmd, $family == AF_INET6 ? '-6' : '-4' if defined($family);
+
     my $found;
     while (($elapsed = tv_interval($starttime)) < $timeout) {
        # -Htln = don't print header, tcp, listening sockets only, numeric ports
-       run_command(['/bin/ss', '-Htln', "sport = :$port"], outfunc => sub {
+       run_command($cmd, outfunc => sub {
            my $line = shift;
            if ($line =~ m/^LISTEN\s+\d+\s+\d+\s+\S+:(\d+)\s/) {
                $found = 1 if ($port == $1);
@@ -898,6 +904,13 @@ sub next_spice_port {
     return next_unused_port(61000, 61099, $family, $address);
 }
 
+sub must_stringify {
+       my ($value) = @_;
+       eval { $value = "$value" };
+       return "error turning value into a string: $@" if $@;
+       return $value;
+}
+
 # sigkill after $timeout  a $sub running in a fork if it can't write a pipe
 # the $sub has to return a single scalar
 sub run_fork_with_timeout {
@@ -929,7 +942,7 @@ sub run_fork_with_timeout {
            $pipe_out->flush();
        };
        if (my $err = $@) {
-           print {$pipe_out} encode_json({ error => $err });
+           print {$pipe_out} encode_json({ error => must_stringify($err) });
            $pipe_out->flush();
            POSIX::_exit(1);
        }
@@ -975,49 +988,40 @@ sub run_fork {
 sub df {
     my ($path, $timeout) = @_;
 
-    my $res = {
-       total => 0,
-       used => 0,
-       avail => 0,
+    my $df = sub { return Filesys::Df::df($path, 1) };
+
+    my $res = eval { run_fork_with_timeout($timeout, $df) } // {};
+    warn $@ if $@;
+
+    # untaint the values
+    my ($blocks, $used, $bavail) = map { defined($_) ? (/^(\d+)$/) : 0 }
+       $res->@{qw(blocks used bavail)};
+
+    return {
+       total => $blocks,
+       used => $used,
+       avail => $bavail,
     };
+}
 
-    my $pipe = IO::Pipe->new();
-    my $child = fork();
-    if (!defined($child)) {
-       warn "fork failed: $!\n";
-       return $res;
-    }
+sub du {
+    my ($path, $timeout) = @_;
 
-    if (!$child) {
-       $pipe->writer();
-       eval {
-           my $df = Filesys::Df::df($path, 1);
-           print {$pipe} "$df->{blocks}\n$df->{used}\n$df->{bavail}\n"
-               if defined($df);
-           $pipe->close();
-       };
-       if (my $err = $@) {
-           warn $err;
-           POSIX::_exit(1);
-       }
-       POSIX::_exit(0);
-    }
+    my $size;
 
-    $pipe->reader();
+    $timeout //= 10;
 
-    my $readvalues = sub {
-       $res->{total} = int(((<$pipe> // 0) =~ /^(\d*)$/)[0]);
-       $res->{used}  = int(((<$pipe> // 0) =~ /^(\d*)$/)[0]);
-       $res->{avail} = int(((<$pipe> // 0) =~ /^(\d*)$/)[0]);
-    };
-    eval {
-       run_with_timeout($timeout, $readvalues);
+    my $parser = sub {
+       my $line = shift;
+
+       if ($line =~ m/^(\d+)\s+total$/) {
+           $size = $1;
+       }
     };
-    warn $@ if $@;
-    $pipe->close();
-    kill('KILL', $child);
-    waitpid($child, 0);
-    return $res;
+
+    run_command(['du', '-scb', $path], outfunc => $parser, timeout => $timeout);
+
+    return $size;
 }
 
 # UPID helper
@@ -1626,9 +1630,16 @@ sub encrypt_pw {
 }
 
 # intended usage: convert_size($val, "kb" => "gb")
-# on reduction (converting to a bigger unit) we round up by default if
-# information got lost. E.g. `convert_size(1023, "b" => "kb")` returns 1
+# we round up to the next integer by default
+# E.g. `convert_size(1023, "b" => "kb")` returns 1
 # use $no_round_up to switch this off, above example would then return 0
+# this is also true for converting down e.g. 0.0005 gb to mb returns 1
+# (0 if $no_round_up is true)
+# allowed formats for value:
+# 1234
+# 1234.
+# 1234.1234
+# .1234
 sub convert_size {
     my ($value, $from, $to, $no_round_up) = @_;
 
@@ -1641,21 +1652,22 @@ sub convert_size {
        pb => 5,
     };
 
-    $from = lc($from); $to = lc($to);
+    die "no value given"
+       if !defined($value) || $value eq "";
+
+    $from = lc($from // ''); $to = lc($to // '');
     die "unknown 'from' and/or 'to' units ($from => $to)"
-       if !(defined($units->{$from}) && defined($units->{$to}));
+       if !defined($units->{$from}) || !defined($units->{$to});
 
-    my $shift_amount = $units->{$from} - $units->{$to};
+    die "value '$value' is not a valid, positive number"
+       if $value !~ m/^(?:[0-9]+\.?[0-9]*|[0-9]*\.[0-9]+)$/;
 
-    if ($shift_amount > 0) {
-       $value <<= ($shift_amount * 10);
-    } elsif ($shift_amount < 0) {
-       my $remainder = ($value & (1 << abs($shift_amount)*10) - 1);
-       $value >>= abs($shift_amount) * 10;
-       $value++ if $remainder && !$no_round_up;
-    }
+    my $shift_amount = ($units->{$from} - $units->{$to}) * 10;
+
+    $value *= 2**$shift_amount;
+    $value++ if !$no_round_up && ($value - int($value)) > 0.0;
 
-    return $value;
+    return int($value);
 }
 
 1;