Làm thế nào chúng ta có thể biết ai ở đầu kia của thiết bị đầu cuối giả?


26

Nếu tôi làm một:

echo foo > /dev/pts/12

Một số quá trình sẽ đọc nó foo\ntừ mô tả tập tin của nó đến phía chủ.

Có cách nào để tìm hiểu xem (những) quá trình đó là gì không?

Hay nói cách khác, làm thế nào tôi có thể tìm ra xterm / sshd / script / screen / tmux / wish / socat ... ở đầu kia của /dev/pts/12?

lsof /dev/ptmxsẽ cho tôi biết các quy trình có mô tả tập tin ở phía chủ của bất kỳ pty nào. Bản thân một quá trình có thể sử dụng ptsname()( TIOCGPTNioctl) để tìm ra thiết bị nô lệ dựa trên fd của chính nó cho phía chủ, vì vậy tôi có thể sử dụng:

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

cho mỗi pid / fd được trả về lsofđể xây dựng ánh xạ đó, nhưng có cách nào trực tiếp hơn, đáng tin cậy hơn và ít xâm phạm hơn để có được thông tin đó không?


Đây có phải là những gì bạn muốn? sudo find /proc/*/fd/0 -ls | grep '/dev/pts/4', sẽ cung cấp danh sách các PID ( /proc/PID) làm đầu ra.
slm

@slm, không, nói cách khác, tôi muốn tìm hiểu mà xterm / sshd / script / màn hình / tmux / mong đợi / socat ... là tại khác cuối /dev/pts/4. Thông thường, đó sẽ là tổ tiên chung của các quá trình đã /dev/pts/4mở, nhưng không nhất thiết phải như vậy.
Stéphane Chazelas

1
Nó thậm chí còn tệ hơn với các socket - bạn cần một trình gỡ lỗi kernel!
Gilles 'SO- ngừng trở nên xấu xa'

1
@Falsenames - Tôi hiểu câu hỏi có nghĩa là - có lẽ không chính xác - không phải quá trình nào được truyền dữ liệu đọc - chẳng hạn như trình bao đầu tiên được gọi trong thiết bị đầu cuối - nhưng quá trình thực sự đọc nó từ phía chủ. Chẳng hạn, nếu tôi khởi chạy shell screen, nó sẽ screenphân bổ và chủ động quản lý nô lệ pty cho vòng đời của thiết bị, nhưng - như tôi nghĩ - lớp vỏ được tạo ra dẫn đầu quy trình cho tty đó và vì thế, như của bạn đầu ra cho thấy, bạn nhận được bashhoặc bất cứ điều gì từ pskhông screen. Tôi truy tìm một số xtermstrở lại xtermpid dựa trên /proc/locksnhưng nó đã lỏng lẻo.
mikeerv

Câu trả lời:


3

Lúc đầu, tôi đã cố gắng truy tìm một vài xtermgiây trở lại xtermpid dựa trên thông tin tôi tìm thấy /proc/locksnhưng nó bị lỏng lẻo. Ý tôi là, nó hoạt động, tôi nghĩ vậy, nhưng đó là tình huống tốt nhất - tôi không hiểu đầy đủ tất cả thông tin mà tập tin cung cấp và chỉ khớp với những gì dường như tương ứng giữa nội dung của nó và các quy trình đầu cuối đã biết.

Sau đó, tôi đã cố gắng xem lsof/stracetrên một write/talkquá trình hoạt động giữa các ptys. Tôi chưa bao giờ thực sự sử dụng một trong hai chương trình trước đây, nhưng chúng dường như dựa vào utmp. Nếu pty mục tiêu của tôi không có utmpmục vì bất kỳ lý do gì cả hai đều từ chối thừa nhận rằng nó tồn tại. Có lẽ có một cách xung quanh đó, nhưng tôi đã đủ bối rối để từ bỏ nó.

Tôi đã thử một số udevadmkhám phá với 136 và 128 nút thiết bị số chính như được quảng cáo ptsptmtương ứng /proc/tty/drivers, nhưng tôi cũng thiếu bất kỳ trải nghiệm rất hữu ích nào với công cụ đó và một lần nữa không có gì đáng kể. Thật thú vị, mặc dù, tôi nhận thấy :minphạm vi cho cả hai loại thiết bị được liệt kê ở mức đáng kinh ngạc 0-1048575.

Tuy nhiên, cho đến khi tôi xem lại tài liệu hạt nhân này , tôi mới bắt đầu nghĩ về vấn đề này mount. Tôi đã đọc điều đó nhiều lần trước đây nhưng khi tiếp tục nghiên cứu về dòng đó đã đưa tôi đến bản năm 2012 này,/dev/pts tôi có một ý tưởng:

sudo fuser -v /dev/ptmx

Tôi nghĩ những gì tôi thường sử dụng để liên kết các quá trình với một mount? Và chắc chắn:

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

Vì vậy, với thông tin đó tôi có thể làm, ví dụ từ terminology:

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

Như bạn có thể thấy, với một chút thử nghiệm rõ ràng, một quy trình như vậy có thể được thực hiện để tạo ra một cách đáng tin cậy quá trình tổng thể của một pty tùy ý. Về các ổ cắm, tôi khá chắc chắn một người có thể tiếp cận nó từ hướng đó cũng socatnhư sử dụng trái ngược với trình gỡ lỗi, nhưng tôi vẫn chưa nói rõ làm thế nào. Tuy nhiên, tôi nghi ngờ sscó thể giúp đỡ nếu bạn quen thuộc với nó hơn tôi:

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

Vì vậy, tôi đã thiết lập nó với một thử nghiệm rõ ràng hơn một chút, thực sự:

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

Nó in $$num \0null null cho mỗi pty và kiểm tra io của mỗi tiến trình chính so với kiểm tra trước đó. Nếu sự khác biệt là $$nó liên kết pid với pty. Điều này chủ yếu hoạt động. Ý tôi là, đối với tôi, nó trả về:

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

Điều này là chính xác, nhưng, rõ ràng, đó là một chút không phù hợp. Ý tôi là, nếu một trong những người khác đang đọc một loạt dữ liệu vào thời điểm đó thì có lẽ nó sẽ bị mất. Tôi đang cố gắng tìm ra cách thay đổi các sttychế độ trên một pty khác để gửi bit stop trước hoặc một cái gì đó tương tự để tôi có thể khắc phục điều đó.


2

Nếu bạn chỉ tìm kiếm ai sở hữu kết nối và nơi họ được kết nối, lệnh ai sẽ hoạt động tốt.

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

Nếu bạn cũng muốn biết những gì đang nghe trên kết nối đó, w sẽ hiển thị ở cuối.

$ 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

Và để có được các pids, hãy giới hạn một ps trong phiên tty bạn đang xem. Hoàn toàn không phô trương để khởi động.

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

Lưu ý, điều này có thể dẫn đến cá trích đỏ, tùy thuộc vào thời gian. Nhưng đó là một nơi tốt để bắt đầu.

$ 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

Cảm ơn, nhưng đó không phải là những gì tôi đang tìm kiếm. Ví dụ ở trên, tôi muốn tìm pid của ứng dụng đầu cuối (Xterm / gnome-terminal ...) tương ứng với /dev/pts/4, nơi bạn đã chạy wlệnh đó .
Stéphane Chazelas

Xin lỗi, hoàn toàn bỏ qua phần pid khi tôi quét qua lần đầu tiên. Tôi nghĩ bạn chỉ muốn biết tên quá trình kết thúc.
Falsenames

2

Tôi có cùng một vấn đề với qemu và cuối cùng tôi đã tìm thấy một giải pháp rất tệ (nhưng vẫn là một giải pháp): phân tích bộ nhớ quá trình.

Điều này đang hoạt động ở đây bởi vì tôi biết rằng qemu đang lưu trữ các pts từ xa trong một chuỗi với định dạng cụ thể và được phân bổ trên heap. Có thể nó cũng có thể hoạt động trong các tình huống khác với một vài thay đổi và bằng cách sử dụng lại pid từ đầu ra của bộ nhiệt áp (kiểm tra câu trả lời khác).

Mã được điều chỉnh từ đây .

#! /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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.