Fix pause module so it does not stack trace when redirecting stdout. (#42217)
* Use separate variables for stdin and stdout file descriptors * Do not set stdout to raw mode when output is not a TTY
This commit is contained in:
parent
39ec12f395
commit
1d1595b990
3 changed files with 34 additions and 18 deletions
2
changelogs/fragments/pause-stdout-redirection.yaml
Normal file
2
changelogs/fragments/pause-stdout-redirection.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
bugfixes:
|
||||||
|
- pause - do not set stdout to raw mode when redirecting to a file (https://github.com/ansible/ansible/issues/41717)
|
|
@ -143,7 +143,7 @@ class ActionModule(ActionBase):
|
||||||
result['start'] = to_text(datetime.datetime.now())
|
result['start'] = to_text(datetime.datetime.now())
|
||||||
result['user_input'] = b''
|
result['user_input'] = b''
|
||||||
|
|
||||||
fd = None
|
stdin_fd = None
|
||||||
old_settings = None
|
old_settings = None
|
||||||
try:
|
try:
|
||||||
if seconds is not None:
|
if seconds is not None:
|
||||||
|
@ -167,7 +167,8 @@ class ActionModule(ActionBase):
|
||||||
|
|
||||||
# save the attributes on the existing (duped) stdin so
|
# save the attributes on the existing (duped) stdin so
|
||||||
# that we can restore them later after we set raw mode
|
# that we can restore them later after we set raw mode
|
||||||
fd = None
|
stdin_fd = None
|
||||||
|
stdout_fd = None
|
||||||
try:
|
try:
|
||||||
if PY3:
|
if PY3:
|
||||||
stdin = self._connection._new_stdin.buffer
|
stdin = self._connection._new_stdin.buffer
|
||||||
|
@ -175,52 +176,59 @@ class ActionModule(ActionBase):
|
||||||
else:
|
else:
|
||||||
stdin = self._connection._new_stdin
|
stdin = self._connection._new_stdin
|
||||||
stdout = sys.stdout
|
stdout = sys.stdout
|
||||||
fd = stdin.fileno()
|
stdin_fd = stdin.fileno()
|
||||||
|
stdout_fd = stdout.fileno()
|
||||||
except (ValueError, AttributeError):
|
except (ValueError, AttributeError):
|
||||||
# ValueError: someone is using a closed file descriptor as stdin
|
# ValueError: someone is using a closed file descriptor as stdin
|
||||||
# AttributeError: someone is using a null file descriptor as stdin on windoez
|
# AttributeError: someone is using a null file descriptor as stdin on windoez
|
||||||
stdin = None
|
stdin = None
|
||||||
|
|
||||||
if fd is not None:
|
if stdin_fd is not None:
|
||||||
if isatty(fd):
|
if isatty(stdin_fd):
|
||||||
|
|
||||||
# grab actual Ctrl+C sequence
|
# grab actual Ctrl+C sequence
|
||||||
try:
|
try:
|
||||||
intr = termios.tcgetattr(fd)[6][termios.VINTR]
|
intr = termios.tcgetattr(stdin_fd)[6][termios.VINTR]
|
||||||
except Exception:
|
except Exception:
|
||||||
# unsupported/not present, use default
|
# unsupported/not present, use default
|
||||||
intr = b'\x03' # value for Ctrl+C
|
intr = b'\x03' # value for Ctrl+C
|
||||||
|
|
||||||
# get backspace sequences
|
# get backspace sequences
|
||||||
try:
|
try:
|
||||||
backspace = termios.tcgetattr(fd)[6][termios.VERASE]
|
backspace = termios.tcgetattr(stdin_fd)[6][termios.VERASE]
|
||||||
except Exception:
|
except Exception:
|
||||||
backspace = [b'\x7f', b'\x08']
|
backspace = [b'\x7f', b'\x08']
|
||||||
|
|
||||||
old_settings = termios.tcgetattr(fd)
|
old_settings = termios.tcgetattr(stdin_fd)
|
||||||
tty.setraw(fd)
|
tty.setraw(stdin_fd)
|
||||||
tty.setraw(stdout.fileno())
|
|
||||||
|
# Only set stdout to raw mode if it is a TTY. This is needed when redirecting
|
||||||
|
# stdout to a file since a file cannot be set to raw mode.
|
||||||
|
if isatty(stdout_fd):
|
||||||
|
tty.setraw(stdout_fd)
|
||||||
|
|
||||||
# Only echo input if no timeout is specified
|
# Only echo input if no timeout is specified
|
||||||
if not seconds and echo:
|
if not seconds and echo:
|
||||||
new_settings = termios.tcgetattr(fd)
|
new_settings = termios.tcgetattr(stdin_fd)
|
||||||
new_settings[3] = new_settings[3] | termios.ECHO
|
new_settings[3] = new_settings[3] | termios.ECHO
|
||||||
termios.tcsetattr(fd, termios.TCSANOW, new_settings)
|
termios.tcsetattr(stdin_fd, termios.TCSANOW, new_settings)
|
||||||
|
|
||||||
# flush the buffer to make sure no previous key presses
|
# flush the buffer to make sure no previous key presses
|
||||||
# are read in below
|
# are read in below
|
||||||
termios.tcflush(stdin, termios.TCIFLUSH)
|
termios.tcflush(stdin, termios.TCIFLUSH)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if fd is not None:
|
if stdin_fd is not None:
|
||||||
|
|
||||||
key_pressed = stdin.read(1)
|
key_pressed = stdin.read(1)
|
||||||
|
|
||||||
if key_pressed == intr: # value for Ctrl+C
|
if key_pressed == intr: # value for Ctrl+C
|
||||||
clear_line(stdout)
|
clear_line(stdout)
|
||||||
raise KeyboardInterrupt
|
raise KeyboardInterrupt
|
||||||
|
|
||||||
if not seconds:
|
if not seconds:
|
||||||
if fd is None or not isatty(fd):
|
if stdin_fd is None or not isatty(stdin_fd):
|
||||||
display.warning("Not waiting for response to prompt as stdin is not interactive")
|
display.warning("Not waiting for response to prompt as stdin is not interactive")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -255,9 +263,9 @@ class ActionModule(ActionBase):
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
# cleanup and save some information
|
# cleanup and save some information
|
||||||
# restore the old settings for the duped stdin fd
|
# restore the old settings for the duped stdin stdin_fd
|
||||||
if not(None in (fd, old_settings)) and isatty(fd):
|
if not(None in (stdin_fd, old_settings)) and isatty(stdin_fd):
|
||||||
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
termios.tcsetattr(stdin_fd, termios.TCSADRAIN, old_settings)
|
||||||
|
|
||||||
duration = time.time() - start
|
duration = time.time() - start
|
||||||
result['stop'] = to_text(datetime.datetime.now())
|
result['stop'] = to_text(datetime.datetime.now())
|
||||||
|
|
|
@ -15,6 +15,12 @@ ansible-playbook test-pause-no-tty.yml -i ../../inventory 2>&1 | \
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# Test redirecting stdout
|
||||||
|
# Issue #41717
|
||||||
|
ansible-playbook pause-3.yml -i ../../inventory > /dev/null \
|
||||||
|
&& echo "Successfully redirected stdout" \
|
||||||
|
|| echo "Failure when attempting to redirect stdout"
|
||||||
|
|
||||||
# Test pause with seconds and minutes specified
|
# Test pause with seconds and minutes specified
|
||||||
ansible-playbook test-pause.yml -i ../../inventory "$@"
|
ansible-playbook test-pause.yml -i ../../inventory "$@"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue