|
| 1 | +# $OpenBSD: ssh-tty.sh,v 1.1 2025/10/20 00:45:10 djm Exp $ |
| 2 | +# Placed in the Public Domain. |
| 3 | + |
| 4 | +# Basic TTY smoke test |
| 5 | + |
| 6 | +tid="ssh-tty" |
| 7 | + |
| 8 | +# Fake home directory to avoid user shell configuration. |
| 9 | +FAKEHOME="$OBJ/.fakehome" |
| 10 | +rm -rf "$FAKEHOME" |
| 11 | +mkdir -m 0700 -p "$FAKEHOME" |
| 12 | + |
| 13 | +# tmux stuff |
| 14 | +TMUX=tmux |
| 15 | +test -x $TMUX || skip "tmux not found" |
| 16 | +CLEANENV="env -i HOME=$HOME LOGNAME=$USER USER=$USER PATH=$PATH SHELL=$SHELL" |
| 17 | +TMUX_TEST="$CLEANENV $TMUX -f/dev/null -Lopenssh-regress-ssh-tty" |
| 18 | +sess="regress-ssh-tty$$" |
| 19 | + |
| 20 | +# Multiplexing control socket. |
| 21 | +CTL=$OBJ/ctl-sock |
| 22 | + |
| 23 | +# Some randomish strings used for signalling back and forth. |
| 24 | +# We use the octal variants via printf(1). |
| 25 | +MAGIC1="XY23zzY" |
| 26 | +MAGIC1_OCTAL="\130\131\062\063\172\172\131" |
| 27 | +MAGIC2="99sMarT86" |
| 28 | +MAGIC2_OCTAL="\071\071\163\115\141\162\124\070\066" |
| 29 | +MAGIC3="woLF1701d" |
| 30 | +MAGIC3_OCTAL="\167\157\114\106\061\067\060\061\144" |
| 31 | +MAGIC4="lUh4thX4evR" |
| 32 | +MAGIC4_OCTAL="\154\125\150\064\164\150\130\064\145\166\122" |
| 33 | + |
| 34 | +# Wait for a mux process to become ready. |
| 35 | +wait_for_mux_ready() |
| 36 | +{ |
| 37 | + for i in 1 2 3 4 5 6 7 8 9; do |
| 38 | + ${SSH} -F $OBJ/ssh_config -S $CTL -Ocheck otherhost \ |
| 39 | + >/dev/null 2>&1 && return 0 |
| 40 | + sleep $i |
| 41 | + done |
| 42 | + fatal "mux never becomes ready" |
| 43 | +} |
| 44 | + |
| 45 | +# Wait for a mux process to have finished. |
| 46 | +wait_for_mux_done() |
| 47 | +{ |
| 48 | + for i in 1 2 3 4 5 6 7 8 9; do |
| 49 | + test -S $CTL || return 0 |
| 50 | + sleep $i |
| 51 | + done |
| 52 | + fatal "mux socket never removed" |
| 53 | +} |
| 54 | + |
| 55 | +# Wait for a regex to appear in terminal output. |
| 56 | +wait_for_regex() { |
| 57 | + string="$1" |
| 58 | + errors_are_fatal="$2" |
| 59 | + for x in 1 2 3 4 5 6 7 8 9 10 ; do |
| 60 | + $TMUX_TEST capture-pane -pt $sess | grep "$string" >/dev/null |
| 61 | + [ $? -eq 0 ] && return |
| 62 | + sleep 1 |
| 63 | + done |
| 64 | + if test -z "$errors_are_fatal"; then |
| 65 | + fail "failed to match \"$string\" in terminal output" |
| 66 | + return |
| 67 | + fi |
| 68 | + fatal "failed to match \"$string\" in terminal output" |
| 69 | +} |
| 70 | + |
| 71 | +# Check that a regex does *not* appear in terminal output |
| 72 | +not_in_term() { |
| 73 | + string="$1" |
| 74 | + error="$2" |
| 75 | + errors_are_fatal="$3" |
| 76 | + $TMUX_TEST capture-pane -pt $sess | grep "$string" > /dev/null |
| 77 | + [ $? -ne 0 ] && return |
| 78 | + if test -z "$errors_are_fatal"; then |
| 79 | + fail "$error" |
| 80 | + return |
| 81 | + fi |
| 82 | + fatal "$error" |
| 83 | +} |
| 84 | + |
| 85 | +trap "$TMUX_TEST kill-session -t $sess 2>/dev/null" EXIT |
| 86 | + |
| 87 | +run_test() { |
| 88 | + tag="$1" |
| 89 | + ssh_args="$2" |
| 90 | + # Prepare a tmux session. |
| 91 | + $TMUX_TEST kill-session -t $sess 2>/dev/null |
| 92 | + $TMUX_TEST new-session -d -s $sess |
| 93 | + #echo XXXXXXXXXX $sess; sleep 10 |
| 94 | + |
| 95 | + # Command to start SSH; sent as keystrokes to tmux session. |
| 96 | + RCMD="$CLEANENV $SHELL" |
| 97 | + CMD="$SSH -F $OBJ/ssh_proxy $ssh_args -S $CTL x -tt $RCMD" |
| 98 | + |
| 99 | + verbose "${tag}: start connection" |
| 100 | + # arrange for the shell to print something after ssh completes. |
| 101 | + $TMUX_TEST send-keys -t $sess "$CMD && printf '$MAGIC1_OCTAL\n'" ENTER |
| 102 | + wait_for_mux_ready |
| 103 | + |
| 104 | + verbose "${tag}: send string" |
| 105 | + $TMUX_TEST send-keys -t $sess "printf '$MAGIC2_OCTAL\n'" ENTER |
| 106 | + wait_for_regex "$MAGIC2" |
| 107 | + |
| 108 | + verbose "${tag}: ^c interrupts process" |
| 109 | + # ^c should interrupt the sleep and prevent the magic string |
| 110 | + # from appearing. |
| 111 | + $TMUX_TEST send-keys -t $sess "sleep 30 || printf '$MAGIC3_OCTAL\n'" |
| 112 | + $TMUX_TEST send-keys -t $sess ENTER |
| 113 | + $TMUX_TEST send-keys -t $sess "C-c" |
| 114 | + # send another string to let us know that the sleep has finished. |
| 115 | + $TMUX_TEST send-keys -t $sess "printf '$MAGIC4_OCTAL\n'" ENTER |
| 116 | + wait_for_regex "$MAGIC4" |
| 117 | + not_in_term "$MAGIC3" "^c did not interrupt" |
| 118 | + |
| 119 | + verbose "${tag}: ~? produces help" |
| 120 | + $TMUX_TEST send-keys -t $sess ENTER "~?" |
| 121 | + wait_for_regex "^Supported escape sequences:$" |
| 122 | + |
| 123 | + verbose "${tag}: ~. terminates session" |
| 124 | + $TMUX_TEST send-keys -t $sess ENTER "~." |
| 125 | + wait_for_mux_done |
| 126 | + not_in_term "$MAGIC1" "ssh unexpectedly exited successfully after ~." |
| 127 | + |
| 128 | + verbose "${tag}: restart session" |
| 129 | + $TMUX_TEST send-keys -t $sess "$CMD && printf '$MAGIC1_OCTAL\n'" ENTER |
| 130 | + wait_for_mux_ready |
| 131 | + |
| 132 | + verbose "${tag}: eof terminates session successfully" |
| 133 | + $TMUX_TEST send-keys -t $sess ENTER "C-d" |
| 134 | + wait_for_regex "$MAGIC1" |
| 135 | +} |
| 136 | + |
| 137 | +# Make sure tmux is working as expected before we start. |
| 138 | +$TMUX_TEST kill-session -t $sess 2>/dev/null |
| 139 | +$TMUX_TEST new-session -d -s $sess |
| 140 | +# Make sure the session doesn't contain the magic strings we will use |
| 141 | +# for signalling or any #? output. |
| 142 | +not_in_term "$MAGIC1" "terminal already contains magic1 string" fatal |
| 143 | +not_in_term "$MAGIC2" "terminal already contains magic2 string" fatal |
| 144 | +not_in_term "$MAGIC3" "terminal already contains magic3 string" fatal |
| 145 | +not_in_term "$MAGIC4" "terminal already contains magic4 string" fatal |
| 146 | +not_in_term "^Supported escape" "terminal already contains escape help" fatal |
| 147 | +$TMUX_TEST send-keys -t $sess "printf '$MAGIC1_OCTAL\n'" ENTER |
| 148 | +wait_for_regex "$MAGIC1" fatal |
| 149 | +$TMUX_TEST kill-session -t $sess 2>/dev/null |
| 150 | + |
| 151 | +run_test "basic" "-oControlMaster=yes" |
| 152 | +run_test "ControlPersist" "-oControlMaster=auto -oControlPersist=1s" |
0 commit comments