Interaction with remote programs¶
Fabric’s primary operations, run
and
sudo
, are capable of sending local input to the remote
end, in a manner nearly identical to the ssh
program. For example, programs
which display password prompts (e.g. a database dump utility, or changing a
user’s password) will behave just as if you were interacting with them
directly.
However, as with ssh
itself, Fabric’s implementation of this feature is
subject to a handful of limitations which are not always intuitive. This
document discusses such issues in detail.
Note
Readers unfamiliar with the basics of Unix stdout and stderr pipes, and/or terminal devices, may wish to visit the Wikipedia pages for Unix pipelines and Pseudo terminals respectively.
Combining stdout and stderr¶
The first issue to be aware of is that of the stdout and stderr streams, and why they are separated or combined as needed.
Buffering¶
Fabric 0.9.x and earlier, and Python itself, buffer output on a line-by-line basis: text is not printed to the user until a newline character is found. This works fine in most situations but becomes problematic when one needs to deal with partial-line output such as prompts.
Note
Line-buffered output can make programs appear to halt or freeze for no reason, as prompts print out text without a newline, waiting for the user to enter their input and press Return.
Newer Fabric versions buffer both input and output on a character-by-character
basis in order to make interaction with prompts possible. This has the
convenient side effect of enabling interaction with complex programs utilizing
the “curses” libraries or which otherwise redraw the screen (think top
).
Crossing the streams¶
Unfortunately, printing to stderr and stdout simultaneously (as many programs do) means that when the two streams are printed independently one byte at a time, they can become garbled or meshed together. While this can sometimes be mitigated by line-buffering one of the streams and not the other, it’s still a serious issue.
To solve this problem, Fabric uses a setting in our SSH layer which merges the
two streams at a low level and causes output to appear more naturally. This
setting is represented in Fabric as the combine_stderr env var and
keyword argument, and is True
by default.
Due to this default setting, output will appear correctly, but at the
cost of an empty .stderr
attribute on the return values of
run
/sudo
, as all output will appear
to be stdout.
Conversely, users requiring a distinct stderr stream at the Python level and
who aren’t bothered by garbled user-facing output (or who are hiding stdout and
stderr from the command in question) may opt to set this to False
as
needed.
Pseudo-terminals¶
The other main issue to consider when presenting interactive prompts to users is that of echoing the user’s own input.
Echoes¶
Typical terminal applications or bona fide text terminals (e.g. when using a Unix system without a running GUI) present programs with a terminal device called a tty or pty (for pseudo-terminal). These automatically echo all text typed into them back out to the user (via stdout), as interaction without seeing what you had just typed would be difficult. Terminal devices are also able to conditionally turn off echoing, allowing secure password prompts.
However, it’s possible for programs to be run without a tty or pty present at all (consider cron jobs, for example) and in this situation, any stdin data being fed to the program won’t be echoed. This is desirable for programs being run without any humans around, and it’s also Fabric’s old default mode of operation.
Fabric’s approach¶
Unfortunately, in the context of executing commands via Fabric, when no pty is present to echo a user’s stdin, Fabric must echo it for them. This is sufficient for many applications, but it presents problems for password prompts, which become insecure.
In the interests of security and meeting the principle of least surprise (insofar as users are typically expecting things to behave as they would when run in a terminal emulator), Fabric 1.0 and greater force a pty by default. With a pty enabled, Fabric simply allows the remote end to handle echoing or hiding of stdin and does not echo anything itself.
Note
In addition to allowing normal echo behavior, a pty also means programs
that behave differently when attached to a terminal device will then do so.
For example, programs that colorize output on terminals but not when run in
the background will print colored output. Be wary of this if you inspect
the return value of run
or sudo
!
For situations requiring the pty behavior turned off, the --no-pty
command-line argument and always_use_pty env var may be used.
Combining the two¶
As a final note, keep in mind that use of pseudo-terminals effectively implies combining stdout and stderr – in much the same way as the combine_stderr setting does. This is because a terminal device naturally sends both stdout and stderr to the same place – the user’s display – thus making it impossible to differentiate between them.
However, at the Fabric level, the two groups of settings are distinct from one
another and may be combined in various ways. The default is for both to be set
to True
; the other combinations are as follows:
run("cmd", pty=False, combine_stderr=True)
: will cause Fabric to echo all stdin itself, including passwords, as well as potentially alteringcmd
’s behavior. Useful ifcmd
behaves undesirably when run under a pty and you’re not concerned about password prompts.run("cmd", pty=False, combine_stderr=False)
: with both settingsFalse
, Fabric will echo stdin and won’t issue a pty – and this is highly likely to result in undesired behavior for all but the simplest commands. However, it is also the only way to access a distinct stderr stream, which is occasionally useful.run("cmd", pty=True, combine_stderr=False)
: valid, but won’t really make much of a difference, aspty=True
will still result in merged streams. May be useful for avoiding any edge case problems incombine_stderr
(none are presently known).