Introduction
Do you ever want to see other people's terminals on a Linux server? I have.Of course, having the terminal physically shown is one way, but it's a bit of a hassle. You can also use the
script
command, but it is a little more laborious because you have to ask the shower to execute the script
command.So I've created a tool that makes it easy to see the terminals of other users logged in on the server! This tool wraps
strace
. It is called ttycopy
. It's incredible simple and very useful.https://github.com/d-tsuji/ttycopy
strace
It is well known that
strace
command can be used to trace a system call. Before introducing the workings of ttycopy
, let's review how to check a system call with strace
. For example, if the current directory is /tmp
and there is an empty file named sample.txt
under /tmp
, the result of stracing is shown below. In other words, the situation is as follows.[root@localhost tmp]# pwd
/tmp
[root@localhost tmp]# ls
sample.txt
We can check with
strace
what kind of system call is being called when you call ls
.[root@localhost tmp]# strace ls
execve("/usr/bin/ls", ["ls"], [/* 27 vars */]) = 0
brk(NULL) = 0xaf7000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2afb9f4000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=84100, ...}) = 0
mmap(NULL, 84100, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f2afb9df000
close(3) = 0
open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320i\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=155784, ...}) = 0
mmap(NULL, 2255184, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2afb5ad000
mprotect(0x7f2afb5d1000, 2093056, PROT_NONE) = 0
mmap(0x7f2afb7d0000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x23000) = 0x7f2afb7d0000
mmap(0x7f2afb7d2000, 6480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2afb7d2000
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f288e995000
write(1, "sample.txt\n", 11sample.txt
) = 11
close(1) = 0
munmap(0x7f288e995000, 4096) = 0
close(2) = 0
exit_group(0) = ?
Even if we take one operation to get the result of the
ls
command as described above, we can see that many system calls are made behind the scenes.ttycopy
I would like to tell you how this is being processing. The basic flow of the process is as follows.
1.Check the PID of the tty's login process
The shell implementation is as follows.
pid=`ps fauwwx | grep sshd.*${tty} | grep -v grep | sed -e 's/^[a-zA-Z0-9]\+[ \n\r\f\t]\+\([0-9]\+\).*/\1/'`
This captures the PID using a regular expression with a string of the following process tree that can be retrieved when grep is done. You can use regular expressions to make it look neat.
[tsuji@localhost ~]$ ps fauwwx | grep sshd.
root 1531 0.0 0.1 82568 6236 ? Ss 15:29 0:00 /usr/sbin/sshd -D
root 2830 0.0 0.2 149824 8916 ? Ss 15:29 0:01 \_ sshd: root@pts/0
root 13315 0.0 0.2 158980 10280 ? Ss 18:58 0:00 \_ sshd: root@notty
root 14956 0.0 0.2 154512 9352 ? Ss 20:10 0:00 \_ sshd: tsuji [priv]
tsuji 14959 0.0 0.1 154512 4092 ? S 20:10 0:00 \_ sshd: tsuji@pts/1
tsuji 15012 0.0 0.0 112672 2268 pts/1 S+ 20:11 0:00 \_ grep --color=auto sshd.
2.Retrieving read system calls to PID using the strace command
The shell implementation is as follows.
strace -e read -s16384 -q -xx -p ${pid} 2>&1
What we're doing is using trace to retrieve the read system call issued by the PID associated with the tty we just retrieved. Because various other system calls are called besides the read system call, only the read system call is extracted with the option
-e read
of trace.The read system call was the system call defined below, and the results that can be obtained with starce are also as follows.
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
For a user who is logged in at another terminal, execute the
ls
command in the /tmp
directory.straing terminal
# strace -p 14959 -e read
read(0, "l", 1) = 1
read(0, "s", 1) = 1
read(0, "\r", 1) = 1
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=2985, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
We can only get results for read system calls that are being read from the standard input as shown above. The standard input is the file descriptor number 0.
3.Print a formatted string of the read system call output by strace
The goal is to display the contents of the second argument of the read system call obtained in step 2. The shell implementation is as follows.
sed -une "s/^read([0-9]\+, \"\(.*\)\".*/echo -n \$'\1'/p" | bash
First, the first part
sed -une "s/^read([0-9]\+, \"\(.*\)\".*/echo -n \$'\1'/p"
. It captures the second argument of the read system call in a regular expression and gives it the strings echo -n` and `$''
.The string output to the source terminal
[tsuji@localhost tmp]$ ls
sample.txt
[tsuji@localhost tmp]$
Strace the terminal string to be output after processing.
echo -n $'\x00\x00\x00\x10\x97\xcb\x9f\x29\xb7\x68\xee\xb5\x30\x84\xee\xad\x9d\x73\x52\x01\x17\x63\x30\x62\xe3\xe2\x7e\x61\x26\x64\x5b\x73\x93\x64\x96\xfd'
echo -n $'l'
echo -n $'\x00\x00\x00\x10\x70\x94\x30\xff\x6a\xfa\xbf\xfb\x1a\x1c\x23\x95\x85\x7d\x8f\xa4\xb7\x55\x42\xa4\x8e\x6e\x3e\x3e\xcd\xe4\xd1\xf7\xfb\x05\x5c\xb8'
echo -n $'s'
echo -n $'\x00\x00\x00\x10\x2d\xda\xa7\x40\xa5\xbc\x6a\xf4\x3f\x42\xce\x89\x6d\x3b\x6a\xe1\x16\xe6\x2e\x14\x55\x0d\xdd\x2b\xd8\x9c\xe0\x81\x84\x7d\x8d\x9d'
echo -n $'\r\n'
echo -n $'sample.txt\r\n'
echo -n $'\x1b\x5d\x30\x3b\x74\x73\x75\x6a\x69\x40\x6c\x6f\x63\x61\x6c\x68\x6f\x73\x74\x3a\x2f\x74\x6d\x70\x07\x5b\x74\x73\x75\x6a\x69\x40\x6c\x6f\x63\x61\x6c\x68\x6f\x73\x74\x20\x74\x6d\x70\x5d\x24\x20'
The
-n
option of echo -n
is to avoid line breaks when interpreting. the $''
form of Bash has the following effect. excerpt from the Man pageWords of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard. Backslash escape sequences, if present, are decoded as follows:
ref: https://linux.die.net/man/1/bash
This can be used to expand to the meaning that the string holds.
All that's left to do is to let the shell interpret the
echo
string with the | bash
to display the string.Summary
I was able to casually share the terminal by retrieving a string of arguments for the read system call with
strace
and restoring them in real time. I think ttycopy
is very useful and an educational tool because you can see other people's work in real time. If you want to scan the terminal, you should try it.Thank you!