| 1 | # Redirect File Descriptor of Running Process # |
| 2 | |
| 3 | This note explains how to redirect `stdin` (or any other file descriptor) of a |
| 4 | pre-existing process using the GNU debugger (`gdb`) and a FIFO. It was tested |
| 5 | on FreeBSD 11. |
| 6 | |
| 7 | An example of use would be saving the contents of remote `vi` sessions after |
| 8 | they are detached due to a dropped connection. |
| 9 | |
| 10 | First, make a FIFO: |
| 11 | |
| 12 | $ mkfifo /tmp/vififo |
| 13 | |
| 14 | Assuming there is a pre-existing `vi` session with PID `91266`, connect |
| 15 | with `gdb`, close file descriptor `0` and reopen it as a connection to the |
| 16 | FIFO with the `call close` and `call open` commands. |
| 17 | |
| 18 | $ gdb -p 91266 |
| 19 | <snip> |
| 20 | Attaching to process 91266 |
| 21 | <snip> |
| 22 | (gdb) call close (0) |
| 23 | $1 = 0 |
| 24 | (gdb) call open ("/tmp/vififo", 0600) |
| 25 | |
| 26 | At this point `gdb` will appear to hang. Leave it and open a new terminal. Use |
| 27 | `echo` to send characters to the process through the FIFO. |
| 28 | |
| 29 | Special characters may be escaped by pressing `Ctrl-V` followed by the |
| 30 | character. For example, to send an `Escape`, press `Ctrl-V` followed by |
| 31 | `Escape` which results in an `Escape` code, or `^[`. |
| 32 | |
| 33 | Continuing the example, tell `vi` to save the current buffer to a file. |
| 34 | |
| 35 | $ echo "^[:w /tmp/vi_recover.txt" > /tmp/vififo |
| 36 | |
| 37 | After this command the `gdb` session should start responding again, returning |
| 38 | to a `(gdb)` prompt. Exit `gdb`. |
| 39 | |
| 40 | $2 = 0 |
| 41 | (gdb) quit |
| 42 | A debugging session is active. |
| 43 | |
| 44 | Inferior 1 [process 91266] will be detached. |
| 45 | |
| 46 | Quit anyway? (y or n) Y |
| 47 | Detaching from program: /hh/bin/vi, process 91266 |
| 48 | [Inferior 1 (process 91266) detached] |
| 49 | |
| 50 | The characters have now been received by `vi` and a file should be waiting at |
| 51 | `/tmp/vi_recover.txt`. |
| 52 | |
| 53 | |
| 54 | # Expose Process STDIO on TCP Port # |
| 55 | |
| 56 | This note explains how to launch a process and connect its |
| 57 | `stdin`/`stdout`/`stderr` directly to a TCP port. It was tested on FreeBSD 12. |
| 58 | |
| 59 | As an example, we will launch a `bash` process running on a Debian 10 machine |
| 60 | and connect it to TCP port `4242`. Then, from a FreeBSD machine we will connect |
| 61 | and utilize the `bash` session remotely. |
| 62 | |
| 63 | First, on the Debian machine, we use |
| 64 | [`socat`](http://www.dest-unreach.org/socat/), a program which establishes two |
| 65 | bidirectional byte streams and transfers data between them. In our case, we |
| 66 | tell `socat` to build a PTY at `/tmp/pty_test` and connect it to TCP port |
| 67 | `4242`. |
| 68 | |
| 69 | socat PTY,link=/tmp/pty_test,raw TCP-LISTEN:4242,reuseaddr,fork |
| 70 | |
| 71 | Note that the `PTY` argument to `socat` must precede the `TCP-LISTEN` argument. |
| 72 | Despite the bidirectional nature, the endpoint definition order is not |
| 73 | interchangeable. |
| 74 | |
| 75 | Now we use `setsid` to start a new `bash` process and attach it to the PTY at |
| 76 | `/tmp/pty_test`. Note that we collapse `stdout` and `stderr` into a single |
| 77 | combined stream. |
| 78 | |
| 79 | setsid sh -c 'exec bash --login <> /tmp/pty_test >&0 2>&1' |
| 80 | |
| 81 | Replace `bash --login` with whatever other command you desire to execute. |
| 82 | |
| 83 | Now, from the FreeBSD machine, connect the remote TCP port to your local STDIO |
| 84 | with `socat`. Assuming the Debian machine running `bash` is at `192.168.1.107`, |
| 85 | execute the following command. |
| 86 | |
| 87 | socat STDIO,raw TCP:192.168.1.107:4242 |
| 88 | |
| 89 | At this point you have a fully interative `bash` session running on the Debian |
| 90 | machine, controlled from the FreeBSD machine. |