Bagaimana cara mendapatkan nama asli dari terminal pengendali?

13

Bagaimana seseorang bisa mendapatkan nama asli dari terminal pengendali (jika ada, kalau tidak kesalahan) sebagai pathname?

Dengan "nama asli", maksud saya tidak /dev/tty, yang tidak dapat digunakan oleh proses arbitrer lainnya untuk merujuk ke terminal yang sama. Saya lebih suka jawabannya sebagai kode shell sederhana (seperti contoh di bawah) jika memungkinkan, jika tidak sebagai fungsi C.

Perhatikan bahwa ini harus bekerja bahkan jika input standar dialihkan, sehingga ttyutilitas tidak dapat digunakan: orang akan mendapatkan not a ttykesalahan dalam kasus seperti itu, karena ttyhanya mencetak nama file terminal yang terhubung ke input standar.

Di Linux, seseorang dapat menggunakan:

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

tetapi ini tidak portabel, seperti menurut POSIX, format nama terminal tidak ditentukan .

Mengenai fungsi C, ctermid (NULL)pengembalian /dev/tty, yang tidak berguna di sini.

Catatan: menurut zshdokumentasi, seseorang harus dapat melakukannya

zsh -c 'echo $TTY'

tetapi ini saat ini (versi 5.0.7) gagal ketika input standar dan output standar dialihkan:

$ zsh -c 'echo $TTY > /dev/tty' < /dev/null
/dev/pts/9
$ zsh -c 'echo $TTY > /dev/tty' < /dev/null > /dev/null
/dev/tty
vinc17
sumber
@ mikeserv Saya pikir pssolusinya mencakup sebagian besar sistem (dan whotidak membantu lebih dari ps), mungkin dengan kode sedikit lebih untuk menangani pengidentifikasi saja (seperti "04"). Saya bertanya-tanya apakah ada solusi yang lebih portabel.
vinc17
Mungkin ada hubungannya dengan set pre-paired - pasangan bty gaya lama bsd juga, mungkin. Tidak semua ptys adalah tipe UNIX 98. Anyway, from man xterm: -Sccn Opsi ini memungkinkan xtermuntuk digunakan sebagai saluran i / o untuk program yang ada ... Nilai opsi adalah beberapa huruf dari nama pty untuk digunakan dalam mode slave, ditambah nomor fd yang diwarisi. Jika opsi berisi karakter "/", itu membatasi nama pty dari fd.
mikeserv
@mikeserv Perhatikan bahwa solusinya tidak bekerja psdari busybox (yang digunakan oleh Android, BTW), bahkan di bawah GNU / Linux. Apa yang Anda maksud dengan " xtermbisa mengatasinya 04"?
vinc17
busyboxbukan POSIX-conformant. toyboxNamun, sangat baik.
mikeserv

Jawaban:

8

"Terminal pengendali" alias. CTTY, adalah distincted dari " yang proses terminal berinteraksi dengan".

Cara standar untuk mendapatkan jalur ctty adalah ctermid (3). Saat memanggil ini, Dalam freebsd sejak rilis 10, jalan yang sebenarnya dilihat [1], sementara freebsd dan implementasi glibc yang lebih lama [2] tanpa syarat mengembalikan "/ dev / tty"].

ps (1) dari paket linux procps 3.2.8, baca entri numerik di / proc / * / stat [3], dan kemudian kurangi pathname sebagian dengan menebak [4, 5] karena kurangnya dukungan sistem [6] .

Namun jika kita tidak benar-benar tertarik pada ctty tetapi terminal apa pun yang terkait dengan stdio, tty (1) mencetak jalur terminal yang terhubung ke stdin, yang identik dengan ttyname(fileno(stdin))di c, dan alternatifnya adalah readlink /proc/self/fd/0.


Pikiran yang kurang penting mengenai perilaku "/ dev / tty" tanpa syarat: Spesifikasi hanya mengatakan string yang dikembalikan oleh ctermid "ketika digunakan sebagai nama jalur, merujuk ke terminal pengendali saat ini", alih-alih secara langsung "adalah nama jalur saat ini mengendalikan terminal ". Ini mungkin ditafsirkan sebagai "/ dev / tty" bukan terminal pengendali, tetapi hanya merujuk ke terminal pengendali jika proses yang sama terbuka (3). Sehingga tidak melanggar "terminal mungkin ctty untuk paling banyak satu sesi" aturan [7].

