Jak uzyskać prawdziwą nazwę terminala sterującego?

13

Jak uzyskać prawdziwą nazwę terminala sterującego (jeśli istnieje, w przeciwnym razie błąd) jako nazwę ścieżki?

Przez „prawdziwą nazwę” mam na myśli nie /dev/tty, która nie może być używana przez inne arbitralne procesy w odniesieniu do tego samego terminala. Wolę odpowiedź jako prosty kod powłoki (jak w poniższym przykładzie), jeśli to możliwe, w przeciwnym razie jako funkcję C.

Zauważ, że musi to działać, nawet jeśli standardowe wejście jest przekierowane, aby ttynarzędzie nie mogło być użyte: not a ttyw takim przypadku wystąpiłby błąd, ponieważ ttypo prostu drukuje nazwę pliku terminala podłączonego do standardowego wejścia.

Pod Linuksem można użyć:

echo "/dev/`ps -p $$ -o tty | tail -n 1`"

ale nie jest to przenośne, ponieważ zgodnie z POSIX format nazwy terminala nie jest określony .

Jeśli chodzi o funkcje C, ctermid (NULL)zwraca /dev/tty, co jest tutaj bezużyteczne.

Uwaga: zgodnie z zshdokumentacją należy to zrobić

zsh -c 'echo $TTY'

ale obecnie (wersja 5.0.7) kończy się niepowodzeniem, gdy przekierowywane jest zarówno standardowe wejście, jak i standardowe wyjście:

$ zsh -c 'echo $TTY > /dev/tty' < /dev/null
/dev/pts/9
$ zsh -c 'echo $TTY > /dev/tty' < /dev/null > /dev/null
/dev/tty
vinc17
źródło
@mikeserv Myślę, że psrozwiązanie obejmuje większość systemów (i whonie pomaga bardziej niż ps), prawdopodobnie z nieco większym kodem do obsługi samego identyfikatora (np. „04”). Zastanawiałem się, czy istnieje jeszcze bardziej przenośne rozwiązanie.
vinc17
Może to mieć związek ze sparowanymi zestawami - być może również ze starymi parami pty w stylu bsd . Nie wszystkie pty są typami UNIX 98. W każdym razie od man xterm: -Sccn Ta opcja pozwala xtermna użycie jej jako kanału we / wy dla istniejącego programu ... Wartość opcji to kilka liter nazwy pty używanej w trybie slave plus odziedziczony numer fd. Jeśli opcja zawiera znak „/”, oddziela nazwę pty od fd.
mikeserv
@mikeserv Zauważ, że rozwiązanie nie działa psz Busybox (który jest wykorzystywany przez Androida, btw), nawet pod GNU / Linux. Co rozumiesz przez „ xtermporadzisz sobie z tym 04”?
vinc17
busyboxnie jest zgodny z POSIX. toyboxrobi jednak bardzo dobrze.
mikeserv

Odpowiedzi:

8

„Terminal kontrolujący”, inaczej. ctty jest distincted z „ zacisk procesem interakcji z”.

Standardowym sposobem na uzyskanie ścieżki mola jest ctermid (3). Po wywołaniu tego, we Freebsd od wydania 10, rzeczywista ścieżka jest sprawdzana [1], podczas gdy starsze implementacje Freebsd i glibc [2] bezwarunkowo zwracają „/ dev / tty”].

ps (1) z pakietu linux procps 3.2.8, przeczytaj wpis numeryczny w / proc / * / stat [3], a następnie częściowo odejmij nazwę ścieżki , zgadując [4, 5] z powodu braku obsługi systemu [6] .

Jeśli jednak nie jesteśmy ściśle zainteresowani molem, ale każdy terminal związany ze stdio, tty (1) drukuje ścieżkę terminala podłączoną do stdin, która jest identyczna jak ttyname(fileno(stdin))w c, i alternatywą jest readlink /proc/self/fd/0.


Mniej ważna myśl dotycząca bezwarunkowego zachowania „/ dev / tty”: Określa tylko, że ciąg zwrócony przez ctermid „gdy jest używany jako nazwa ścieżki, odnosi się do bieżącego terminala sterującego”, zamiast jakiegoś prostego ”to nazwa ścieżki bieżącego terminal sterujący ". Można to interpretować jako to, że „/ dev / tty” nie jest terminalem sterującym, ale odnosi się do terminalu sterującego tylko wtedy, gdy ten sam proces go otworzy (3). Nie naruszając w ten sposób zasady „terminal może być pomostem maksymalnie na jedną sesję” [7].

