subprocess.Popen: seltsames Verhalten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
sventhef
User
Beiträge: 6
Registriert: Samstag 7. April 2007, 12:18
Wohnort: Kiel
Kontaktdaten:

Moin zusammen!
Ich sitz hier grad an einem Problem, das irgendwie unlösbar scheint (ist wahrscheinlich ganz einfach...). Und zwar geht es um Folgendes: Ich rufe ein Programm mit subprocess.Popen (unter Linux) auf, warte bis es beendet ist und lese dann den returncode:

Code: Alles auswählen

cmd = ['/sbin/ip','link','set','dev',iface,'up']
p = subprocess.Popen(cmd)
res = p.wait()
Nun ergibt sich dieses Problem: Wenn ich das Skript über ein Terminal mit "./skript.py" oder "python skript.py" starte, funktioniert alles perfekt, der returncode ist 0.

Lege ich jetzt aber auf dem GNOME Desktop einen Starter an, der das Skript aufruft (egal ob direkt oder mit "python skript.py"), dann ist der returncode immer 1, es ist also irgendwas schief gegangen.

Der Rest des Skripts funktioniert wunderbar, das Problem tritt nicht nur bei dem Aufruf oben auf, sondern bei jedem aufgerufenen Programm.

Ich wär dankbar für jeden erdenklichen Lösungsansatz!
lunar

sventhef hat geschrieben: Nun ergibt sich dieses Problem: Wenn ich das Skript über ein Terminal mit "./skript.py" oder "python skript.py" starte, funktioniert alles perfekt, der returncode ist 0.

Lege ich jetzt aber auf dem GNOME Desktop einen Starter an, der das Skript aufruft (egal ob direkt oder mit "python skript.py"), dann ist der returncode immer 1, es ist also irgendwas schief gegangen.
Nur nebenbei: Es würde mich interessieren, woher du das weißt? Ich kann in deinem Skript keinen Code zur Ausgabe des Return-Codes finden...
Der Rest des Skripts funktioniert wunderbar, das Problem tritt nicht nur bei dem Aufruf oben auf, sondern bei jedem aufgerufenen Programm.
Versuch mal folgendes:

Code: Alles auswählen

cmd = ['/sbin/ip','link','set','dev',iface,'up']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
res = p.wait()
output = p.communicate()[0]
print output
Damit siehst du die Ausgabe des Programmes. Spontan würde ich jetzt raten, dass du ein "Permission denied" zu sehen bekommst. Das Aktivieren von Netzwerkkarten ist nämlich normalerweise nur root erlaubt, ich denke aber nicht, dass du unter Gnome mit Root-Privilegien unterwegs bist (das wäre zumindest ziemlich dumm).
sventhef
User
Beiträge: 6
Registriert: Samstag 7. April 2007, 12:18
Wohnort: Kiel
Kontaktdaten:

danke erst mal für deine antwort.
die ausgabe des returncodes hab ich im post einfach mal weggelassen.

Und ich hab natürlich noch was vergessen, so ist es richtig:

Code: Alles auswählen

cmd = ['/usr/bin/sudo','/sbin/ip','link','set','dev',iface,'up']
wenn ich das skript von der konsole aufrufe, funktioniert das halt auch, das interface wird aktiviert. nur anders gehts halt nicht.
lunar

sventhef hat geschrieben:danke erst mal für deine antwort.
die ausgabe des returncodes hab ich im post einfach mal weggelassen.

Und ich hab natürlich noch was vergessen, so ist es richtig:

Code: Alles auswählen

cmd = ['/usr/bin/sudo','/sbin/ip','link','set','dev',iface,'up']
wenn ich das skript von der konsole aufrufe, funktioniert das halt auch, das interface wird aktiviert. nur anders gehts halt nicht.
Mmmh, sudo verlangt ein Passwort, wenn du es nicht anderweitig konfiguriert hast. Auf der Shell funktioniert dein Skript, da du das Passwort direkt eingeben kannst. Weitere Aufrufe funktionieren, weil das Passwort-Ticket an die Shell gebunden ist und der Aufruf des Kommandos die Shell von Skript erbt. Gnome jedoch führt seine Prozesse wahrscheinlich aus, ohne an ein Terminal zu binden. Deswegen kann sudo auch kein Passwort lesen und schlägt fehl.

Lösungen:
Sudo so konfigurieren, dass es für diesen Aufruf kein Passwort verlangt oder sudo aus dem Kommandozeilenaufruf entfernen und stattdessen den Starter so anlegen, dass er das Skript mit Root-Rechten ausführt. Das ist zumindest bei KDE möglich.
BlackJack

Oder `gtksu` statt `sudo` verwenden, dann bekommt man ein Fenster zur Passworteingabe.
lunar

BlackJack hat geschrieben:Oder `gtksu` statt `sudo` verwenden, dann bekommt man ein Fenster zur Passworteingabe.
Dann bekommt man aber Probleme mit der Ausführung, wenn kein X vorhanden ist.

Ich persönlich halte wenig davon, sowas im Skript fest einzucoden. Lieber einmal os.geteuid() == 0 prüfen und bei False eine Meldung ausgeben. Das zwingt den User nicht dazu, ein bestimmtes Programm zu Authentifizierung zu verwenden.
Es gibt neben sudo nämlich noch einige Alternativen wie super oder op, die andere User vorziehen. Außerdem soll es da auch System ohne sudo geben (Ubuntu ist ja nicht die Welt) ;)
sventhef
User
Beiträge: 6
Registriert: Samstag 7. April 2007, 12:18
Wohnort: Kiel
Kontaktdaten:

sudo ist so konfiguriert, dass es kein Passwort will.
ich habs jetzt mal einem anderen Ansatz ausprobiert:
statt subprocess.Popen hab ich jetzt mal pexpect (http://pexpect.sourceforge.net/) ausprobiert, da scheints zu funktionieren. so weit ich das versteh, liegt das problem wohl darin, dass Popen eine Konsole zum Ausführen braucht (ist so meine Vermutung). pexpect startet das Programm auf einer virtuellen Konsole (pseudo TTY). Denk ich mal so. Kann mich natürlich auch irren.

Jedenfalls funktioniert es jetzt. Falls aber noch jemand eine mögliche Lösung hat, würde mich das schon interessieren, weil ich dann nicht das zusätzliche Modul pexpect bräuchte...
lunar

sventhef hat geschrieben:sudo ist so konfiguriert, dass es kein Passwort will.
ich habs jetzt mal einem anderen Ansatz ausprobiert:
statt subprocess.Popen hab ich jetzt mal pexpect (http://pexpect.sourceforge.net/) ausprobiert, da scheints zu funktionieren. so weit ich das versteh, liegt das problem wohl darin, dass Popen eine Konsole zum Ausführen braucht (ist so meine Vermutung).
Nein, das kann nicht sein. Erstens würde das ja in einer Python-Exception resultieren, anstatt den Return-Code zu setzen. Außerdem verwendet subprocess intern einen Kombination aus os.execvp und os.fork zur Ausführung von Prozessen, solange shell nicht explizit auf True gesetzt wurde. Dadurch werden Shell-Exploits verhindert, bei denen der User Shell-Befehl in die Eingabe einschleust.

Mich würde interessieren, was die Ausgabe des Programms sagt (siehe eines meiner vorigen Postings) und was passiert, wenn du dem Starter sagst, er soll das Programm im Terminal ausführen.
sventhef
User
Beiträge: 6
Registriert: Samstag 7. April 2007, 12:18
Wohnort: Kiel
Kontaktdaten:

ok, hab jetzt mal die ausgabe angeguckt. in allen fällen ist die "\n". Da gibts also keine Info...
ich werds mal mit nem gesprächigeren Programm versuchen und mir die Ausgabe angucken.

Wenn ich das Programm vom Starter im Terminal ausführen lasse, funktionierts perfekt...
Das ist zwar erst mal ne Lösung, aber nicht perfekt, weil das Skript in nächster Zeit eine GTK-Oberfläche kriegen soll und dann wäre da ja immer das Terminalfenster...
sventhef
User
Beiträge: 6
Registriert: Samstag 7. April 2007, 12:18
Wohnort: Kiel
Kontaktdaten:

so. die ausgabe von

Code: Alles auswählen

cmd = ['/sbin/ifconfig']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
res = p.wait()
output = p.communicate()[0]
print output
ist, wenn ich das skript von der kommandozeile starte, das was man erwartet (interface-infos). wenn ich aber denn starter benutze, ist die ausgabe wieder "\n". sehr, sehr seltsam
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

lunar hat geschrieben:Nein, das kann nicht sein. Erstens würde das ja in einer Python-Exception resultieren, anstatt den Return-Code zu setzen. Außerdem verwendet subprocess intern einen Kombination aus os.execvp und os.fork zur Ausführung von Prozessen, solange shell nicht explizit auf True gesetzt wurde. Dadurch werden Shell-Exploits verhindert, bei denen der User Shell-Befehl in die Eingabe einschleust.
Doch, das kann sein. ssh und su (und wohl auch sudo) funktionieren nur, wenn sie ueber ptys/ttys ausgefuehrt werden, und nicht ueber pipes an stdin/stdout/stderr, wie es subprocess/popen/ machen.

Am besten waere es, du versuchst, su/sudo etc zu vermeiden. Du koenntest zum Beispiel mit dem setuid-bit arbeiten. Ptys sind naemlich ganz schoen laestig.
lunar

Rebecca hat geschrieben:
lunar hat geschrieben:Nein, das kann nicht sein. Erstens würde das ja in einer Python-Exception resultieren, anstatt den Return-Code zu setzen. Außerdem verwendet subprocess intern einen Kombination aus os.execvp und os.fork zur Ausführung von Prozessen, solange shell nicht explizit auf True gesetzt wurde. Dadurch werden Shell-Exploits verhindert, bei denen der User Shell-Befehl in die Eingabe einschleust.
Doch, das kann sein. ssh und su (und wohl auch sudo) funktionieren nur, wenn sie ueber ptys/ttys ausgefuehrt werden, und nicht ueber pipes an stdin/stdout/stderr, wie es subprocess/popen/ machen.
Das kann natürlich sein...
Am besten waere es, du versuchst, su/sudo etc zu vermeiden. Du koenntest zum Beispiel mit dem setuid-bit arbeiten.
Das wird aber bei einem Skript nicht gehen. Linux ignoriert das SUID Bit bei Skripten und den Python Interpreter SUID zu setzen ist wohl keine soo gute Idee ;)
sventhef
User
Beiträge: 6
Registriert: Samstag 7. April 2007, 12:18
Wohnort: Kiel
Kontaktdaten:

Mir ist auch schon aufgefallen, dass das Problem nur auftritt, wenn ich sudo benutze. doofe sache. ich werd jetzt subprocess.popen für das konkrete problem nich mehr benutzen, sondern wie gesagt pexpect.
das stellt nämlich dem programm ne tty zur verfügung. funktioniert perfekt.

aber vielen dank nochmal für eure schnellen hilfsversuche!
Antworten