my $afterfork;
my $noerr;
my $keeplocale;
+ my $quiet;
eval {
$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";
}
waitpid ($pid, 0);
die $err;
}
- } else {
+ } elsif (!$quiet) {
print $buf;
*STDOUT->flush();
}
waitpid ($pid, 0);
die $err;
}
- } else {
+ } elsif (!$quiet) {
print STDERR $buf;
*STDERR->flush();
}
# 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);
usleep($sleeptime);
}
- return undef;
+ die "Timeout while waiting for port '$port' to get ready!\n";
}
sub next_unused_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 {
$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);
}
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
}
# 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) = @_;
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;