summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2024-08-22 17:47:17 -0400
committerAndrew Cady <d@jerkface.net>2024-08-22 17:47:17 -0400
commita0105a3c44b260f26f362a146c9188f28cf6220a (patch)
tree47b70c8f4a480fb00b8f0a7cd85d1fb7eca59899
parent127e4232975824fe88efbdb8552b4d6e263b8406 (diff)
introduced lock on screen panes to prevent races
-rwxr-xr-xsrc/twopane.bash175
1 files changed, 101 insertions, 74 deletions
diff --git a/src/twopane.bash b/src/twopane.bash
index 1ed840b..59077fe 100755
--- a/src/twopane.bash
+++ b/src/twopane.bash
@@ -1,6 +1,6 @@
1#!/bin/bash 1#!/bin/bash
2BASH_ARGV0=twopane.bash 2BASH_ARGV0=twopane.bash
3echo -n "$0" >/proc/$$/comm 3echo -n "$0" >/proc/$BASHPID/comm
4set -e 4set -e
5set -f 5set -f
6set -o pipefail 6set -o pipefail
@@ -8,17 +8,26 @@ shopt -s lastpipe
8 8
9. read_chars.bash 9. read_chars.bash
10 10
11kill_tty_forward()
12{
13 read pid < "$TWOPANE"/tty_forward.pid
14 /bin/kill "${1:-'-INT'}" -- -$pid 2>/dev/null
15}
16
11BOT_SIZE=8 17BOT_SIZE=8
12BOT_TITLE=input 18BOT_TITLE=input
13 19
14if [ $# = 0 ] 20if [ $# = 0 ]
15then 21then
16 TOP_CMD="$SHELL -i" 22 TOP_CMD="$SHELL -i"
17 BOT_CMD=foreground 23 BOT_CMD=start
18 #BOT_CMD=start
19 #TOP_EXIT=quit 24 #TOP_EXIT=quit
20 #TOP_EXIT=prompt 25 TOP_EXIT=prompt
21 TOP_EXIT=restart 26 #TOP_EXIT=restart
27elif [ "$TWOPANE" -a "$*" = detach ]
28then
29 kill_tty_forward -TSTP
30 exit
22else 31else
23 TOP_CMD=$* 32 TOP_CMD=$*
24 BOT_CMD=start 33 BOT_CMD=start
@@ -75,26 +84,41 @@ screen -ln -t "$BOT_TITLE" 0 bash --noprofile --rcfile "$TWOPANE"/bashrc -i
75layout save 0 84layout save 0
76. 85.
77 86
87with_screen_pane()
88{
89 (( $# > 1 )) || return
90 declare -i PANE="$1" LOCK
91 (( PANE > 0 )) || return
92 (
93 flock $LOCK
94 "$2" "$1" "${@:3}"
95 ) {LOCK}>"$TWOPANE"/pane$PANE
96}
97
78check_screen_pane() 98check_screen_pane()
79{ 99{
80 screen -p "$pane" -Q info >/dev/null 100 (( PANE > 0 )) || return
101 screen -p "$PANE" -Q info >/dev/null
81} 102}
82 103
83kill_screen_pane() 104kill_screen_pane()
84{ 105{
85 while check_screen_pane 106 (( PANE > 0 )) || return
107 while check_screen_pane "$PANE"
86 do 108 do
87 screen -p "$pane" -X kill 109 screen -p "$PANE" -X kill
88 done 110 done
89} 111}
90 112
91start_screen_pane() 113start_screen_pane()
92{ 114{
93 declare -i pane="${1:-1}" 115 (( PANE > 0 )) || return
94 (( pane > 0 )) || return 116 if check_screen_pane "$PANE"
95 if check_screen_pane "$pane"
96 then 117 then
118 i "pane already running: $PANE"
97 return 119 return
120 else
121 i "starting pane: $PANE"
98 fi 122 fi
99 shift 123 shift
100 if [ $# = 0 ] 124 if [ $# = 0 ]
@@ -105,49 +129,55 @@ start_screen_pane()
105 TOP_TITLE="Command: ${*@Q}" 129 TOP_TITLE="Command: ${*@Q}"
106 screen -X focus top 130 screen -X focus top
107 screen -X screen -ln -t "$TOP_TITLE" 1 "$@" 131 screen -X screen -ln -t "$TOP_TITLE" 1 "$@"
108 start_screen_pane_subprocess "$pane" 132 start_screen_pane_subprocess
109} 133}
110 134
111start_screen_pane_subprocess() 135start_screen_pane_subprocess()
112{ 136{
113 declare -i pane="${1:-1}" 137 (( PANE > 0 )) || return
114 subproc_command=( 138 subproc_command=(
115 exec -a top-pane-tty-forward 139 exec -a top-pane-tty-forward
116 socat 140 socat
117 UNIX-LISTEN:"$TWOPANE"/socket 141 UNIX-LISTEN:"$TWOPANE"/socket
118 STDIN,cfmakeraw,opost=1,onlcr=1!!STDOUT 142 STDIN,cfmakeraw,opost=1,onlcr=1!!STDOUT
119 ) 143 )
120 screen -p "$pane" -X exec .!. bash -c "${subproc_command[*]}" 144 screen -p "$PANE" -X exec .!. bash -c "${subproc_command[*]}"
121} 145}
122 146
123restart_screen_pane() 147restart_pane()
124{ 148{
149 (( PANE > 0 )) || return
125 kill_screen_pane 150 kill_screen_pane
126 start_screen_pane "$@" 151 start_screen_pane "$@"
127} 152}
128 153
154restart_top_pane()
155{
156 with_screen_pane 1 restart_pane "$@"
157}
158
129connect_coproc() 159connect_coproc()
130{ 160{
161 [ $# -gt 0 ] || set -- COPROC
131 declare -n coproc="$1" 162 declare -n coproc="$1"
132 shift 163 shift
164 [ $# -gt 0 ] || set -- "${!coproc}"
133 declare -n std1="${!coproc}_STDOUT" 165 declare -n std1="${!coproc}_STDOUT"
134 declare -n std0="${!coproc}_STDIN" 166 declare -n std0="${!coproc}_STDIN"
135 declare -n opid="${!coproc}_PID" 167 declare -n opid="${!coproc}_PID"
136
137 if [ $# = 0 ]
138 then
139 set -- "${!coproc}"
140 fi
141 if ! [ "$opid" ] 168 if ! [ "$opid" ]
142 then 169 then
170 i "coproc starting: ${!coproc}"
143 local STDERR 171 local STDERR
144 { 172 {
145 coproc "${!coproc}" \ 173 coproc "${!coproc}" \
146 { 174 {
147 "$@" 175 "$@"
148 } 2>&$STDERR {STDERR}>&- 176 } 2>&$STDERR {STDERR}>&-
177 disown "%coproc ${!coproc} "
149 } {STDERR}>&2 2>/dev/null 178 } {STDERR}>&2 2>/dev/null
150 disown 179 else
180 i "coproc already running: ${!coproc}"
151 fi 181 fi
152 # The copied file descriptors (unlike the original coprocess 182 # The copied file descriptors (unlike the original coprocess
153 # file descriptors, in ${coproc[0]} and ${coproc[1]}) can be 183 # file descriptors, in ${coproc[0]} and ${coproc[1]}) can be
@@ -160,7 +190,7 @@ connect_coproc()
160 190
161disconnect_coproc() 191disconnect_coproc()
162{ 192{
163 [ $# = 1 ] || return 193 [ $# -gt 0 ] || set -- COPROC
164 declare -n coproc="$1" 194 declare -n coproc="$1"
165 declare -n std0="${!coproc}_STDIN" 195 declare -n std0="${!coproc}_STDIN"
166 declare -n std1="${!coproc}_STDOUT" 196 declare -n std1="${!coproc}_STDOUT"
@@ -212,46 +242,21 @@ start()
212 foreground "$@" 242 foreground "$@"
213} 243}
214 244
215check_tty_reader()
216{
217 [ "$TTY_FORWARD_PID" ] || return
218 TTY_FORWARD_JOBSPEC=$(jobs -sl | pid_to_jobspec "$TTY_FORWARD_PID")
219 [ "$TTY_FORWARD_JOBSPEC" ]
220}
221
222pid_to_jobspec()
223{
224 while read jspec pid status cmd
225 do
226 [ "$pid" = "$1" ] || continue
227 jspec=${jspec%]*}
228 jspec=%${jspec#[}
229 echo $jspec
230 return
231 done
232 false
233}
234
235tty_forward() 245tty_forward()
236{ 246{
237 printf '%d\n' "$BASHPID" > "$TWOPANE"/tty_forward.pid 247 printf '%d\n' "$BASHPID" > "$TWOPANE"/tty_forward.pid
238 read-tty 248 read-tty
239} 249}
240 250
241kill_tty_forward()
242{
243 read pid < "$TWOPANE"/tty_forward.pid
244 /bin/kill -INT -- -$pid 2>/dev/null
245}
246
247TOP_PANE() 251TOP_PANE()
248{ 252{
249 (exec -a bottom-pane-tty-forward socat - UNIX-CONNECT:"$TWOPANE"/socket,forever) 253 (exec -a bottom-pane-tty-forward socat - UNIX-CONNECT:"$TWOPANE"/socket,forever)
250 kill_tty_forward 254 x kill_tty_forward
251 case "$TOP_EXIT" in 255 case "$TOP_EXIT" in
252 restart ) 256 restart )
253 start_screen_pane 1 257 # x kill -USR1 $$
254 kill -USR1 $$ 258 x kill -INT $$
259 with_screen_pane 1 start_screen_pane "$@"
255 ;; 260 ;;
256 quit ) 261 quit )
257 kill -INT $$ 262 kill -INT $$
@@ -263,11 +268,13 @@ TOP_PANE()
263 esac 268 esac
264} 269}
265 270
266sink() 271connect_sink()
267{ 272{
273 [ "$1" ] || return
268 declare -n fd="$1" 274 declare -n fd="$1"
269 declare -n pid="${!fd}_PID" 275 declare -n pid="${!fd}_PID"
270 disconnect_sink "$fd" 276 disconnect_sink "$fd"
277 i "sink starting: ${!fd}"
271 exec {fd}> >("${@:2}") 278 exec {fd}> >("${@:2}")
272 pid=$! 279 pid=$!
273} 280}
@@ -278,6 +285,7 @@ disconnect_sink()
278 declare -n fd="$1" 285 declare -n fd="$1"
279 [ "$fd" ] || return 286 [ "$fd" ] || return
280 declare -n pid="${!fd}_PID" 287 declare -n pid="${!fd}_PID"
288 i "sink stopping: ${!fd}"
281 exec {fd}>&- 289 exec {fd}>&-
282 kill $pid 2>/dev/null 290 kill $pid 2>/dev/null
283 unset fd pid 291 unset fd pid
@@ -299,30 +307,36 @@ echo_sender()
299 307
300background() 308background()
301{ 309{
302 start_screen_pane 1 "$@" 310 with_screen_pane 1 start_screen_pane "$@"
303 focus bottom 311 focus bottom
304 312
305 disconnect_sink ECHOSEND 313 disconnect_sink ECHOSEND
306 connect 314 connect
307 sink ECHOSEND echo_sender 315 connect_sink ECHOSEND echo_sender
316 kill %tty_forward
317 %tty_forward &
318 disown %tty_forward
319 i 'starting tty_forward'
308 quiet_bg tty_forward >&$ECHOSEND 320 quiet_bg tty_forward >&$ECHOSEND
309 321 echo $! > "$TWOPANE"/tty_forward.pid
310 TTY_FORWARD_PID=$!
311 printf '%d\n' "$TTY_FORWARD_PID" > "$TWOPANE"/tty_forward.pid
312 printf '%s\n' \
313 "#!/bin/bash" \
314 "kill -TSTP $TTY_FORWARD_PID" \
315 "screen -X focus bottom" \
316 > "$TWOPANE"/unforward
317 chmod +x "$TWOPANE"/unforward
318} 322}
319 323
320foreground() 324foreground()
321{ 325{
322 check_tty_reader || background "$@" 326 while true
323 focus top 327 do
324 fg %tty_forward >/dev/null 2>&1 328 background "$@"
325 focus bottom 329 focus top
330 set -x
331 %tty_forward >/dev/null 2>&1
332 if [ "$TOP_EXIT" != restart ] || jobs %tty_forward >/dev/null 2>&1
333 then
334 focus bottom
335 set +x
336 break
337 fi
338 set +x
339 done
326} 340}
327 341
328twopane() 342twopane()
@@ -345,34 +359,47 @@ resize()
345 screen -X resize "$@" 359 screen -X resize "$@"
346} 360}
347 361
362attach()
363{
364 if jobs %tty_forward >/dev/null 2>&1
365 then
366 %tty_forward
367 else
368 foreground
369 fi
370}
371
348SIGUSR1() 372SIGUSR1()
349{ 373{
350 i SIGUSR1 "${BASH_COMMAND@A}" 374 i "SIGUSR1: ${BASH_COMMAND@A}"
375}
376
377SIGCHLD()
378{
379 i "SIGCHILD: ${BASH_COMMAND@A}"
351} 380}
352 381
353our_bashrc_main() 382our_bashrc_main()
354{ 383{
384 BASH_ARGV0=twopane
385 echo -n "$0" >/proc/$BASHPID/comm
355 set -m 386 set -m
356 set -f 387 set -f
357 set -o pipefail 388 set -o pipefail
358 trap 'quit' EXIT
359 trap 'SIGUSR1' USR1 389 trap 'SIGUSR1' USR1
390 # trap 'SIGCHLD' CHLD
391 trap '! [ "$DEBUG" ] || read -p "Exit> "; quit' EXIT
360 export PS1="$BOT_TITLE\\\$ " 392 export PS1="$BOT_TITLE\\\$ "
361} 393}
362 394
363save_file bashrc <<. 395save_file bashrc <<.
364BASH_ARGV0=twopane
365echo -n "\$0" >/proc/\$\$/comm
366${TOP_CMD@A} 396${TOP_CMD@A}
367${BOT_CMD@A} 397${BOT_CMD@A}
368${TOP_TITLE@A} 398${TOP_TITLE@A}
369${BOT_TITLE@A} 399${BOT_TITLE@A}
370
371${TOP_EXIT@A} 400${TOP_EXIT@A}
372
373$(declare -f) 401$(declare -f)
374our_bashrc_main 402our_bashrc_main
375
376$BOT_CMD 403$BOT_CMD
377. 404.
378 405