Piping data to Linux program which expects a TTY (terminal) - linux

I have a program in Linux which refuses to run if its stdin/stdout is not a TTY (terminal device). Is there an easy-to-use tool which will create a PTY, start the program with the newly created TTY, and copy all data over stdin/stdout?
The use case is not interactive, but scripting. I'm looking for the most lightweight solution, preferably not creating TCP connections, and not requiring too many other tools and libraries to be installed.

unbuffer, part of expect (sudo apt-get install expect-dev on Ubuntu Lucid), can fool a program into thinking it's connected to a TTY.
$ tty
/dev/pts/3
$ echo | tty
not a tty
$ echo | unbuffer tty
/dev/pts/11

You can use socat for this: echo your stdin strings | socat EXEC:"your_program",pty STDIO >/stdout_file
For example with bash: echo ls | socat EXEC:'bash',pty STDIO >/tmp/ls_out
Or as described here, for a program run with docker:
# Run the docker task, here bash, in background
docker run -it --rm --name test ubuntu &
# Send "ls -la" to the bash running inside docker
echo 'ls -la' | socat EXEC:'docker attach test',pty STDIN
# Show the result
docker logs test

Related

How to redirtect terminal STDIN when running a script in Docker?

I have a script that consumes users input. It is run like this:
./script <<EOF
> input
> onther input
> more input
> EOF
I need to distribute the script as a Docker image.
I am able to run the script in two steps.
First, I get into the containers shell.
docker run -it my-docker-tag sh
And then, inside the shell, I execute the script itself.
Question: Is it possible to run the script in on shut (without having to navigate to the containers shall)?
I tried this:
docker run -it my-docker-tag ./script <<EOF
> input
> onther input
> more input
> EOF
But it fails with:
the input device is not a TTY
The Docker run reference notes:
Specifying -t is forbidden when the client is receiving its standard input from a pipe, as in:
$ echo test | docker run -i busybox cat
If you remove the -t option, shell pipes and redirections around a (foreground) docker run command will work as you expect.
# A heredoc as in the question should work too
sudo docker run --rm my-docker-tag ./script <script-input.txt

WSL, Running linux commands with "wsl --exec <cmd>" or "wsl -- <cmd>"

wsl -h shows the following:
--exec, -e <CommandLine> Execute the specified command without using the default Linux shell.
-- Pass the remaining command line as is.
What does "without using the default Linux shell" mean (i.e. what else is it going to use, if not the default shell!?)?.
Additionally, by way of an example, I now have three possible ways to run Linux ls from my PowerShell prompt (i.e. this will not be Get-ChildItem aliased to ls, but instead a Linux command via WSL):
PS C:\> wsl -e ls # Alternatively, wsl --exec ls
PS C:\> wsl -- ls
PS C:\> wsl ls
But all outputs appear to be the same. How would you explain the differences between these three ways of running a WSL Linux command from a PowerShell prompt?
I think it means wsl runs the command directly, instead of spawning a shell process to run the command.
For example, if I run :
wsl -e sleep 10
From another terminal, I have :
root 1482 1 0 11:32 tty3 00:00:00 /init
ubuntu 1483 1482 0 11:32 tty3 00:00:00 sleep 10
We can see /init is the parent of sleep 10, without a shell in between.
A cool trick is using this to set the X11 $DISPLAY variable, letting you use windows terminal to get remote shells using WSLG.
# in microsoft terminal or powershell use this command line
wsl.exe -- ssh -a -X -Y $hostname
then on the remote system
# DISPLAY will show something like localhost:10.0 on the remote system
echo $DISPLAY
# use a program like xeyes to test
xeyes

Bash run task via ssh and get stdout, stderr live output to file in background

