Як ми можемо знати, хто на іншому кінці псевдотермінального пристрою?


26

Якщо я роблю:

echo foo > /dev/pts/12

Деякий процес прочитає це foo\nз його дескриптора файлів на стороні головного.

Чи є спосіб дізнатися, що це (ті) процеси (є)?

Або іншими словами, як я міг дізнатися, який xterm / sshd / script / screen / tmux / очікувати / socat ... знаходиться на іншому кінці /dev/pts/12?

lsof /dev/ptmxпідкаже мені процеси, які мають дескриптори файлів на головній стороні будь-якого pty. Сам процес може використовувати ptsname()( TIOCGPTNioctl), щоб знайти підлеглий пристрій на основі власного fd на головну сторону, щоб я міг використовувати:

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

для кожного повернутого pid / fd lsofдля створення цього відображення, але чи існує більш прямий, надійний і менш нав'язливий спосіб отримати цю інформацію?


Це те, чого ти хочеш? sudo find /proc/*/fd/0 -ls | grep '/dev/pts/4', надасть перелік PID ( /proc/PID) як вихід.
slm

@slm, ні, іншими словами, я хочу дізнатися, який xterm / sshd / script / screen / tmux / очікувати / socat ... знаходиться на іншому кінці /dev/pts/4. Зазвичай це буде загальним предком тих процесів, які /dev/pts/4відкриті, але не обов'язково.
Стефан Шазелас

1
Ще гірше з сокетами - вам потрібен налагоджувач ядра!
Жиль "ТАК - перестань бути злим"

1
@Falsenames - Я зрозумів, що питання означає - можливо, неправильно - не те, який процес передається прочитаним даним - наприклад, перша оболонка, що викликається в терміналі, - але який процес насправді читає з головного боку. Наприклад, якщо я запускаю оболонку screen, вона screenвиділяє та активно управляє підлеглим pty протягом життя пристрою, але - як я думаю - оболонка робиться лідером процесів для цього tty і так, як ваш Вихід показує, ви отримуєте bashабо що ще psне screen. Я простежив кілька xtermsназад до xtermpid на основі, /proc/locksале це було вільно.
mikeserv

Відповіді:


3

Спочатку я спробував простежити кілька xtermсекунд назад до xtermпід на основі інформації, яку я знайшов, /proc/locksале це було вільно. Я думаю, це спрацювало, я думаю, але це було в кращому випадку нескінченно - я не повністю розумію всю інформацію, яку надає файл, і лише відповідав тому, що, здавалося, відповідає його вмісту та відомим термінальним процесам.

Потім я спробував спостерігати lsof/straceза активним write/talkпроцесом між ptys. Я ніколи раніше не використовував жодної програми, але вони, схоже, покладаються на них utmp. Якщо мій цільовий pty utmpз будь-якої причини не мав запису, вони обоє відмовилися визнати, що він існує. Можливо, є шлях, але я був досить розгублений, щоб відмовитися від цього.

Я спробував щось udevadmвідкрити із 136 та 128 основними вузлами пристроїв, як рекламуються, ptsі ptmвідповідно /proc/tty/drivers, але мені також не вистачає будь-якого дуже корисного досвіду роботи з цим інструментом, і в черговий раз виявилося нічого істотного. Цікаво, що, проте, я помітив, що :minдіапазон для обох типів пристроїв був перерахований у приголомшливому стані 0-1048575.

Тільки після того, як я переглянув цей документ ядра , я почав думати про проблему з точки зору mounts. Я раніше це читав кілька разів, але коли тривалі дослідження в цьому рядку привели мене до цього патчету 2012 /dev/ptsроку, у мене виникла ідея:

sudo fuser -v /dev/ptmx

Я подумав, що я зазвичай використовую для асоціювання процесів з mount? І точно:

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

Тож із цією інформацією я можу зробити, наприклад, з terminology:

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

Як бачите, за допомогою невеликого явного тестування такий процес може бути зроблений для досить надійного виведення основного процесу довільного pty. Щодо розеток, я впевнений, що можна було б підійти до нього з цього напрямку, використовуючи socatна відміну від налагоджувача, але я ще не повинен розібратися, як це зробити. І все-таки, я підозрюю, що ssможе допомогти, якщо ви з ним більше знайомі, ніж я:

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

Тож я налаштував це трохи більш чітким тестуванням:

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

Він друкує $$кількість \0нульових байтів до кожного pty та перевіряє io кожного основного процесу проти попередньої перевірки. Якщо різниця є, $$то вона асоціює pid з pty. Це здебільшого працює. Я маю на увазі, для мене це повертається:

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

Що правильно, але, вочевидь, це трохи расично. Я маю на увазі, якби хтось із тих, хто читав у той час купу даних, він, ймовірно, пропустив би. Я намагаюся розібратися, як змінити sttyрежими на інший pty, щоб спочатку надіслати стоп-біт або щось подібне, щоб я міг це виправити.


2

Якщо ви просто шукаєте, хто належить до з'єднання та звідки вони з'єднані, команда who буде добре працювати.

$ 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)

Якщо ви також хочете знати, що слухає на цьому з’єднанні, w покаже це наприкінці.

$ 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

А щоб отримати підказки, обмежте ps на сеанс, який ви шукаєте. Повністю ненав’язливий до завантаження.

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

Зауважте, це може призвести до появи червоних оселедців, залежно від часу. Але це добре місце для початку.

$ 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

Дякую, але це не те, що я шукаю. Наприклад, вище, я б хотів знайти pid термінальної програми (Xterm / gnome-terminal ...), який відповідає тому /dev/pts/4, де ви запустили цю wкоманду.
Стефан Шазелас

Вибачте, повністю пропустив пид-частину під час першого сканування. Я думав, ти просто хочеш дізнатися кінцеву назву процесу.
Підроблені імена

2

У мене була така ж проблема з qemu, і я нарешті знайшов дуже погане рішення (але все-таки рішення): аналіз процесорної пам'яті.

Це працює тут, тому що я знаю, що qemu зберігає віддалені пункти в рядку з певним форматом і виділяється на купі. Можливо, це може працювати і в інших ситуаціях, виконавши декілька змін та повторне використання pid з виходу термофіксатора (перевірте іншу відповідь).

Звідси код адаптований .

#! /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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.