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