Getting tmux Working in Docker with s6 overlay - blitterated/docker-dev-env-s6 GitHub Wiki
- Tried running
tmux
after s6 initialized, but was only greeted with a blank screen. - Ran through all the steps in s6-TERM-issue.md, and posted it to the s6-overlay GitHub issues here.
- Reviewed skarnet's suggestions below
- Reviewed source code of progams in s6's init sequence
- Found a lot of tty related changes happening in s6-linux-init
- Tied a clue from skarnet to some code where STDIN was reopened as read only
- Confirmed with lsof in the container
- Learned a lot about file descript[or|ion] copying/inheritance with linux processes and
fork
- Figured out how to reopen STDIN as r/w in Bash
- Tested it out, and it worked
TODO: Review the following:
- [Session s6, tmux, TERM issue 20221002] bookmarks.
- ~/src/s6/s6-TERM-issue.md
Most of this is from a reply from skarnet to an issue I posted on s6-overlay's GitHub.
- s6-overlay does not set the TERM variable: it purposefully does not inherit any variables from the host. If you see it set to
dumb
, it's probably bash seeing it unset and setting it todumb
by default. If you want a different TERM variable, you can set it in your Dockerfile. - s6-overlay isn't designed to handle interactive sessions. It tries to accommodate interactive containers as best as it can, but there will inevitably be things that don't work perfectly. The reason for this is that the terminal needs to be closed and reopened during the container boot, because s6-overlay's init process does not want to use it as a control terminal - only the CMD may run with a control terminal.
- Controlling Terminal of a Process
- s6 init code that closes and reopens the terminal
- This also interacts poorly with the ad-hoc terminal transmission performed by the
docker
command. Behind the scenes,docker run -it
does very ugly things to pretend that the container is running directly in your terminal - while it absolutely isn't. This makes it very difficult to support interactive sessions that work in all possible cases; s6-overlay is already jumping through several hoops in order for^C
to work properly in most cases.- TODO: find the hoops for
ctrl-c
(SIGINT) in the code.
- TODO: find the hoops for
- tmux is a third layer of terminal transmission, inside the container. At this point, it would be a miracle if terminals worked perfectly through the whole chain.
tmux not working in Docker
tmux(1) or screen(1) refuse to start via script invoked via service(8)
Specifically, it is the absence of the TERM environment variable that causes tmux to produce the above error ("open terminal failed: terminal does not support clear").
The solution is to add TERM to the list of variables provided to (in this case) the child tmux. Globally adding TERM to the list of variables that are passed to service(8) child-scripts seems harmless enough (warranted even; for pedantic properness).
SO: TERM environment variable not set
You can see if it's really not set. Run the command
set | grep TERM
.If not, you can set it like that:
export TERM=xterm
SO: TERM environment variable not set
You can replace :
export TERM=xterm
with :
export TERM=linux
It works even in kernel with virgin system.
List the Open File Descriptors in the Current Bash Session
apt --yes install lsof
lsof -a -d 0-2147483647 -p $$
set | grep TERM
export TERM=xterm
exec 0<>/dev/pts/0
docker kill $(docker ps -q)
# no s6
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 1 root 0u CHR 136,0 0t0 3 /dev/pts/0
bash 1 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 1 root 2u CHR 136,0 0t0 3 /dev/pts/0
bash 1 root 255u CHR 136,0 0t0 3 /dev/pts/0
# s6 initialized
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 61 root 0r CHR 136,0 0t0 3 /dev/pts/0
bash 61 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 61 root 2u CHR 136,0 0t0 3 /dev/pts/0
bash 61 root 255u CHR 136,0 0t0 3 /dev/pts/0
root@0a6508df7ebc:~# tty
/dev/pts/0
cat <<EOF > infdtest
#! /bin/bash
exec 0<&- # close STDIN
exec 0<>/dev/pts/0 # reopen STDIN as r/w
EOF
chmod +x infdtest # make it executable
Went digging through source from /init
forward until I found this line in s6-linux-init.c
fd = open("/dev/tty0", O_RDONLY | O_NOCTTY) ;
Double checked in a container with lsof
, and it was read only. Instead of /dev/tty0/
it was on /dev/pts/0
. That makes sense for a container.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 61 root 0r CHR 136,0 0t0 3 /dev/pts/0
bash 61 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 61 root 2u CHR 136,0 0t0 3 /dev/pts/0
bash 61 root 255u CHR 136,0 0t0 3 /dev/pts/0
So I tried reopening STDIN as r/w, and it worked.
export TERM=xterm
exec 0<>/dev/pts/0
tmux
This does not work if run in .bashrc
. Not sure why. It does still work when run interactively from the Bash shell.