Der C-Code funktioniert einwandfrei und tritt korrekt in den Namespace ein, aber der Go-Code scheint EINVAL immer vom Aufruf setns
zurückzugeben, um den Namespace mnt
einzugeben. Ich habe eine Reihe von Permutationen (einschließlich eingebetteten C-Code mit cgo und externen .so
) auf Go 1.2
, 1.3
und den aktuellen Tipp versucht.
Beim Durchlaufen des Codes in gdb
wird angezeigt, dass beide Sequenzen setns
in libc
genauso aufrufen (oder so erscheint es mir).
Ich habe gekocht, was scheint das Problem bis auf den Code unten. Was mache ich falsch?
Ich habe einen Shell-Alias zum Starten von schnellen Busybox-Containern:
%Vor% Nach dem Ausführen startet startbb
einen Container und gibt seine PID aus.
lxc-checkconfig
outputs:
uname -a
erzeugt:
Der folgende C-Code funktioniert einwandfrei:
%Vor% Nach dem Kompilieren mit gcc -o checkns checkns.c
lautet die Ausgabe von sudo ./checkns <PID>
:
Umgekehrt funktioniert der folgende Go-Code (der identisch sein sollte) nicht ganz so gut:
%Vor% Stattdessen führt sudo go run main.go <PID>
Folgendes aus:
(Es gibt ein Problem, das beim Go-Projekt eingereicht wurde )
Die Antwort auf diese Frage lautet also, dass Sie setns
aus einem single-threaded Kontext aufrufen müssen. Dies ist sinnvoll, da setns
den aktuellen Thread dem Namespace zuordnen soll. Da Go multi-threaded ist, müssen Sie den Aufruf setns
vornehmen, bevor die Go-Laufzeitthreads starten.
Ich denke das liegt daran, dass der Thread, in dem der Aufruf von syscall.RawSyscall
ausgeführt wird, nicht der Hauptthread ist - selbst mit runtime.LockOSThread
das Ergebnis ist nicht was Sie würden erwarten (dh, dass die Goroutine zum Haupt-C-Thread "gesperrt" ist und daher äquivalent zu dem Konstruktortrick ist, der unten erläutert wird).
Die Antwort, die ich nach der Einreichung des Problems erhielt, schlug vor, den " cgo
Konstruktortrick" zu verwenden. Ich konnte keine "richtige" Dokumentation zu diesem "Trick" finden, aber es wird in nsinit
von Docker / Michael Crosby verwendet und obwohl ich diesen Code Zeile für Zeile durchgegangen bin, habe ich nicht versucht, ihn auf diese Weise auszuführen (siehe unten für Frustration).
Der "Trick" ist im Grunde, dass Sie cgo
erhalten können, um eine C-Funktion auszuführen, bevor Sie die Go-Laufzeit starten.
Fügen Sie dazu das Makro __attribute__((constructor))
hinzu, um die Funktion zu dekorieren, die vor dem Start von Go ausgeführt werden soll:
Als Template habe ich checkns.go
folgendermaßen geändert:
Dieser Code funktioniert, erfordert aber, dass PID
fest codiert ist, da er nicht korrekt von der Kommandozeileneingabe gelesen wird, aber er veranschaulicht die Idee (und funktioniert, wenn Sie ein PID
von einem wie oben beschrieben gestarteten Container bereitstellen) .
Es ist frustrierend, weil ich setns
mehrmals aufrufen wollte, aber da dieser C-Code vor dem Start der Go-Laufzeit ausgeführt wird, ist kein Go-Code verfügbar.
Update: In den Kernel-Mailinglisten herumzublättern bietet dieser Link zu einer Konversation, die dies dokumentiert. Ich kann es nicht in wirklich veröffentlichten Manpages finden, aber hier ist das Zitat von einem Patch zu setns(2)
, bestätigt von Eric Biederman:
Ein Prozess kann möglicherweise nicht mit einem neuen Mount-Namespace reassoziiert werden es ist multi-threaded. Das Ändern des Mount-Namespace erfordert dass der Aufrufer sowohl CAP_SYS_CHROOT als auch CAP_SYS_ADMIN besitzt Funktionen in einem eigenen Benutzernamespace und CAP_SYS_ADMIN in der Ziel-Mount-Namespace.
Tags und Links c linux go system-calls cgo