[pve-devel] [PATCH common v2] tools: add pipe_socket_to_command

Thomas Lamprecht t.lamprecht at proxmox.com
Thu Aug 17 15:05:45 CEST 2017


Signed-off-by: Thomas Lamprecht <t.lamprecht at proxmox.com>
---

changes v1 -> v2:
 * break out of the loop after the 30 second timeout, this allows us to
   report an error back instead of endlessly waiting. Can only happen if the
   process hangs in uninterruptible sleep stat (e.g. blocked in IO path).

 src/PVE/Tools.pm | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm
index bd025e2..9818f63 100644
--- a/src/PVE/Tools.pm
+++ b/src/PVE/Tools.pm
@@ -580,6 +580,64 @@ sub run_command {
     return $exitcode;
 }
 
+# Run a command with a tcp socket as standard input.
+sub pipe_socket_to_command  {
+    my ($cmd, $ip, $port) = @_;
+
+    my $params = {
+	Listen => 1,
+	ReuseAddr => 1,
+	Proto => &Socket::IPPROTO_TCP,
+	GetAddrInfoFlags => 0,
+	LocalAddr => $ip,
+	LocalPort => $port,
+    };
+    my $socket = IO::Socket::IP->new(%$params) or die "failed to open socket: $!\n";
+
+    print "$ip\n$port\n"; # tell remote where to connect
+    *STDOUT->flush();
+
+    alarm 0;
+    local $SIG{ALRM} = sub { die "timed out waiting for client\n" };
+    alarm 30;
+    my $client = $socket->accept; # Wait for a client
+    alarm 0;
+    close($socket);
+
+    # We want that the command talks over the TCP socket and takes
+    # ownership of it, so that when it closes it the connection is
+    # terminated, so we need to be able to close the socket. So we
+    # can't really use PVE::Tools::run_command().
+    my $pid = fork() // die "fork failed: $!\n";
+    if (!$pid) {
+	POSIX::dup2(fileno($client), 0);
+	POSIX::dup2(fileno($client), 1);
+	close($client);
+	exec {$cmd->[0]} @$cmd or do {
+	    warn "exec failed: $!\n";
+	    POSIX::_exit(1);
+	};
+    }
+
+    close($client);
+    if (waitpid($pid, 0) != $pid) {
+	kill(15 => $pid); # if we got interrupted terminate the child
+	my $count = 0;
+	while (waitpid($pid, POSIX::WNOHANG) != $pid) {
+	    usleep(100000);
+	    $count++;
+	    kill(9 => $pid), last if $count > 300; # 30 second timeout
+	}
+    }
+    if (my $sig = ($? & 127)) {
+	die "got signal $sig\n";
+    } elsif (my $exitcode = ($? >> 8)) {
+	die "exit code $exitcode\n";
+    }
+
+    return undef;
+}
+
 sub split_list {
     my $listtxt = shift || '';
 
-- 
2.11.0




More information about the pve-devel mailing list