Konsekuensi lain adalah bahwa ketika saya tanpa terminal pengendali, ctermid tidak gagal - kegagalan seperti itu diizinkan oleh spesifikasi [8] -, jadi hanya saya yang bisa mengetahui ketidakberdayaan saya sampai gagal membuka berikutnya (3), yang tidak apa-apa karena spesifikasi juga mengatakan panggilan terbuka (3) tidak dijamin untuk berhasil.

把 友情 留 在 无 盐
sumber
Ini tidak lebih portabel daripada pssolusi yang saya berikan dalam pertanyaan saya, karena tidak semua OS memiliki /procsistem file. Perhatikan bahwa psitu sendiri menggunakan tautan baca aktif /proc/self/fd/2(yang berfungsi meskipun kesalahan standar dialihkan).
vinc17
1
diedit. dan ps readlink di / proc / * / fd / 2 bukan untuk menemukan ctty, tetapi untuk mencari informasi tambahan untuk memetakan terminal numerik ke path, lihat tautan [4] [5].
把 友情 留 在 无 盐
1
Suntingan yang bagus. Tentang ctty; Saya tidak dapat berbicara untuk vinc17, tetapi walaupun Anda mungkin selalu dapat menulis ke suatu tempat, hanya ada satu file yang harus tetap terbuka untuk menjaga grup proses Anda tetap hidup.
mikeserv
1
@ vinc17 - jika Anda memiliki setiap deskriptor file terbuka pada CTTY Anda maka Anda dapat membacanya dengan tty. stderrmungkin yang terbaik karena itu spec'd menjadi terbuka r / w. Jadi tty <&2.
mikeserv
1
Bahwa terminal yang diberikan mungkin ctty untuk paling banyak satu sesi tidak membuat glibc tidak sesuai untuk ctermid()selalu kembali "/dev/tty". Nama itu selalu merujuk ke terminal pengontrol dari proses mengaksesnya , yang bervariasi berdasarkan sesi. Terminal adalah sesi khusus, tetapi nama yang diaksesnya tidak perlu.
PellMel
5

Spec POSIX benar-benar melakukan lindung nilai taruhannya di mana Terminal Pengendali terkait, dan yang didefinisikan sebagai berikut:

  • Mengontrol Terminal
    • Pertanyaan yang mana dari beberapa file khusus yang merujuk ke terminal yang dimaksud tidak dibahas dalam POSIX.1. Pathname /dev/ttyadalah sinonim untuk terminal pengendali yang terkait dengan suatu proses.

Itu ada dalam daftar Definisi - dan hanya itu yang ada di sana. Namun dalam General Terminal Interface , beberapa kata:

  • Terminal dapat menjadi bagian dari suatu proses sebagai terminal pengendali. Setiap proses sesi yang memiliki terminal pengendali memiliki terminal pengendali yang sama. Terminal mungkin merupakan terminal pengendali untuk paling banyak satu sesi. Terminal pengendali untuk suatu sesi dialokasikan oleh pemimpin sesi dengan cara yang ditentukan implementasi. Jika pemimpin sesi tidak memiliki terminal pengendali, dan membuka file perangkat terminal yang belum dikaitkan dengan sesi tanpa menggunakan opsi O_NOCTTY (lihat buka ()), itu ditentukan implementasi apakah terminal menjadi terminal pengendali sesi pemimpin.

  • Terminal pengendali diwarisi oleh proses anak selama panggilan fungsi fork (). Suatu proses melepaskan terminal pengendali ketika membuat sesi baru dengansetsid()fungsi; proses lain yang tersisa di sesi lama yang memiliki terminal ini sebagai terminal pengendali mereka terus memilikinya. Setelah penutupan deskriptor file terakhir dalam sistem (apakah itu dalam sesi saat ini atau tidak) yang terkait dengan terminal pengendali, tidak ditentukan apakah semua proses yang memiliki terminal itu sebagai terminal pengendali berhenti memiliki terminal pengendali. Apakah dan bagaimana pemimpin sesi dapat memperoleh kembali terminal pengendali setelah terminal pengendali dilepaskan dengan cara ini tidak ditentukan. Suatu proses tidak melepaskan terminal pengendali hanya dengan menutup semua deskriptor file yang terkait dengan terminal pengendali jika proses lain terus membukanya.

