How to run a Docker command in the background after TTY input

Attached the TTY for a command that required input, then detach it to run in the background

Ngô Việt Khánh Huy
Level Up Coding

--

A few days ago, I created my own remote signer for the Tezos blockchain. For those who are not familiar with Tezos, a signer is a process that holds users’ credentials and signs all transactions on behalf of them. Thanks to Tezos’s docker repository, the setup has gone smoothly. What I have to do is pull the docker image, and run its command.

$ docker run --net=host tezos/tezos:carthagenet tezos-signer launch socket signer -a 127.0.0.1 -p 22000 -W

Everything went well in the test environment when I added a testing key in plain text. For production, I need to introduce some encryption. A simple way is to use Tezos’s default encryption, which will require me to enter a password whenever I start the process. The execution is something like this:

$ docker run --net=host -it tezos/tezos:carthagenet tezos-signer launch socket signer -a 127.0.0.1 -p 22000 -W Enter password for encrypted key "my_account": (I enter my password here) Jun 9 02:58:39 - client.signer: Accepting TCP requests on 127.0.0.1:22000 <-- (this line means signer process started)

Noticed that I added -it params to the docker run command. These params attached TTY to docker execution to support password prompt. Sounds good? One thing left. How do I run this command in the background? It's OK to enter a password when I run the script. But I want it to run in background after that, instead of running on foreground as a blocking process on my terminal (and die if my session terminated).

Not work: use yes command

I used to use yes command to provide input in advance. This command will send y for all inputs required by the following one. See the sample below (we can use rm -rf instead, I just want to show how yes works here):

yes can support custom input as well. In the next sample, I created a test.sh file, which asked for a name and printed out a welcome message.

$ vi test.sh #!/bin/bash 
read -p "Enter Your Name: " username
echo "Welcome $username!"
$ chmod +x ./test.sh $ yes Huy | test.sh
Welcome Huy!

I thought yes can help to fill in the password for my command above. If so, I can then add & at the tail to run in the background. But it did not.

If I keep -it params, docker won't accept the input. It complains input device is not a TTY.

$ yes (my-password) | docker run --net=host -it tezos/tezos:carthagenet tezos-signer launch socket signer -a 127.0.0.1 -p 22000 -W the input device is not a TTY

If I remove -i, it seems Docker receives input in the wrong format. It caused the signer to throw an error (Unix.ENOTTY).

$ yes (my-password) | docker run --net=host -t tezos/tezos:carthagenet tezos-signer launch socket signer -a 127.0.0.1 -p 22000 -W Enter password for encrypted key "my_account": 
Fatal error: Unix.Unix_error(Unix.ENOTTY, "tcgetattr", "")

If I remove -t, Docker did ask me for input, but it hung after that and my process didn't seem to work.

$ yes (my-password) | docker run --net=host -i tezos/tezos:carthagenet tezos-signer launch socket signer -a 127.0.0.1 -p 22000 -W Enter password for encrypted key "my_account": (hang here > <! )

So, yes command can't help.

Not work: use mkfifo

Another solution I can think about is to use mkfifo to create an input pipeline. Let's come back with the test.sh I created above.

$ vi test.sh #!/bin/bash 
read -p "Enter Your Name: " username
echo "Welcome $username!"
$ chmod +x ./test.sh $ mkfifo ./myname $ ./test.sh < ./myname & $ echo "Huy" > ./myname
Welcome Huy!

When my command needs input, it will wait until someone adds content to the pipe. The pipe will now replace the traditional input TTY.

But this solution didn’t work with Docker either. What I get is very similar to yes: Unix.Unix_error(Unix.ENOTTY, "tcgetattr", "") or the input device is not a TTY

Working Solution: run with TTY, input, then detach the TTY

I found a solution (Yeah)! And luckily, the solution is much more simple than the other two above. Docker supports detach TTY from the attached session. The solution now is pretty simple:

  • Run my command
  • Type in the input if required
  • Press Ctrl+P, Ctrl+Q (I used MacOS) to detach TTY from running session

Bump! The detached session is a background process by default.

Originally published at https://huynvk.dev.

--

--