Python-Shell mit xmlrpclib returns: not-well formed

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
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Ich möchte eine Python-Shell mit XML-RPC schreiben. Beides vereint in einem Skript. Klappt auch zum Teil, also ich kann Verzeichnisse anlegen und wieder löschen... Der Server wird gestartet mit 'pyshell.py server'. Der Client eben mit 'pyshell.py client' :)

Aber: Wenn ich (unter Windows) den Befehl 'dir' eigebe, schmiert mir der Client ab und bringt die Meldung:
Python Interpreter hat geschrieben:Traceback (most recent call last):
File "pyshell.py", line 84, in ?
XmlRpcShell(sys.argv[1:])
File "pyshell.py", line 39, in __init__
self.client()
File "pyshell.py", line 77, in client
sys.stdout.write(self.client.cmd(cmd))
File "C:\Programme\Python24\lib\xmlrpclib.py", line 1096, in __call__
return self.__send(self.__name, args)
File "C:\Programme\Python24\lib\xmlrpclib.py", line 1383, in __request
verbose=self.__verbose
File "C:\Programme\Python24\lib\xmlrpclib.py", line 1147, in request
return self._parse_response(h.getfile(), sock)
File "C:\Programme\Python24\lib\xmlrpclib.py", line 1281, in _parse_response
p.feed(response)
File "C:\Programme\Python24\lib\xmlrpclib.py", line 527, in feed
self._parser.Parse(data, 0)
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 5, column 23
Zur Sicherheit lasse ich mir mal die Ausgabe mit

Code: Alles auswählen

sys.stdout.write(line) # for testing purposes only
anzeigen. Im Fenster des Servers wird sie problemlos angezeigt. Also es wird ja ein String generiert, aber der muss irgendwas komisches beinhalten, dass die 'xmlrpclib' nicht empfangen kann/will. Am einfachen Zeilenumbruch liegt es nicht, denn den kann man auch mal von Hand über return "foo\nbar" ausgeben.

Keine Ahnung, woran das liegen könnte... habt ihr eine Idee?

Das zweite Problem ist, dass der Server abschmiert, sobald ich z.B. 'cmd.exe' eingebe. Man müsste mal testen, ob das unter Unixen bei '/bin/bash' auch passiert. Der Server hängt sich auch auf, wenn ich z.B. '%ProgramFiles\Python24\python.exe' aufrufen will und solche Sachen... Wie erzeugen die ihre Ausgabe? Kommt da nichts über stdout?

Code: Alles auswählen

import sys, os, subprocess, time, md5
from SimpleXMLRPCServer import SimpleXMLRPCServer

class XmlRpcShellServer(SimpleXMLRPCServer):
    # overridings
    allow_reuse_address = True
    # don't stop serving until stop is True
    stop = False
    def serve_forever(self):
        while not self.stop:
            self.handle_request()


class XmlRpcShell:
    # configuration
    config = {
        'protocol': "http",
        'host': "localhost",
        'port': 8080,
        'stopcmd': "exit",
        'timeout': 5
    }
    # access list
    access = {
        'user': "pass"
    }
    # constructor
    def __init__(self, args):
        # check arguments
        if not len(args) > 0:
            self.usage()
        self.mode = args[0]
        # check access
        """ testing """
        self.user = "droptix" # for testing purposes until 'access list' works
        if self.mode == "server":
            self.server()
        elif self.mode == "client":
            self.client()
        else:
            self.usage()
    # server function(s)
    def cmd(self, cmd):
        if cmd == self.config['stopcmd']:
            self.server.stop = True
            return "stop server"
        else:
            cmdtime = time.time()
            output = ""
            process = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            while True:
                runtime = time.time() - cmdtime
                line = process.stdout.read() # hanging up here...
                sys.stdout.write(line) # for testing purposes only
                if line == "" or runtime >= self.config['timeout']:
                    break
                output += line
            return output # e.g. cmd='dir' exits with: xml.parsers.expat.ExpatError: not well-formed (invalid token): line 5, column 23
    # server
    def server(self):
        print "booting"
        self.server = XmlRpcShellServer((self.config['host'], self.config['port']))
        print "registering functions"
        self.server.register_function(self.cmd)
        print "server booted"
        self.server.serve_forever()
    def client(self):
        from xmlrpclib import Server
        self.client = Server("%s://%s:%s" % (self.config['protocol'], self.config['host'], self.config['port']))
        cmd = None
        while not cmd == self.config['stopcmd']:
            if sys.platform == "win32":
                prompt = ">"
            else:
                prompt = "$ "
            cmd = raw_input("%s@%s%s" % (self.user, self.config['host'], prompt))
            sys.stdout.write(self.client.cmd(cmd))
    # usage
    def usage(self):
        print "USAGE MESSAGE"
        sys.exit(1)

if __name__ == "__main__":
    XmlRpcShell(sys.argv[1:])
murph
User
Beiträge: 622
Registriert: Freitag 14. April 2006, 19:23
Kontaktdaten:

murph@murphs:~$ ./pyshell.py client
droptix@localhost$ dir
ablage databasereader.py~ Musik sudopyko.py
archiver4geschi.py~ Desktop My\ Downloads sudopyko.py~
archiver.py~ ebay py2exe.py sun
ausgabekonsole.pyw~ Encoder.py~ pyshell.py test.py~
ausgabe.py forschung.php pyshell.py~ test.txt~
ausgabe.py~ gameoflive.py~ PYTHON UT2004
bittorrent hier.txt~ Python-2.4.3 Vercoder.py~
blender.pyw~ internetgame server4gamez verschluesseln.py~
brainfuck.py meine_dateien spammer.py~ winex
brainfuck.py~ meinedateien.zip spiele Zahlentester.py~
databasereader.py movies sudoku.py~
droptix@localhost$
Das war unter ubuntu linux!
Eine längere Fehlermeldung habe ich bekommen, als ich den Server nicht gestartet hatte.
Und zum absturz bringt man das. wenn man in der pyshell die pyshell wiedrer ausführt^^
aber wer so ******* im hirn ist, hat selber schuld^^
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Also unter Linux scheint das problemlos zu laufen, aber wenn ein Windows mit ins Spiel kommt, gibt's massive Probleme.

Ich hab meine PyShell auf meinen Debian-Rechner gepackt und dort den Server gestartet. Vom selben Debian-Rechner aus habe ich den PyShell-Client gestartet. Ich konnte soweit erstmal alles machen. Auch der Befehl "ls" ging ohne Probleme.

Der PyShell-Server lief noch immer auf Debian. Da hab ich unter Windows den PyShell-Client gestartet. Der Prompt kommt, aber egal welchen Befehl man eingibt, es folgte wieder ein Absturz mit folgender Meldung:
cmd.exe hat geschrieben:tom@localhost>ls
Traceback (most recent call last):
File "pyshell.py", line 85, in ?
XmlRpcShell(sys.argv[1:])
File "pyshell.py", line 39, in __init__
self.client()
File "pyshell.py", line 78, in client
sys.stdout.write(self.client.cmd(cmd))
File "C:\Programme\Python24\lib\xmlrpclib.py", line 1096, in __call__
return self.__send(self.__name, args)
File "C:\Programme\Python24\lib\xmlrpclib.py", line 1383, in __request
verbose=self.__verbose
File "C:\Programme\Python24\lib\xmlrpclib.py", line 1129, in request
self.send_content(h, request_body)
File "C:\Programme\Python24\lib\xmlrpclib.py", line 1243, in send_content
connection.endheaders()
File "C:\Programme\Python24\lib\httplib.py", line 798, in endheaders
self._send_output()
File "C:\Programme\Python24\lib\httplib.py", line 679, in _send_output
self.send(msg)
File "C:\Programme\Python24\lib\httplib.py", line 646, in send
self.connect()
File "C:\Programme\Python24\lib\httplib.py", line 630, in connect
raise socket.error, msg
socket.error: (10061, 'Connection refused')
Firewall deaktivieren brachte auch nix, dachte erst die blockt irgendwas. Hat jemand eine Idee dazu? Ist die 'xmlrpclib.py' unter Windows vielleicht eingeschränkt oder ist das einfach ein Netzwerkproblem?

[Nachtrag]

Da der PyShell-Server noch "an" war, musste ich ihn mit Strg+C gewaltsam stoppen. Dabei kam folgende Meldung (unter Debian):
bash hat geschrieben:Traceback (most recent call last):
File "./pyshell.py", line 85, in ?
XmlRpcShell(sys.argv[1:])
File "./pyshell.py", line 37, in __init__
self.server()
File "./pyshell.py", line 66, in server
self.server.serve_forever()
File "./pyshell.py", line 12, in serve_forever
self.handle_request()
File "/usr/lib/python2.4/SocketServer.py", line 217, in handle_request
request, client_address = self.get_request()
File "/usr/lib/python2.4/SocketServer.py", line 373, in get_request
return self.socket.accept()
File "/usr/lib/python2.4/socket.py", line 169, in accept
sock, addr = self._sock.accept()
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Du kannst von Remote-Hosts nicht verbinden, weil dein Server nur auf Localhost hört, und daher nur Verbindungen von Localhost akzeptiert. Versuch mal beim Server als Host einen Leeren String oder 0.0.0.0 anzugeben, dass sollte es sein.

Edit: Siehe hier.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Oh ja, wie klug von mir... :oops: Danke, ich hab jetzt länger nicht mehr reingeschaut und irgendwie nicht drauf geachtet...

Gut, dann nehme ich die obigen Fehler zurück! Es geht, wenn der PyShell-Server auf nem Linux läuft und ich unter Windows den PyShell-Client verwende. Keine Probleme...

Aber wieso geht das nicht, wenn ich den PyShell-Server unter Windows laufen lasse? Weder mit nem Linux noch mit nem Windows Client?

Mit Linux als Server und Windows als Client bekomme ich wieder die Meldung vom Anfang:
cmd.exe hat geschrieben:Traceback (most recent call last):
File "./pyshell.py", line 98, in ?
XmlRpcShell(sys.argv[1:])
File "./pyshell.py", line 41, in __init__
self.client()
File "./pyshell.py", line 91, in client
sys.stdout.write(self.client.cmd(cmd)) # for testing purposes only
File "/usr/lib/python2.4/xmlrpclib.py", line 1096, in __call__
return self.__send(self.__name, args)
File "/usr/lib/python2.4/xmlrpclib.py", line 1383, in __request
verbose=self.__verbose
File "/usr/lib/python2.4/xmlrpclib.py", line 1147, in request
return self._parse_response(h.getfile(), sock)
File "/usr/lib/python2.4/xmlrpclib.py", line 1281, in _parse_response
p.feed(response)
File "/usr/lib/python2.4/xmlrpclib.py", line 527, in feed
self._parser.Parse(data, 0)
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 5, column 23
Edit: In deinem verlinkten Thread hast du gesagt, dass wenn ich den Server per Remote (z.B. mit dem Client) stoppe, dass dann der Port nicht gleich wieder frei gegeben wird. Ist das wirklich so? Wenn ja, hast du das "Problem" gelöst? Also ich kann x Mal hintereinander den Server starten und stoppen... immer mit demselben Port. Unter Linux wie Windows.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

droptix hat geschrieben:Edit: In deinem verlinkten Thread hast du gesagt, dass wenn ich den Server per Remote (z.B. mit dem Client) stoppe, dass dann der Port nicht gleich wieder frei gegeben wird. Ist das wirklich so? Wenn ja, hast du das "Problem" gelöst? Also ich kann x Mal hintereinander den Server starten und stoppen... immer mit demselben Port. Unter Linux wie Windows.
Aslo bei mir war das so. Soweit ich mich erinnern kann, gab es eine socket-Option mit der man das Beeinflussen konnte. Keine Ahnung mehr wie das genau ging, ist schon 2 Jahre her.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Ich muss hier nochmal nachhaken, weil ich keine Lösung für das Verhalten unter Windows finde.

1) Vielleicht tiefer in die XML-RPC-Lib: Was heißt überhaupt "xml.parsers.expat.ExpatError: not well-formed (invalid token)"?

2) Wenn ich Befehle wie "mc" (Midnight Commander) ausführe, wo also eine Art GUI dargestellt werden soll, kommt nichts über sys.stdout zurück. Dadurch ergeben sich zwei Probleme:

2a) Der Server hängt sich auf. Ich kann ihn nur brutal als Administrator oder auch durch eine zweite Shell beenden. Solange der Server hängt, ist der Port nicht wieder verwendbar.

2b) Gibt es eine Möglichkeit, auch grafische Oberflächen wie den MC darzustellen? Oder auch Befehle wie "clear", die ja eigentlich den Bildschirm löschen sollen? Ist es überhaupt denkbar, dass man mit Python und relativ einfachen Mitteln eine Shell bauen kann, die sich ähnlich zum SSH-Server und PuTTY verhält... wo man also nativ auf dem Serversystem arbeiten kann?
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Vorweg: Das folgende gilt fuer Linux, von Windows hab ich keinen Plan.

"Text-Grafix" (und Cursorpositionierung) besteht aus ANSI-Escape-Sequenzen, das sind bestimmte Steuerzeichen, die vom Terminal interpretiert werden. Stdout/Stdin usw. sind lediglich "normale" Filedescriptoren, keine Terminals.

Probiert man mal Folgendes:

Code: Alles auswählen

rbreu@zam285:~> mc > ausgabe
Cannot get terminal settings: Inappropriate ioctl for device (25)
kann man sehen, das mc sich beschwert, wenn er seine Ausgabe an stdout umlenken soll. In der Datei "Ausgabe" landet dann nur Buchstabensalat, wenn man sie mit einem Texteditor anschaut. Den kann man auch nicht mehr vernuenftig in "Grafik zurueck umwandeln", weil einem Informationen ueber das Terminal fehlen, die sind ueber stdout einfach verloren gegangen. (Probier mal more ausgabe,dann bekommt man zwar Farbe, aber alles ist ducheinander.)

Dafuer sind unter *nix Peudoterminals (ptys) da. Falls die dich interessieren , ich habe mich da mal mit befasst:
http://www.python-forum.de/topic-5267.html
http://www.python-forum.de/topic-6245.html
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Ah ja, über einen der Beiträge bin ich schonmal gestolpert... Ich hätte gedacht, dass die Ausgabe in der Konsole auch so ein Standard ist wie stdin und stdout.

Mit Python eine weitere Unix-Shell (oder Pseudoterminal) basteln macht für mich keinen Sinn. Mein Ziel ist eine cross-platform fähige Python-Shell. Prinzipiell reicht auch eine normale Konsole ohne Terminal-Fähigkeiten.

Wäre halt nur nett gewesen, wenn man das mit Python realisieren könnte. Manchmal trifft man ja auch auf nützliche Konsolenprogramme, die eine GUI haben... und es wäre schade, die dann nicht nutzen zu können.

Kann man VOR dem Aufruf eines Programmes testen, ob es seine Ausgabe in stdout umlenken kann? Dann könnte ich unterbinden, dass sich meine Shell bei Programmen wie "mc" aufhängt.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

droptix hat geschrieben:Kann man VOR dem Aufruf eines Programmes testen, ob es seine Ausgabe in stdout umlenken kann? Dann könnte ich unterbinden, dass sich meine Shell bei Programmen wie "mc" aufhängt.
Mmh, das ist schwierig. Zwei spontane Ideen, von denen ich nicht weiss, wie praktikabel sie sind:

1.) Du koenntest das jeweilige Programm vorher in einer Testumgebung starten, bevor es dann "richtig" in deiner Shell gestartet wird.

2.) Die meisten Programme, die "Textgrafik" erzeugen, benutzen die (N)Curses-Bibliothek. Man koennte vorher mal gucken, ob ein Programm damit verlinkt ist:

Code: Alles auswählen

rbreu@abby:~> ldd /usr/bin/top
        linux-gate.so.1 =>  (0xffffe000)
        libncurses.so.5 => /lib/libncurses.so.5 (0x4002f000)
        libc.so.6 => /lib/tls/libc.so.6 (0x40077000)
        libdl.so.2 => /lib/libdl.so.2 (0x40196000)
        /lib/ld-linux.so.2 (0x40000000)
Damit wuerde man Programme wie ssh natuerlich nicht erwischen, die zwar keine Textgrafik produzieren, aber zur Passworteingabe trotzdem ein Terminal wollen. Ausserdem kann es sein, dass Programme zusaetzlich zum Textgrafik-Modus noch Kommandozeilenfunktionen haben. Suses Konfigurationsprogramm yast zum Beispiel laesst einen per Kommandozeile direkt ein Software-Paket installieren, und natuerlich bietet fast jedes Programm sowas wie --help oder --version an...

