How to Connect a Shell to a Pseudo Tty

Is there a way to connect a shell to a pseudo tty?

The question presupposes you have a single server process but in fact you would need two.

One process is your 'backgrounded' application using the slave end of the pty.

The other process would be a helper which manages the master end of the pty.
The master end must remain opened at all times. The pty pair is removed when the masters file descriptor is closed.

The backgrounded applicaiton on the slave side, reads and writes to its end.
The master side decides what to do with those reads and writes.

To connect from another terminal you need to tell the master end where to send the read and writes.

You could give it the name of the pty terminal you are using somehow. The process managing the master end could then forward the reads and writes as appropriate.

This alone is insufficient for some applications. In particular if the terminal's size is changed the application should recieve a SIGWINCH. The master will need to pass this to the slave somehow. The terminal capabilities could also differ and require translation.

To handle those your client side needs a third process which talks to the process running the master end of the pty. This is how screen and tmux work.

So there is no standard program that does what you want because there is no standard way to handle this. Programs like screen do this their own way but can be used as is in most cases.

In an answer to the linked question the OP (of that question) uses a terminal in raw mode which probably means those complications do not apply.
For example if you just want to forward stdout from the master a pty name to duplicate the output to would be sufficient.

The screen program on the other hand creates a socket for each virtual screen (running on the master side of the pty). The same screen executable running (in a different mode) as a client process connects to that socket to talk to the application.

Given that screen and tmux are designed explicitly to do this it is not clear what advantage you would gain from doing this yourself.
It could be an interesting learning exercise however.

The new question describes your use case better.
You could not create new user interfaces on demand with screen.
It does handle the foreground and background use case very nicely so long as you remember to run the process under screen from the outset, where screens attach and detach command would replace direct use of bg and fg.

How to determine which pair of pseudo tty ports are connected to each other in bash

Updated Answer

It looks like you'll have to use a file if socat must be backgrounded.

( socat ... 2>&1 | grep -Eo "/dev/pts/\d+" > /tmp/a ) &
portA=$(head -n 1 /tmp/a)
portB=$(tail -n 1 /tmp/a)

Original Answer

@jeremysprofile 's answer is probably more sensible but, just for fun, you could do either of these also:

socat ... | grep -Eo "/dev/pts/\d+" | { read portA; read portB; }

Or, using bash's "process substitution", you could do:

{ read portA; read portB; } < <(socat ... | grep -Eo "/dev/pts/\d+")

Then you would do this after either of them:

./test_pty_app $portA &
./test_pty_app $portB &

Confused about Docker -t option to Allocate a pseudo-TTY

The -t option goes to how Unix/Linux handles terminal access. In the past, a terminal was a hardline connection, later a modem based connection. These had physical device drivers (they were real pieces of equipment). Once generalized networks came into use, a pseudo-terminal driver was developed. This is because it creates a separation between understanding what terminal capabilities can be used without the need to write it into your program directly (read man pages on stty, curses).

So, with that as background, run a container with no options and by default you have a stdout stream (so docker run | <cmd> works); run with -i, and you get stdin stream added (so <cmd> | docker run -i works); use -t, usually in the combination -it and you have a terminal driver added, which if you are interacting with the process is likely what you want. It basically makes the container start look like a terminal connection session.

Pseudo-terminal will not be allocated because stdin is not a terminal

Try ssh -t -t(or ssh -tt for short) to force pseudo-tty allocation even if stdin isn't a terminal.

See also: Terminating SSH session executed by bash script

From ssh manpage:

-T      Disable pseudo-tty allocation.

-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.

Pretend to be a tty in bash for any command

There are a number of options, as outlined by several other Stack Overflow answers (see Caarlos's comment). I'll summarize them here though:

  1. Use script + printf, requires no extra dependencies:

     0<&- script -qefc "ls --color=auto" /dev/null | cat

    Or make a bash function faketty to encapsulate it:

     faketty () {
    script -qefc "$(printf "%q " "$@")" /dev/null
    }
    faketty ls --color=auto | cat

    Or in the fish shell:

     function faketty
    script -qefc "(printf "%q " "$argv")" /dev/null
    end
    faketty ls --color=auto | cat

    (credit goes to this answer)

    http://linux.die.net/man/1/script

  2. Use the unbuffer command (as part of the expect suite of commands), unfortunately this requires an extra package install, but it's the easiest solution:

     sudo apt-get install expect-dev   # or brew install expect
    unbuffer -p ls --color=auto | cat

    Or if you use the fish shell:

     function faketty
    unbuffer -p $argv
    end
    faketty ls --color=auto | cat

    http://linux.die.net/man/1/unbuffer

This is a great article on how TTYs work and what Pseudo-TTYs (PTYs) are, it's worth taking a look at if you want to understand how the linux shell works with file descriptors to pass around input, output, and signals. http://www.linusakesson.net/programming/tty/index.php

how to do if else in ssh pseudo-tty allocation to remote server

You will have to quote the commands that is to be sent to the server like so:

ssh -t -t user@remote-host "
if [ -d '/test/dir_test' ]; then
sudo chown -R user:admin /test/dir_test/
sudo rm -rf /test/dir_test/*
else
sudo mkdir /test/dir_test/
fi"

Be aware that sudo needs a password unless otherwise specified in its configuration.

What is Pseudo TTY-Allocation? (SSH and Github)

As explained in "gitolite: PTY allocation request failed on channel 0", it is important to do ssh test connection with -T, because some server could abort the transaction entirely if a text-terminal (tty) is requested.

-T avoids requesting said terminal, since GitHub has no intention of giving you an interactive secure shell, where you could type command.

GitHub only wants to reply to your ssh request, in order to ascertain that the ssh command does work (you have the right public/private keys, and the public one has been registered to your GitHub account)

PuTTy would be an example of a terminal emulator, serial console and network file transfer application. It supports several network protocols, including SCP, SSH, Telnet and rlogin.

The name "PuTTY" has no definitive meaning, though "tty" is the name for a terminal in the Unix tradition, usually held to be short for Teletype.


Other use-cases for -T (beside testing)

  • Transferring binary files
  • Execute commands on a remote server
  • SSH tunneling: ssh -fnT -L port:server:port user@server (-f for background: you don't want to execute command, don't need a TTY and just want to establish a tunnel)


Related Topics



Leave a reply



Submit