I am running a python 3 script via ssh, and I want to see the stdout and stderr in a file on the remote server.
Also, I would like to see the file updated live while the script is running and that the script will run in the background so the ssh connection will not wait for the script to finish.
While looking at other questions, I managed to answer most of my requests.
Here is what I come up with:
ssh user#machine_ip "(python3 my_script.py 2>&1 | tee output.log) &"
The problem is that the ssh is waiting for the script to finish.
So combining the answer from this question with some hackidy hack nonsense...
Really all you need to do is this:
(( python3 my_script.py 0<&- 2>&1 | tee -a ${OUTFILE} | nc -kl ${PORT} &) &)
Explanation:
Run your python script: python3 my_script.py
Detach stdin (so it can run independently): ... 0<&-
Redirect stderr to stdout so we can pipe them along together: ... 2>&1
Append output to some file but also keep the output going to stdout so we can pipe it someplace else: ... | tee -a ${OUTFILE} |
Pipe stdout to a netcat listening port so this machine can essentially "serve" the stdout from your python script: ... | nc -kl ${PORT}
Create a "double nested background subshell" this is explained in the link above but this will allow you to orphan "blah" so that it'll run even if your ssh connection ends (( ... blah ... &) &)
To view the stdout/stderr of my_script.py you now have several options. If you are still logged into that remote machine you can:
tail -f ${OUTFILE} # Same "OUTFILE" used in explanation component 4
nc localhost $PORT # Same "PORT" used in explanation component 5
If you are no longer logged in and you are now on a different machine (but on the same network) you can:
nc ${remote_machine} $PORT # Same "PORT" used in explanation component 5
Where ${remote_machine} is the hostname or IP address of the machine you ssh'ed into to run your command

'su' command in Docker returns 'must be run from terminal'

I'm developing a docker environment for teaching purposes and need to be able to switch users inside docker.
I set up the 'user' user with a password but when I try to switch to it with su, I get "su must be run from terminal".
I get this if I try to ssh into the docker and also by issuing commands through a php shell (an apache service is running on the Docker instance).
Any help is much appreciated.
When you are ssh-ing in or going in via php your session is not being allocated a pty. I have used each of the following solutions:
ANSWER 1: use ssh -t or ssh -tt to get pty allocated when logging in using ssh:
I had great fun getting commands to run right due to ptys when running sessions like this: jenkins shell -> ssh driver -> ssh test -> docker exec.
Good answer here:
https://unix.stackexchange.com/questions/105422/command-must-be-run-from-a-terminal
"Try the -t option to ssh. If that does not work try -tt."
"-t Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty."
ANSWER 2: use docker run -t ... and docker exec -it
Use the -t and -it options to allocate pty in your docker exec session.
Also with docker exec you can simply use the -u option to login to container as different users and avoid using su. e.g.
$ docker exec -u root -it small_hypatia bash
There is a good question and answer on this here:
https://github.com/docker/docker/issues/8631
ANSWER 3: use python to spawn a pty in your shell
Quite a cute hack :)
jenkins#e9fbe94d4c89:~$ su -
su: must be run from a terminal
$ echo "import pty; pty.spawn('/bin/bash')" > /tmp/asdf.py
$ python /tmp/asdf.py
$ su -
Password:
root#e9fbe94d4c89:~#
This solution work by using 'script' command from the 'bsdutiles' package that setup a pty (a terminal). The 'sleep' command is there to prevent sending the password before the 'su' command is ready to read it. The 'tail' command remove the "Password:" input line issued by 'su'.
sh -c "sleep 1; echo rootpassword" | script -qc 'su -c whoami - root' | tail -n +2
Beware that the rootpassword could be see in many ways (history, ps, /proc/, etc...). Start the command with a space to at least avoid history recording.
If you use su-exec instead of su the issue with tty completely vanishes since it calls execvp directly instead of forking like su does.
Gosu is another similar alternative.

How to enter bash of an ubuntu docker container?

I want to run an ubuntu container and enter bash:
[root#localhost backup]# docker run ubuntu bash
[root#localhost backup]#
The ubuntu container exits directly. How can I enter the bash?
Use -i and -t options.
Example:
$ docker run -i -t ubuntu /bin/bash
root#9055316d5ae4:/# echo "Hello Ubuntu"
Hello Ubuntu
root#9055316d5ae4:/# exit
See: Docker run Reference
$ docker run --help | egrep "(-i,|-t,)"
-i, --interactive=false Keep STDIN open even if not attached
-t, --tty=false Allocate a pseudo-TTY
Update: The reason this works and keeps the container running (running /bin/bash) is because the -i and -t options (specifically -i) keep STDIN open and so /bin/bash does not immediately terminate thus terminate the container. -- The reason you also need/want -t is because you presumably want to have an interactive terminal-like session so t creates a new pseudo-tty for you. -- Furthermore if you looked at the output of docker ps -a without using the -i/-t options you'd see that your container terminated normally with an exit code of 0.

Resources