On Wed, 25 May 2011 19:48:01 +0100 Stefan Hajnoczi <stefa...@linux.vnet.ibm.com> wrote:
> QEMU supports socket chardevs that establish connections like a server > or a client. The QEMUMonitorProtocol class only supports connecting as > a client. It is not possible to connect race-free when launching QEMU > since trying to connect before QEMU has bound and is listening on the > socket results in failure. > > Add the QEMUMonitorProtocol(server=True) argument to bind and listen on > the socket. The QEMU process can then be launched and connects to the > already existing QMP socket without a race condition: > > qmp = qmp.QEMUMonitorProtocol(monitor_path, server=True) > popen = subprocess.Popen(args) > qmp.accept() Would be cool to get the demo shell doing this (not a required for the initial merge, of course). > > Signed-off-by: Stefan Hajnoczi <stefa...@linux.vnet.ibm.com> > --- > QMP/qmp.py | 43 ++++++++++++++++++++++++++++++++----------- > 1 files changed, 32 insertions(+), 11 deletions(-) > > diff --git a/QMP/qmp.py b/QMP/qmp.py > index 2565508..c7dbea0 100644 > --- a/QMP/qmp.py > +++ b/QMP/qmp.py > @@ -22,19 +22,24 @@ class QMPCapabilitiesError(QMPError): > pass > > class QEMUMonitorProtocol: > - def __init__(self, address): > + def __init__(self, address, server=False): > """ > Create a QEMUMonitorProtocol class. > > @param address: QEMU address, can be either a unix socket path > (string) > or a tuple in the form ( address, port ) for a TCP > connection > - @note No connection is established, this is done by the connect() > method > + @param server: server mode listens on the socket (bool) > + @raise socket.error on socket connection errors > + @note No connection is established, this is done by the connect() or > + accept() methods > """ > self.__events = [] > self.__address = address > self.__sock = self.__get_sock() > - self.__sockfile = self.__sock.makefile() > + if server: > + self.__sock.bind(self.__address) > + self.__sock.listen(1) > > def __get_sock(self): > if isinstance(self.__address, tuple): > @@ -43,6 +48,17 @@ class QEMUMonitorProtocol: > family = socket.AF_UNIX > return socket.socket(family, socket.SOCK_STREAM) > > + def __negotiate_capabilities(self): > + self.__sockfile = self.__sock.makefile() > + greeting = self.__json_read() > + if greeting is None or not greeting.has_key('QMP'): > + raise QMPConnectError > + # Greeting seems ok, negotiate capabilities > + resp = self.cmd('qmp_capabilities') > + if "return" in resp: > + return greeting > + raise QMPCapabilitiesError > + > def __json_read(self, only_event=False): > while True: > data = self.__sockfile.readline() > @@ -67,14 +83,19 @@ class QEMUMonitorProtocol: > @raise QMPCapabilitiesError if fails to negotiate capabilities > """ > self.__sock.connect(self.__address) > - greeting = self.__json_read() > - if greeting is None or not greeting.has_key('QMP'): > - raise QMPConnectError > - # Greeting seems ok, negotiate capabilities > - resp = self.cmd('qmp_capabilities') > - if "return" in resp: > - return greeting > - raise QMPCapabilitiesError > + return self.__negotiate_capabilities() > + > + def accept(self): > + """ > + Await connection from QMP Monitor and perform capabilities > negotiation. > + > + @return QMP greeting dict > + @raise socket.error on socket connection errors > + @raise QMPConnectError if the greeting is not received > + @raise QMPCapabilitiesError if fails to negotiate capabilities > + """ > + self.__sock, _ = self.__sock.accept() > + return self.__negotiate_capabilities() > > def cmd_obj(self, qmp_cmd): > """