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