Masih banyak yang belum ditentukan - dan jujur ​​saya pikir itu masuk akal. Walaupun terminal adalah antarmuka pengguna utama, terminal juga semua jenis hal lain dalam beberapa kasus - seperti perangkat keras yang sebenarnya, atau bahkan jenis printer - tetapi dalam banyak kasus itu hampir tidak ada sama sekali - seperti terminal xtermyang hanya merupakan emulator . Sulit untuk mendapatkan spesifik di sana - dan saya tidak berpikir itu akan banyak demi kepentingan Unix, karena terminal melakukan lebih banyak daripada Unix.

Bagaimanapun, POSIX juga cukup rapuh tentang bagaimana psseharusnya berperilaku di mana ctty bersangkutan.

Itu -asaklarnya:

  • Tulis informasi untuk semua proses yang terkait dengan terminal. Implementasi dapat menghilangkan pemimpin sesi dari daftar ini.

Bagus. Pemimpin sesi dapat dihilangkan. Itu tidak terlalu membantu.

Dan -t:

  • Tulis informasi untuk proses yang terkait dengan terminal yang diberikan dalam termlist. Aplikasi harus memastikan bahwa termlist adalah argumen tunggal dalam bentuk <blank>daftar yang dipisahkan oleh koma. Pengidentifikasi terminal harus diberikan dalam format yang ditentukan implementasi .

... yang merupakan kekecewaan lainnya. Tapi itu terus mengatakan ini tentang sistem XSI:

  • Pada sistem yang sesuai dengan XSI, mereka harus diberikan dalam salah satu dari dua bentuk: nama file perangkat (misalnya, tty04) atau, jika nama file perangkat dimulai dengan tty, hanya pengidentifikasi yang mengikuti karakter tty (misalnya, 04) .

Itu sedikit lebih baik, tetapi bukan jalan. Juga pada sistem XSI ada -dsaklar:

  • Tulis informasi untuk semua proses, kecuali pemimpin sesi.

... yang setidaknya jelas. Anda dapat menentukan -osaklar utput juga dengan ttystring format, tetapi, seperti yang telah Anda catat, format outputnya ditentukan oleh implementasi. Namun, saya pikir itu sama baiknya dengan yang didapat. Saya berpikir bahwa - dengan banyak pekerjaan - sakelar di atas dikombinasikan dengan beberapa utilitas lain dapat membuat Anda rata-rata yang bagus. Jujur saja, saya tidak tahu kapan / bagaimana hal itu merusak Anda - dan saya belum bisa membayangkan situasi di mana itu akan terjadi. Tapi, saya pikir mungkin jika kita menambahkan fuserdan findkita dapat memverifikasi jalurnya.

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"*}"

Hal /dev/nullitu hanya untuk menunjukkan bahwa itu bisa bekerja ketika tidak ada subkulit pencarian yang memiliki 0,1,2 yang terhubung ke ctty. Bagaimanapun, itu mencetak:

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

Sekarang yang di atas mendapatkan jalur penuh pada mesin saya, dan saya membayangkan hal itu akan terjadi pada kebanyakan orang. Saya juga bisa membayangkan itu bisa gagal. Itu hanya heuristik kasar.

Ini mungkin gagal karena banyak alasan lain mungkin, tetapi jika Anda berada di sistem yang memungkinkan ketua sesi untuk melepaskan semua deskriptor ke ctty dan tetap sebagai sid maka sebagai spec memungkinkan, maka ini jelas tidak akan membantu. Yang mengatakan, saya pikir ini bisa mendapatkan perkiraan yang cukup bagus dalam banyak kasus.

Tentu saja yang paling mudah hal yang harus dilakukan jika Anda memiliki setiap deskriptor terhubung ke CTTY Anda hanya ...

tty <&2

... atau serupa.

mikeserv
sumber