Getting tmux Working in Docker with s6 overlay - blitterated/docker-dev-env-s6 GitHub Wiki

Early draft. Incomplete.

What happened

  1. Tried running tmux after s6 initialized, but was only greeted with a blank screen.
  2. Ran through all the steps in s6-TERM-issue.md, and posted it to the s6-overlay GitHub issues here.
  3. Reviewed skarnet's suggestions below
  4. Reviewed source code of progams in s6's init sequence
  5. Found a lot of tty related changes happening in s6-linux-init
  6. Tied a clue from skarnet to some code where STDIN was reopened as read only
  7. Confirmed with lsof in the container
  8. Learned a lot about file descript[or|ion] copying/inheritance with linux processes and fork
  9. Figured out how to reopen STDIN as r/w in Bash
  10. Tested it out, and it worked

TODO: Review the following:

  • [Session s6, tmux, TERM issue 20221002] bookmarks.
  • ~/src/s6/s6-TERM-issue.md

Some info about ttys in these docs

Filling in some blanks regarding terminals and ttys

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 to dumb 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.
  • 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.
  • 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.

From notes and crib sheets

Earliest research

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.

Setting up for lsof and running tmux

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)

lsof results

# 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

Heredoc to create a quick test script for resetting STDIN

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

Reply to issue

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

.bashrc issue

This does not work if run in .bashrc. Not sure why. It does still work when run interactively from the Bash shell.

⚠️ **GitHub.com Fallback** ⚠️