|
23 | 23 | """
|
24 | 24 | from .base import XmlServiceTransport
|
25 | 25 |
|
| 26 | +import socket |
| 27 | + |
26 | 28 | __all__ = [
|
27 | 29 | 'SshTransport'
|
28 | 30 | ]
|
@@ -75,33 +77,45 @@ def _call(self, tk):
|
75 | 77 | """
|
76 | 78 | command = "/QOpenSys/pkgs/bin/xmlservice-cli"
|
77 | 79 | stdin, stdout, stderr = self.conn.exec_command(command)
|
| 80 | + channel = stdout.channel |
| 81 | + |
78 | 82 | xml_in = tk.xml_in()
|
79 | 83 | stdin.write(xml_in.encode())
|
80 |
| - stdin.flush() |
81 |
| - stdin.channel.shutdown_write() |
| 84 | + stdin.close() |
| 85 | + channel.shutdown_write() |
| 86 | + |
| 87 | + # Disable blocking I/O |
| 88 | + # chan.settimeout(0.0) is equivalent to chan.setblocking(0) |
| 89 | + # https://docs.paramiko.org/en/stable/api/channel.html#paramiko.channel.Channel.settimeout |
| 90 | + channel.settimeout(0.0) |
82 | 91 |
|
83 | 92 | # rather than doing all this loop-de-loop, we could instead just use
|
84 | 93 | # a single call to stdout.readlines(), but there is a remote
|
85 | 94 | # possibility that the process is hanging writing to a filled-up
|
86 |
| - # stderr pipe. So, we read a little from both until we're all done |
| 95 | + # stderr pipe. So, we read from both until we're all done |
87 | 96 | err_out = b""
|
88 | 97 | xml_out = b""
|
89 |
| - blockSize = 64 # arbitrary |
90 |
| - while not stderr.closed or not stdout.closed: |
91 |
| - if not stderr.closed: |
92 |
| - newData = stderr.read(blockSize) |
93 |
| - if not newData: |
94 |
| - stderr.close() # reaching EOF doesn't implicitly close |
95 |
| - else: |
96 |
| - err_out += newData |
97 |
| - if not stdout.closed: |
98 |
| - newData = stdout.read(blockSize) |
99 |
| - if not newData: |
100 |
| - stdout.close() # reaching EOF doesn't implicitly close |
101 |
| - else: |
102 |
| - xml_out += newData |
103 |
| - stdout.channel.close() |
104 |
| - stderr.channel.close() |
| 98 | + |
| 99 | + # Convenience wrapper for reading data from stdout/stderr |
| 100 | + # Returns empty binary string if EOF *or* timeout (no data) |
| 101 | + # Closes file on EOF |
| 102 | + def read_data(f): |
| 103 | + if f.closed: |
| 104 | + return b"" |
| 105 | + |
| 106 | + try: |
| 107 | + data = f.read() |
| 108 | + if not data: |
| 109 | + f.close() |
| 110 | + return data |
| 111 | + except socket.timeout: |
| 112 | + return b"" |
| 113 | + |
| 114 | + while not all((stdout.closed, stderr.closed)): |
| 115 | + xml_out += read_data(stdout) |
| 116 | + err_out += read_data(stderr) |
| 117 | + |
| 118 | + channel.close() |
105 | 119 | return xml_out
|
106 | 120 |
|
107 | 121 | def _close(self):
|
|
0 commit comments