/usr/lib/python2.7/dist-packages/fabric/io.py is in fabric 1.10.2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | from __future__ import with_statement
import sys
import time
import re
import socket
from select import select
from fabric.state import env, output, win32
from fabric.auth import get_password, set_password
import fabric.network
from fabric.network import ssh, normalize
from fabric.utils import RingBuffer
from fabric.exceptions import CommandTimeout
if win32:
import msvcrt
def _endswith(char_list, substring):
tail = char_list[-1 * len(substring):]
substring = list(substring)
return tail == substring
def _has_newline(bytelist):
return '\r' in bytelist or '\n' in bytelist
def output_loop(*args, **kwargs):
OutputLooper(*args, **kwargs).loop()
class OutputLooper(object):
def __init__(self, chan, attr, stream, capture, timeout):
self.chan = chan
self.stream = stream
self.capture = capture
self.timeout = timeout
self.read_func = getattr(chan, attr)
self.prefix = "[%s] %s: " % (
env.host_string,
"out" if attr == 'recv' else "err"
)
self.printing = getattr(output, 'stdout' if (attr == 'recv') else 'stderr')
self.linewise = (env.linewise or env.parallel)
self.reprompt = False
self.read_size = 4096
self.write_buffer = RingBuffer([], maxlen=len(self.prefix))
def _flush(self, text):
self.stream.write(text)
# Actually only flush if not in linewise mode.
# When linewise is set (e.g. in parallel mode) flushing makes
# doubling-up of line prefixes, and other mixed output, more likely.
if not env.linewise:
self.stream.flush()
self.write_buffer.extend(text)
def loop(self):
"""
Loop, reading from <chan>.<attr>(), writing to <stream> and buffering to <capture>.
Will raise `~fabric.exceptions.CommandTimeout` if network timeouts
continue to be seen past the defined ``self.timeout`` threshold.
(Timeouts before then are considered part of normal short-timeout fast
network reading; see Fabric issue #733 for background.)
"""
# Internal capture-buffer-like buffer, used solely for state keeping.
# Unlike 'capture', nothing is ever purged from this.
_buffer = []
# Initialize loop variables
initial_prefix_printed = False
seen_cr = False
line = []
# Allow prefix to be turned off.
if not env.output_prefix:
self.prefix = ""
start = time.time()
while True:
# Handle actual read
try:
bytelist = self.read_func(self.read_size)
except socket.timeout:
elapsed = time.time() - start
if self.timeout is not None and elapsed > self.timeout:
raise CommandTimeout(timeout=self.timeout)
continue
# Empty byte == EOS
if bytelist == '':
# If linewise, ensure we flush any leftovers in the buffer.
if self.linewise and line:
self._flush(self.prefix)
self._flush("".join(line))
break
# A None capture variable implies that we're in open_shell()
if self.capture is None:
# Just print directly -- no prefixes, no capturing, nada
# And since we know we're using a pty in this mode, just go
# straight to stdout.
self._flush(bytelist)
# Otherwise, we're in run/sudo and need to handle capturing and
# prompts.
else:
# Print to user
if self.printing:
printable_bytes = bytelist
# Small state machine to eat \n after \r
if printable_bytes[-1] == "\r":
seen_cr = True
if printable_bytes[0] == "\n" and seen_cr:
printable_bytes = printable_bytes[1:]
seen_cr = False
while _has_newline(printable_bytes) and printable_bytes != "":
# at most 1 split !
cr = re.search("(\r\n|\r|\n)", printable_bytes)
if cr is None:
break
end_of_line = printable_bytes[:cr.start(0)]
printable_bytes = printable_bytes[cr.end(0):]
if not initial_prefix_printed:
self._flush(self.prefix)
if _has_newline(end_of_line):
end_of_line = ''
if self.linewise:
self._flush("".join(line) + end_of_line + "\n")
line = []
else:
self._flush(end_of_line + "\n")
initial_prefix_printed = False
if self.linewise:
line += [printable_bytes]
else:
if not initial_prefix_printed:
self._flush(self.prefix)
initial_prefix_printed = True
self._flush(printable_bytes)
# Now we have handled printing, handle interactivity
read_lines = re.split(r"(\r|\n|\r\n)", bytelist)
for fragment in read_lines:
# Store in capture buffer
self.capture += fragment
# Store in internal buffer
_buffer += fragment
# Handle prompts
expected, response = self._get_prompt_response()
if expected:
del self.capture[-1 * len(expected):]
self.chan.sendall(str(response) + '\n')
else:
prompt = _endswith(self.capture, env.sudo_prompt)
try_again = (_endswith(self.capture, env.again_prompt + '\n')
or _endswith(self.capture, env.again_prompt + '\r\n'))
if prompt:
self.prompt()
elif try_again:
self.try_again()
# Print trailing new line if the last thing we printed was our line
# prefix.
if self.prefix and "".join(self.write_buffer) == self.prefix:
self._flush('\n')
def prompt(self):
# Obtain cached password, if any
password = get_password(*normalize(env.host_string))
# Remove the prompt itself from the capture buffer. This is
# backwards compatible with Fabric 0.9.x behavior; the user
# will still see the prompt on their screen (no way to avoid
# this) but at least it won't clutter up the captured text.
del self.capture[-1 * len(env.sudo_prompt):]
# If the password we just tried was bad, prompt the user again.
if (not password) or self.reprompt:
# Print the prompt and/or the "try again" notice if
# output is being hidden. In other words, since we need
# the user's input, they need to see why we're
# prompting them.
if not self.printing:
self._flush(self.prefix)
if self.reprompt:
self._flush(env.again_prompt + '\n' + self.prefix)
self._flush(env.sudo_prompt)
# Prompt for, and store, password. Give empty prompt so the
# initial display "hides" just after the actually-displayed
# prompt from the remote end.
self.chan.input_enabled = False
password = fabric.network.prompt_for_password(
prompt=" ", no_colon=True, stream=self.stream
)
self.chan.input_enabled = True
# Update env.password, env.passwords if necessary
user, host, port = normalize(env.host_string)
set_password(user, host, port, password)
# Reset reprompt flag
self.reprompt = False
# Send current password down the pipe
self.chan.sendall(password + '\n')
def try_again(self):
# Remove text from capture buffer
self.capture = self.capture[:len(env.again_prompt)]
# Set state so we re-prompt the user at the next prompt.
self.reprompt = True
def _get_prompt_response(self):
"""
Iterate through the request prompts dict and return the response and
original request if we find a match
"""
for tup in env.prompts.iteritems():
if _endswith(self.capture, tup[0]):
return tup
return None, None
def input_loop(chan, using_pty):
while not chan.exit_status_ready():
if win32:
have_char = msvcrt.kbhit()
else:
r, w, x = select([sys.stdin], [], [], 0.0)
have_char = (r and r[0] == sys.stdin)
if have_char and chan.input_enabled:
# Send all local stdin to remote end's stdin
byte = msvcrt.getch() if win32 else sys.stdin.read(1)
chan.sendall(byte)
# Optionally echo locally, if needed.
if not using_pty and env.echo_stdin:
# Not using fastprint() here -- it prints as 'user'
# output level, don't want it to be accidentally hidden
sys.stdout.write(byte)
sys.stdout.flush()
time.sleep(ssh.io_sleep)
|