Bagaimana kita bisa tahu siapa yang ada di ujung terminal pseudo-terminal?

26

Jika saya melakukan:

echo foo > /dev/pts/12

Beberapa proses akan membacanya foo\ndari deskriptor file ke sisi master.

Apakah ada cara untuk mengetahui apa itu proses itu?

Atau dengan kata lain, bagaimana saya bisa mengetahui mana xterm / sshd / script / screen / tmux / expect / socat ... yang ada di ujung lain /dev/pts/12?

lsof /dev/ptmxakan memberi tahu saya proses yang memiliki deskriptor file di sisi master dari setiap pty. Sebuah proses itu sendiri dapat menggunakan ptsname()( TIOCGPTNioctl) untuk mencari tahu perangkat slave berdasarkan fd sendiri ke sisi master, jadi saya bisa menggunakan:

gdb --batch --pid "$the_pid" -ex "print ptsname($the_fd)"

untuk masing-masing pid / fd dikembalikan oleh lsofuntuk membangun pemetaan itu, tetapi apakah ada cara yang lebih langsung, dapat diandalkan dan kurang mengganggu untuk mendapatkan informasi itu?

Stéphane Chazelas
sumber
Apa ini yang kau inginkan? sudo find /proc/*/fd/0 -ls | grep '/dev/pts/4', akan memberikan daftar PID ( /proc/PID) sebagai output.
slm
@slm, tidak, dengan kata lain, saya ingin mencari tahu mana xterm / sshd / script / layar / tmux / expect / socat ... yang ada di ujung lain/dev/pts/4 . Biasanya, itu akan menjadi nenek moyang yang sama dari proses-proses yang telah /dev/pts/4terbuka, tetapi tidak harus.
Stéphane Chazelas
1
Lebih buruk lagi dengan soket - Anda memerlukan debugger kernel!
Gilles 'SO- berhenti bersikap jahat'
1
@Falsenames - Saya mengerti pertanyaan yang dimaksud - mungkin salah - bukan proses apa yang melewati data yang dibaca - seperti shell pertama yang dipanggil di terminal - tetapi proses apa yang sebenarnya membacanya dari sisi master. Misalnya, jika saya meluncurkan shell screen, itu screenyang mengalokasikan dan secara aktif mengelola budak pty untuk kehidupan perangkat, tetapi - seperti, saya pikir - shell dibuat pemimpin proses untuk tty itu dan, seperti Anda output menunjukkan, Anda dapatkan bashatau apa pun dari pstidak screen. Saya melacak beberapa xtermskembali ke xtermpid berdasarkan /proc/lockstetapi itu longgar.
mikeserv

Jawaban:

3

Pada awalnya saya mencoba melacak kembali beberapa xtermke xtermpid berdasarkan info yang saya temukan di /proc/lockstetapi itu longgar. Maksud saya, itu berhasil, saya pikir, tetapi itu adalah keadaan terbaik - saya tidak sepenuhnya memahami semua informasi yang disediakan oleh file dan hanya mencocokkan apa yang tampaknya sesuai antara kontennya dan proses terminal yang dikenal.

Kemudian saya mencoba menonton lsof/stracepada write/talkproses aktif antara ptys. Saya belum pernah benar-benar menggunakan salah satu program sebelumnya, tetapi mereka tampaknya mengandalkan utmp. Jika pty saya yang ditargetkan tidak memiliki utmpentri untuk alasan apa pun mereka berdua menolak untuk mengakui bahwa itu ada. Mungkin ada jalan keluar, tetapi saya cukup bingung untuk meninggalkannya.

Saya mencoba beberapa udevadmpenemuan dengan 136 dan 128 node perangkat nomor utama seperti yang diiklankan untuk masing pts- ptmmasing /proc/tty/drivers, tetapi saya juga tidak memiliki pengalaman yang sangat berguna dengan alat itu dan sekali lagi muncul tidak ada yang substansial. Menariknya, saya perhatikan :minkisaran untuk kedua jenis perangkat ini sangat mengejutkan 0-1048575.

Baru setelah saya meninjau kembali kernel kernel ini saya mulai berpikir tentang masalah dalam hal mounts. Saya telah membaca itu beberapa kali sebelumnya, tetapi ketika penelitian lanjutan di baris itu membawa saya ke tambalan 2012 ini/dev/pts saya punya ide:

sudo fuser -v /dev/ptmx

Saya pikir apa yang biasanya saya gunakan untuk mengaitkan proses dengan mount? Dan tentu saja:

                     USER        PID ACCESS COMMAND
/dev/ptmx:           root      410   F.... kmscon
                     mikeserv  710   F.... terminology

Maka dengan informasi itu saya bisa lakukan, misalnya dari terminology:

sudo sh -c '${cmd:=grep rchar /proc/410/io} && printf 1 >/dev/pts/0 && $cmd'
###OUTPUT###
rchar: 667991010
rchar: 667991011

Seperti yang Anda lihat, dengan sedikit pengujian eksplisit, proses semacam itu dapat dilakukan untuk menghasilkan proses master pty yang sewenang-wenang. Mengenai soket, saya cukup yakin orang bisa mendekatinya dari arah itu juga menggunakan socatsebagai lawan debugger, tapi saya belum meluruskan bagaimana. Namun, saya curiga ssmungkin membantu jika Anda lebih terbiasa dengan saya daripada saya:

sudo sh -c 'ss -oep | grep "$(printf "pid=%s\n" $(fuser /dev/ptmx))"'

Jadi saya mengaturnya dengan pengujian yang sedikit lebih eksplisit, sebenarnya:

sudo sh <<\CMD
    chkio() {
        read io io <$1
        dd bs=1 count=$$ </dev/zero >$2 2>/dev/null
        return $((($(read io io <$1; echo $io)-io)!=$$))
    }
    for pts in /dev/pts/[0-9]* ; do
        for ptm in $(fuser /dev/ptmx 2>/dev/null)
            do chkio /proc/$ptm/io $pts && break
        done && set -- "$@" "$ptm owns $pts"
    done
    printf %s\\n "$@"
 CMD

Ini mencetak $$num \0null byte untuk setiap pty dan memeriksa setiap proses master terhadap cek sebelumnya. Jika perbedaannya $$maka ia menghubungkan pid dengan pty. Ini kebanyakan berhasil. Maksud saya, bagi saya, ia kembali:

410 owns /dev/pts/0
410 owns /dev/pts/1
710 owns /dev/pts/2

Yang benar, tetapi, jelas, itu sedikit bersemangat. Maksud saya, jika salah satu dari mereka membaca dalam banyak data pada saat itu mungkin akan hilang. Saya mencoba mencari cara untuk mengubah sttymode pada pty lain untuk mengirim bit berhenti dulu atau sesuatu seperti itu sehingga saya bisa memperbaikinya.

mikeserv
sumber
2

Jika Anda hanya mencari siapa yang memiliki koneksi dan dari mana mereka terhubung, perintah who akan bekerja dengan baik.

$ who
falsenames   tty8         Jun 13 16:54 (:0)
falsenames   pts/0        Jun 16 11:18 (:0)
falsenames   pts/1        Jun 16 12:59 (:0)
falsenames   pts/2        Jun 16 13:46 (:0)
falsenames   pts/3        Jun 16 14:10 (:0)
falsenames   pts/4        Jun 16 16:41 (:0)

Jika Anda juga ingin tahu apa yang sedang mendengarkan pada koneksi itu, w akan menunjukkannya di akhir.

$ w
 16:44:09 up 2 days, 23:51,  6 users,  load average: 0.26, 0.98, 1.25
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
falsenames   tty8     :0               Fri16    2days 53:36   0.59s x-session-manager
falsenames   pts/0    :0               11:18    5:25m  1:10   1:10  synergys -a 10.23.8.245 -c .synergy.conf -f -d DEBUG
falsenames   pts/1    :0               12:59    3:44m  0.05s  0.05s bash
falsenames   pts/2    :0               13:46    2:52m  0.11s  0.11s bash
falsenames   pts/3    :0               14:10    2:17   0.07s  0.07s bash
falsenames   pts/4    :0               16:41    1.00s  0.04s  0.00s w

Dan untuk mendapatkan pids, batasi ps hingga sesi tty yang Anda lihat. Sepenuhnya tidak mengganggu untuk boot.

$ ps -t pts/0 --forest 
  PID TTY          TIME CMD
23808 pts/0    00:00:00 bash
23902 pts/0    00:03:27  \_ synergys

Catatan, ini dapat menyebabkan ikan haring merah, tergantung pada waktu. Tapi ini tempat yang bagus untuk memulai.

$ tty
/dev/pts/4
$ ps -t pts/4 --forest
  PID TTY          TIME CMD
27479 pts/4    00:00:00 bash
 3232 pts/4    00:00:00  \_ ps
27634 pts/4    00:00:00 dbus-launch
Nama palsu
sumber
Terima kasih, tapi bukan itu yang saya cari. Di atas misalnya, saya ingin menemukan pid dari aplikasi terminal (Xterm / gnome-terminal ...) yang sesuai dengan /dev/pts/4, di mana Anda menjalankan wperintah itu.
Stéphane Chazelas
Maaf, benar-benar merindukan bagian pid ketika saya memindai pertama kali. Saya pikir Anda hanya ingin tahu nama proses akhir.
Nama samaran
2

Saya memiliki masalah yang sama dengan qemu, dan akhirnya saya menemukan solusi yang sangat buruk (tapi masih solusi): mem-parsing memori proses.

Ini berfungsi di sini karena saya tahu bahwa qemu menyimpan pts jarak jauh dalam sebuah string dengan format tertentu dan dialokasikan pada heap. Mungkin itu dapat bekerja dalam situasi lain juga dengan beberapa perubahan dan dengan menggunakan kembali pid dari output fuser (periksa jawaban lain).

Kode diadaptasi dari sini .

#! /usr/bin/env python

import sys
pid = sys.argv[1]

import re
maps_file = open("/proc/" + pid + "/maps", 'r')
mem_file = open("/proc/" + pid + "/mem", 'r', 0)
for line in maps_file.readlines():
    # You may want to remove the 'heap' part to search all RAM
    m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r]).*\[heap\]', line)
    if m and m.group(3) == 'r':
        start = int(m.group(1), 16)
        end = int(m.group(2), 16)
        mem_file.seek(start)
        chunk = mem_file.read(end - start)
        # You may want to adapt this one to reduce false matches
        idx = chunk.find("/dev/pts/")
        if idx != -1:
            end = chunk.find("\0", idx)
            print chunk[idx:end]
maps_file.close()
mem_file.close()
calandoa
sumber