Inną konsekwencją jest to, że kiedy nie mam żadnego kontrolującego terminala, ctermid nie zawodzi - takie awarie są dozwolone przez specyfikacje [8] - więc mogę tylko uświadomić sobie brak mej molo do czasu awarii kolejnego otwarcia (3), co jest w porządku, ponieważ specyfikacje mówią również, że wywołanie open (3) nie jest gwarancją sukcesu.

把 友情 留 在 无 盐
źródło
Nie jest to bardziej przenośne niż psrozwiązanie, które podałem w moim pytaniu, ponieważ nie wszystkie systemy operacyjne mają /procsystem plików. Zauważ, że pssam używa on readlink /proc/self/fd/2(który działa nawet jeśli przekierowany jest błąd standardowy).
vinc17
1
edytowane. i ps readlink na / proc / * / fd / 2 nie w celu znalezienia nabrzeża, ale w celu uzyskania dodatkowych informacji w celu zmapowania terminala numerycznego na ścieżkę, patrz link [4] [5].
把 友情 留 在 无 盐
1
Doskonała edycja. Jeśli chodzi o molo; Nie mogę mówić za vinc17, ale chociaż prawdopodobnie zawsze możesz gdzieś pisać, istnieje tylko jeden plik, który musi pozostać otwarty, aby utrzymać grupę procesów przy życiu.
mikeserv
1
@ vinc17 - jeśli masz jakieś deskryptory plików na swoim molo, możesz je odczytać tty. stderrjest prawdopodobnie najlepszy, ponieważ zostałby określony jako r / w. Tak tty <&2.
mikeserv
1
To, że dany terminal może być molo przez co najwyżej jedną sesję, nie powoduje, że glibc nie jest zgodny z jego ciągłym ctermid()zwracaniem "/dev/tty". Ta nazwa zawsze odnosi się do terminala sterującego procesu uzyskującego do niego dostęp , który różni się w zależności od sesji. Terminal jest specyficzny dla sesji, ale nazwa, pod którą można uzyskać do niego dostęp, nie musi być.
PellMel,
5

Specyfikacja POSIX naprawdę zabezpiecza swoje zakłady, jeśli chodzi o Terminal Kontrolujący , i który definiuje w ten sposób:

  • Terminal kontrolny
    • Pytanie o to, który z kilku ewentualnie specjalnych plików odnoszących się do terminala nie zostało uwzględnione w POSIX.1. Nazwa ścieżki /dev/ttyjest synonimem terminala sterującego powiązanego z procesem.

To jest na liście Definicje - i to wszystko, co tam jest. Ale w Ogólnym interfejsie terminalowym mówi się coś więcej:

  • Terminal może należeć do procesu jako terminal sterujący. Każdy proces sesji, który ma terminal kontrolny, ma ten sam terminal kontrolny. Terminal może być terminalem sterującym dla co najwyżej jednej sesji. Terminal sterujący dla sesji jest przydzielany przez lidera sesji w sposób zdefiniowany w implementacji. Jeśli lider sesji nie ma kontrolującego terminala i otworzy plik urządzenia terminala, który nie jest jeszcze powiązany z sesją bez użycia opcji O_NOCTTY (patrz open ()), określa się, czy terminal stanie się kontrolującym terminalem sesji lider.

  • Terminal sterujący jest dziedziczony przez proces potomny podczas wywołania funkcji fork (). Proces zrzeka się terminala sterującego, gdy tworzy nową sesję zsetsid()funkcjonować; inne procesy pozostające w starej sesji, które miały ten terminal jako terminal kontrolny, nadal go mają. Po zamknięciu ostatniego deskryptora pliku w systemie (niezależnie od tego, czy znajduje się w bieżącej sesji) powiązanego z terminalem sterującym, nie jest określone, czy wszystkie procesy, które miały ten terminal jako terminal kontrolny, przestają mieć terminal kontrolny. Czy i w jaki sposób lider sesji może ponownie uzyskać terminal kontrolny po tym, jak terminal kontrolny został zrzeczony w ten sposób, nie jest określone. Proces nie zrzeka się terminala sterującego, po prostu zamykając wszystkie deskryptory plików powiązane z terminalem sterującym, jeśli inne procesy nadal go otwierają.

