Es scheint, dass ich durch eine einfache Shell-Weiterleitung /dev/ptmx
ein neues Pseudo-Terminal bekomme.
$ ls /dev/pts; ls /dev/pts </dev/ptmx
0 1 2 ptmx
0 1 2 3 ptmx
Es verschwindet, sobald der Prozess, dem der fd-Anspruch gehört, beendet wird /dev/ptmx
, es ist jedoch ganz einfach, es beizubehalten:
$ PS1='<$(ls -1 "$@" /dev/pts/*|uniq -u)> $ ' \
exec 3<>/dev/ptmx "$0" -si -- /dev/pts/*
</dev/pts/3> $ ls /dev/pts; exec 3>&-
0 1 2 3 ptmx
<> $ ls /dev/pts; exec 3<>/dev/ptmx
0 1 2 ptmx
</dev/pts/3> $ exit
Es sieht also so aus, als ob das einfache open()
on /dev/ptmx
ausreicht, um ein Pseudoterminal zu erhalten. Ich schätze, das würde die Shell dazu bringen -(oder der Prozess, für den die Umleitung erfolgt)- der Eigentümer der Masterseite der Pty.
Aber wie ordne ich die Slave-Seite zu - bzw.dürfenIch weise die Slave-Seite zu? Und wenn ja, was kann ich damit machen? Kann ich die Lese-/Schreib-/Spülzeiten beeinflussen stty
? Wird ein Prozess auf der Slave-Seite beim Referenzieren auf meinen neuen PTY verweisen /dev/tty
?
Antwort1
Wie @muru in den Kommentaren anmerkt, scheint es also keine einfache Möglichkeit zu geben, das für Sie erstellte PTY nur mit der Shell zu verbinden. Ich habe alles bis auf den unlockpt()
Teil geschafft. Laut etwas, das ich gelesen habeHierEs kann sein, dass es im Kernel einige Optionen zur Kompilierungszeit gibt, um neu erstellte PTY-Sperren zu deaktivieren, aber das wollte ich nicht. Also habe ich etwas anderes gemacht.
Ich brauchte es grantpt()
eigentlich nicht. Laut der Beschreibung gefundenHieres ändert lediglich die UID/GID für /dev/pts/[num]
die Gerätedatei. Aber man mount
es gibt einfachere Möglichkeiten, damit umzugehen. Hier sind einige devpts
Mount-Optionen:
uid=value
Undgid=value
- Dies setzt den Besitzer oder die Gruppe der neu erstellten PTYs auf die angegebenen Werte. Wenn nichts angegeben ist, werden sie auf die UID und GID des Erstellungsprozesses gesetzt. Wenn es beispielsweise einen tty Gruppe mit GID 5, dann
gid=5
werden neu erstellte PTYs zur GruppettyGruppe.
- Dies setzt den Besitzer oder die Gruppe der neu erstellten PTYs auf die angegebenen Werte. Wenn nichts angegeben ist, werden sie auf die UID und GID des Erstellungsprozesses gesetzt. Wenn es beispielsweise einen tty Gruppe mit GID 5, dann
Das war auf meinem System standardmäßig schon der Fall. Aber nachdem ich das gelesen hatte, wurde mir klar, dass ich vielleicht doch etwas ändern wollte. Der nächste Abschnitt lautet:
ptmxmode=value
- Legen Sie den Modus für den neuen ptmx-Geräteknoten im
devpts
Dateisystem fest. - Mit der Unterstützung für mehrere Instanzen von
devpts
(siehenewinstance
Option oben) verfügt jede Instanz über einen privaten ptmx-Knoten im Stammverzeichnis desdevpts
Dateisystems(normalerweise/dev/pts/ptmx
). - Aus Kompatibilitätsgründen mit älteren Versionen des Kernels lautet der Standardmodus des neuen ptmx-Knotens
0000
.ptmxmode=value
Gibt einen nützlicheren Modus für den ptmx-Knoten an und wird dringend empfohlen, wenn dienewinstance
Option angegeben ist.
- Legen Sie den Modus für den neuen ptmx-Geräteknoten im
Obwohl es auch ohne funktioniert hätte, gefiel mir die Idee und ich habe es auf 0640
die empfohlene Einstellung gesetzt.Kernel-Dokumentation. Der Link zur Kernel-Dokumentation geht übrigens näher auf die newinstance
Mount-Option ein – die ziemlich cool ist und es Ihnen im Grunde ermöglicht, pro Mount eine separat nach Namen sortierte Gruppe von PTYs zu erhalten /dev/ptmx
.
Wie dem auch sei,etwas anderesbelief sich im Wesentlichen auf:
mount -o remount,newinstance,gid=5,ptmxmode=0640 /dev/pts
mount --bind /dev/pts/ptmx /dev/ptmx
...wie in den Kernel-Dokumenten empfohlen – siehe den Link, warum. Ich habe die Wirkung der obigen Befehle auch dauerhaft gemacht, indem ich ein paar Zeilen zu meinem hinzugefügt habe /etc/fstab
.
Und...
<<\C cc -xc - -o pts
#include <stdio.h>
int main(int argc, char *argv[]) {
if(unlockpt(0)) return 2;
char *ptsname(int fd);
printf("%s\n",ptsname(0));
return argc - 1;
}
C
Dadurch wird einfach ein kleines C-Programm kompiliert, das versucht, unlockpt()
seinen Standarddin aufzurufen und bei Erfolg den Namen des neu erstellten und entsperrten PTY ausgibt stdout
oder andernfalls stillschweigend 2 zurückgibt.
Sobald das erledigt war, konnte ich mein eigenes erstellenabgeschirmtProzesse wie:
exec 3<>/dev/ptmx
...um dann das Master-seitige FD in der aktuellen Shell zu erhalten...
(setsid -c "$0" -i 2>&1|tee log) <>"$(./pts <&3)" 3>&- >&0 &
Dadurch wird am anderen Ende des Pseudoterminals im Hintergrund eine interaktive Shell ausgeführt. Sie interpretiert alles, was ausgegeben wird, >&3
als Benutzereingabe.
mikeserv@localhost$ echo echo hey >&3
mikeserv@localhost$ cat log
$ hey
$
mikeserv@localhost$ echo echo hey >&3
mikeserv@localhost$ cat log
$ hey
$ hey
$
Damit erhalte ich im Grunde einen im Hintergrund laufenden, protokollierten und interaktiven Interpreter(oder alles andere, was ich darauf ausführen möchte)ala screen
ohne so viel Overhead und mit jedem Dateideskriptor meiner Wahl.
Das Master-seitige FD meiner aktuellen Shell ist die einzige Möglichkeit, die Slave-seitige Eingabe zu bedienen und kann nur von meinem aktuellen Shell-Prozess geschrieben werden.(und seine Kinder). Ich kann mit dem anderen Ende per Brief kommunizieren >&3
und je nach Wunsch daraus oder aus einer Protokolldatei lesen.
Und stty
funktioniert doch auf dem Terminal:
mikeserv@localhost$ echo stty -a ">$(tty)" >&3
speed 38400 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>;
swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc
-ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl
echoke