forked from fabric/fabric
-
Notifications
You must be signed in to change notification settings - Fork 0
/
io.py
132 lines (121 loc) · 5.11 KB
/
io.py
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
from __future__ import with_statement
import sys
import time
from select import select
from fabric.context_managers import settings, char_buffered
from fabric.state import env, output, win32, io_sleep
from fabric.auth import get_password, set_password
import fabric.network
if win32:
import msvcrt
def _flush(pipe, text):
pipe.write(text)
pipe.flush()
def _endswith(char_list, substring):
tail = char_list[-1 * len(substring):]
substring = list(substring)
return tail == substring
def output_loop(chan, which, capture):
# Obtain stdout or stderr related values
func = getattr(chan, which)
if which == 'recv':
prefix = "out"
pipe = sys.stdout
else:
prefix = "err"
pipe = sys.stderr
printing = getattr(output, 'stdout' if (which == 'recv') else 'stderr')
# Initialize loop variables
reprompt = False
initial_prefix_printed = False
while True:
# Handle actual read/write
byte = func(1)
if byte == '':
break
# A None capture variable implies that we're in open_shell()
if 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.
_flush(sys.stdout, byte)
# Otherwise, we're in run/sudo and need to handle capturing and
# prompts.
else:
_prefix = "[%s] %s: " % (env.host_string, prefix)
# Allow prefix to be turned off.
if not env.output_prefix:
_prefix = ""
# Print to user
if printing:
# Initial prefix
if not initial_prefix_printed:
_flush(pipe, _prefix)
initial_prefix_printed = True
# Byte itself
_flush(pipe, byte)
# Trailing prefix to start off next line
if byte in ("\n", "\r"):
_flush(pipe, _prefix)
# Store in capture buffer
capture += byte
# Handle prompts
prompt = _endswith(capture, env.sudo_prompt)
try_again = (_endswith(capture, env.again_prompt + '\n')
or _endswith(capture, env.again_prompt + '\r\n'))
if prompt:
# Obtain cached password, if any
password = get_password()
# 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 capture[-1 * len(env.sudo_prompt):]
# If the password we just tried was bad, prompt the user again.
if (not password) or 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 printing:
_flush(pipe, _prefix)
if reprompt:
_flush(pipe, env.again_prompt + '\n' + _prefix)
_flush(pipe, 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.
chan.input_enabled = False
password = fabric.network.prompt_for_password(
prompt=" ", no_colon=True, stream=pipe
)
chan.input_enabled = True
# Update env.password, env.passwords if necessary
set_password(password)
# Reset reprompt flag
reprompt = False
# Send current password down the pipe
chan.sendall(password + '\n')
elif try_again:
# Remove text from capture buffer
capture = capture[:len(env.again_prompt)]
# Set state so we re-prompt the user at the next prompt.
reprompt = True
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(io_sleep)