Zostało jeszcze wiele nieokreślonych - i szczerze mówiąc, myślę, że to ma sens. Chociaż terminal jest kluczowym interfejsem użytkownika, w niektórych przypadkach jest to również wiele innych rzeczy - na przykład rzeczywisty sprzęt, a nawet rodzaj drukarki - ale w wielu przypadkach jest to praktycznie nic - jak xtermemulator, który jest tylko emulatorem . Trudno jest tu określić konkretnie - i nie sądzę, żeby i tak byłby w interesie Uniksa, ponieważ terminale robią znacznie więcej niż Unix.

W każdym razie POSIX jest dość niepewny, jak pspowinien zachowywać się na molo.

Jest -aprzełącznik:

  • Napisz informacje o wszystkich procesach związanych z terminalami. Implementacje mogą pomijać liderów sesji z tej listy.

Wspaniały. Liderzy sesji mogą zostać pominięci. To nie jest bardzo pomocne.

I -t:

  • Napisz informacje o procesach związanych z terminalami podane w termistrze. Wniosek powinien zapewniać, aby termista był pojedynczym argumentem w postaci <blank>listy oddzielonej przecinkami. Identyfikatory terminali należy podawać w formacie zdefiniowanym przez implementację .

... co jest kolejnym rozczarowaniem. Ale mówi się tak o systemach XSI:

  • W systemach zgodnych z XSI należy je podać w jednej z dwóch form: nazwa pliku urządzenia (na przykład tty04) lub, jeśli nazwa pliku urządzenia zaczyna się od tty, tylko identyfikator następujący po znakach tty (na przykład 04) .

To trochę lepiej, ale to nie jest ścieżka. Również w systemach XSI jest -dprzełącznik:

  • Napisz informacje dla wszystkich procesów, z wyjątkiem liderów sesji.

... co jest przynajmniej jasne. Możesz także określić -oprzełącznik utput za pomocą ciągu ttyformatu, ale, jak zauważyłeś, jego format wyjściowy jest zdefiniowany w implementacji. Mimo to uważam, że jest tak dobry, jak to tylko możliwe. Myślę, że - przy dużym nakładzie pracy - powyższe przełączniki w połączeniu z innymi narzędziami mogą dać ci całkiem niezły ballpark. Szczerze mówiąc, nie wiem, kiedy / jak to się dla ciebie psuje - i nie byłem w stanie wyobrazić sobie takiej sytuacji. Myślę jednak, że jeśli dodamy fuseri findmożemy zweryfikować ścieżkę.

exec 2<>/dev/null
ctty=$(sh -c 'ps -p "$$" -o tty=' <&2)
sid=$(sh -c 'ps -Ao pid= -o tty=|
      grep '"$ctty$"' | 
      grep -Fv "$(ps -do pid=)"'  <&2)
find / -type c -name "*${ctty##*/}*" \
       -exec fuser -uv {} \; 2>&1  |
grep ".*$ctty.*${sid%%"$ctty"*}"

Wszystko /dev/nullpo to, aby pokazać, że może działać, gdy żadna z podpowłok poszukiwawczych nie ma żadnego z 0,1,2 podłączonego do nabrzeża. W każdym razie, to drukuje:

/dev/pts/3:          mikeserv   3342 F.... (mikeserv)zsh

Teraz powyższe uzyskuje pełną ścieżkę na moim komputerze i wyobrażam sobie, że w większości przypadków byłoby to możliwe. Mogę sobie również wyobrazić, że to może zawieść. To tylko szorstka heurystyka.

Może się to nie udać z wielu innych powodów, ale jeśli jesteś w systemie, który pozwala liderowi sesji zrzec się wszystkich deskryptorów na molo, a mimo to pozostać sid, jak pozwala specyfikacja, to na pewno nie pomoże. To powiedziawszy, myślę, że w większości przypadków może to być dość dobre oszacowanie.

Oczywiście najłatwiejszą rzeczą do zrobienia, jeśli masz jakieś deskryptory podłączone do swojego molo, jest po prostu ...

tty <&2

...lub podobne.

mikeserv
źródło