xref: /freebsd/crypto/openssh/regress/ssh-tty.sh (revision 2574974648c68c738aec3ff96644d888d7913a37)
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