Aber man koennte dem User immerhin eine Warnung ausgeben ("Wollen sie dieses Programm wirklich starten?") ;-)
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Ich dachte eben auch an eine Warnung... allerdings nur wenn
(mit relativ hoher Wahrscheinlichkeit) festgestellt wurde, dass der angeforderte Befehl was Anderes als stdout erzeugt. Nun soll das ja auch OS-übergreifend funktionieren :D In Pythons "Lib"-Verzeichnis gibt es übrigens ein Verzeichnis "curses"... Kann man damit was anfangen?

Nun gut, dann bleib ich erstmal bei einer normalen Konsole. Ich hab mal die Fehlermeldung etwas genauer inspiziert: "xml.parsers.expat" ist erstmal ein Pfad in Pythons "Lib"-Verzeichnis. Bei mir unter Windows also "C:\Programme\Python24\Lib\xml\parsers". Dort liegt die Datei/das Modul "expat.py" mit folgendem Inhalt:
xml.parsers.expat.py hat geschrieben:"""Interface to the Expat non-validating XML parser."""
__version__ = '$Revision: 17640 $'

from pyexpat import *
Eine fünfte Zeile gibt es gar nicht... wo mir traceback den Fehler ausspuckt. Im "test"-Verzeichnis gibt es auch eine "test_pyexpat.py" mit folgendem Inhalt:
test.test_pyexpat.py hat geschrieben:# Very simple test - Parse a file and print what happens

# XXX TypeErrors on calling handlers, or on bad return values from a
# handler, are obscure and unhelpful.

import pyexpat
from xml.parsers import expat
[...]
Also könnte der Fehler im Modul "pyexpat" liegen. Das gibt's als Dateien "pyexpat.lib" in Pythons "libs"- und "pyexpat.pyd" im "DLLs"-Verzeichnis (letzteres wahrscheinlich nur Windows?).

Hat jemand noch eine Idee, was den "not well-formed" Fehler auslösen könnte?

Mal dumm gefragt: Vielleicht sorgen unter Windows einige Zeichen für Probleme in der XML, die zurück geliefert werden soll. Muss oder sollte man die irgendwie quoten, bevor meine Funktion die returned? Bei Verzeichnissen wird nämlich z.B. "<DIR>" ausgespuckt, was in XML ja nix zu suchen hat.

Kann ich irgendwie rausfinden, welche Zeichen (also ganz exakt mit Whitespace, Steuerzeichen etc.) das "dir" unter Windows produziert und ob die für XML zulässig sind?
pug
User
Beiträge: 16
Registriert: Dienstag 4. September 2007, 17:00

Hallo,

ist zwar eine sehr späte Antwort da aber ich gerade auch das "not well-formed" Problem bei xmlrpclib hatte möchte ich hier kurz meine Anmerkungen los werden. Eventuell hilft es jemand der wie ich im Forum nach einer Lösung sucht....

Der Fehler tritt anscheinend dann auf, wenn man versucht binäre Daten via XML-RPC zu übermitteln. Die Lösung ist daher denkbar einfach: Die Daten so codieren, dass sie als valide XML Daten durchgehen. Am einfachsten geht das meiner Meinung nach mit dem base64 Modul.

Grüße
Pug
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Das grundsätzliche Encoden jeglicher Daten würde den gesamten Datenverkehr um ein Drittel erhöhen. Man sollte vorher wissen um welche Daten es sich handelt und eine Art Flag mitschicken, was dem Empfänger verrät wann es wieder base54 decoden muss.

Bei meiner gewünschten Shell werden ja eigentlich nur ASCII-Zeichen übertragen. Man müsste nur wirkungsvoll ausschließen, dass bestimmte Befehle keine Binärdaten ausgeben bzw. vorher prüfen, ob deren Ausgabe nach STDOUT umgeleitet und damit ausgegeben werden kann.

EDIT: Dabei fällt mir grad ein, ob man das Ausgabeergebnis nicht einfach als

Code: Alles auswählen

<![CDATA[ ... ]]>
übertragen kann, so dass sich der Parser nicht beschwert? Binärdaten könnten damit aber trotzdem nicht korrekt übertragen werden... aber zumindest XML-Sonderzeichen.

Kann man eigentlich die Übertragung ZIP-komprimieren? Also serverseitig zippen und clientseitig entzippen?
Antworten