From 47280f63da4ba9d463f9738563f69d4c11bbcc71 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Wed, 18 Aug 2021 13:57:12 +0100 Subject: [PATCH] connection: Fix race in ParamikoBackgroundCommand API When using an ssh background command, the data is read from paramiko as it comes and stored in a buffering pipe by a thread. Currently, the ParamikoBackgroundCommand API will report the command as having completed as soon as paramiko reports it. This however does not necessarily mean that the pipe-filling thread is finished copying the streams, and the client will end up assuming there is no more data to read even though it's not the case. Fix that by ensuring that when poll() returns non-None, the output streams are ready to be drained. --- devlib/connection.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/devlib/connection.py b/devlib/connection.py index e5c4b2e..ecc3071 100644 --- a/devlib/connection.py +++ b/devlib/connection.py @@ -266,10 +266,20 @@ class ParamikoBackgroundCommand(BackgroundCommand): return self._pid def wait(self): - return self.chan.recv_exit_status() + status = self.chan.recv_exit_status() + # Ensure that the redirection thread is finished copying the content + # from paramiko to the pipe. + self.redirect_thread.join() + return status def poll(self): - if self.chan.exit_status_ready(): + # Wait for the redirection thread to finish, otherwise we would + # indicate the caller that the command is finished and that the streams + # are safe to drain, but actually the redirection thread is not + # finished yet, which would end up in lost data. + if self.redirect_thread.is_alive(): + return None + elif self.chan.exit_status_ready(): return self.wait() else: return None