summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Urban <hydrogen18@gmail.com>2013-10-06 21:23:20 -0400
committerSergey Shepelev <temotor@gmail.com>2015-02-21 18:12:48 +0300
commite536cfaea7616b5e76b311bc0ae87858f3409422 (patch)
treee78aa223d0397e3dfb903a960d8c3fc455968b2d
parent52d42dd741fc8da7f1b1ac525ff6c9f2f5739ee7 (diff)
downloadeventlet-tm3.tar.gz
backdoor: support UNIX sockets and IPv6tm3
https://bitbucket.org/eventlet/eventlet/issue/157
-rw-r--r--eventlet/backdoor.py46
-rw-r--r--tests/backdoor_test.py69
2 files changed, 98 insertions, 17 deletions
diff --git a/eventlet/backdoor.py b/eventlet/backdoor.py
index 2067772..6cfd89e 100644
--- a/eventlet/backdoor.py
+++ b/eventlet/backdoor.py
@@ -4,6 +4,7 @@ from code import InteractiveConsole
import errno
import socket
import sys
+import traceback
import eventlet
from eventlet import hubs
@@ -40,20 +41,35 @@ class FileProxy(object):
return getattr(self.f, attr)
-# @@tavis: the `locals` args below mask the built-in function. Should
-# be renamed.
+def format_addr(a):
+ if len(a) >= 2:
+ # IP
+ return '{0}:{1}'.format(*a)
+ if len(a) == 1:
+ # UNIX socket / named pipe
+ return str(a[0])
+ # unknown
+ return str(a)
+
+
class SocketConsole(greenlets.greenlet):
- def __init__(self, desc, hostport, locals):
+ def __init__(self, desc, hostport, init_locals):
self.hostport = hostport
- self.locals = locals
+ self.init_locals = init_locals
# mangle the socket
self.desc = FileProxy(desc)
greenlets.greenlet.__init__(self)
+ # place to store exception if InteractiveConsole terminates
+ self.exc_info = None
def run(self):
try:
- console = InteractiveConsole(self.locals)
+ console = InteractiveConsole(self.init_locals)
console.interact()
+ except SystemExit:
+ pass
+ except BaseException:
+ self.exc_info = sys.exc_info()
finally:
self.switch_out()
self.finalize()
@@ -69,24 +85,27 @@ class SocketConsole(greenlets.greenlet):
def finalize(self):
# restore the state of the socket
self.desc = None
- print("backdoor closed to %s:%s" % self.hostport)
+ print("backdoor closed to {0}".format(format_addr(self.hostport)))
+
+ if self.exc_info is not None:
+ traceback.print_exception(*self.exc_info)
-def backdoor_server(sock, locals=None):
+def backdoor_server(sock, init_locals=None):
""" Blocking function that runs a backdoor server on the socket *sock*,
accepting connections and running backdoor consoles for each client that
connects.
- The *locals* argument is a dictionary that will be included in the locals()
+ The *init_locals* argument is a dictionary that will be included in the locals()
of the interpreters. It can be convenient to stick important application
variables in here.
"""
- print("backdoor server listening on %s:%s" % sock.getsockname())
+ print("backdoor server listening on {0}".format(format_addr(sock.getsockname())))
try:
try:
while True:
socketpair = sock.accept()
- backdoor(socketpair, locals)
+ backdoor(socketpair, init_locals)
except socket.error as e:
# Broken pipe means it was shutdown
if get_errno(e) != errno.EPIPE:
@@ -95,17 +114,16 @@ def backdoor_server(sock, locals=None):
sock.close()
-def backdoor(conn_info, locals=None):
+def backdoor(conn_info, init_locals=None):
"""Sets up an interactive console on a socket with a single connected
client. This does not block the caller, as it spawns a new greenlet to
handle the console. This is meant to be called from within an accept loop
(such as backdoor_server).
"""
conn, addr = conn_info
- host, port = addr
- print("backdoor to %s:%s" % (host, port))
+ print("backdoor to {0}".format(format_addr(addr)))
fl = conn.makefile("rw")
- console = SocketConsole(fl, (host, port), locals)
+ console = SocketConsole(fl, addr, init_locals)
hub = hubs.get_hub()
hub.schedule_call_global(0, console.switch)
diff --git a/tests/backdoor_test.py b/tests/backdoor_test.py
index 6facffe..e91bbbc 100644
--- a/tests/backdoor_test.py
+++ b/tests/backdoor_test.py
@@ -1,11 +1,25 @@
+import os
+
import eventlet
from eventlet import backdoor
from eventlet.green import socket
+import tests
+
+
+SOCKET_PATH = '/tmp/eventlet_backdoor_test.socket'
+
-from tests import LimitedTestCase, main
+def silent_unlink(path):
+ try:
+ os.unlink(SOCKET_PATH)
+ except OSError:
+ pass
-class BackdoorTest(LimitedTestCase):
+class BackdoorTest(tests.LimitedTestCase):
+ def tearDown(self):
+ silent_unlink(SOCKET_PATH)
+
def test_server(self):
listener = socket.socket()
listener.bind(('localhost', 0))
@@ -29,6 +43,55 @@ class BackdoorTest(LimitedTestCase):
# wait for the console to discover that it's dead
eventlet.sleep(0.1)
+ def test_server_on_ipv6(self):
+ listener = socket.socket(socket.AF_INET6)
+ listener.bind(('::1', 0))
+ listener.listen(5)
+ serv = eventlet.spawn(backdoor.backdoor_server, listener)
+ client = socket.socket(socket.AF_INET6)
+ client.connect(listener.getsockname())
+ f = client.makefile('rw')
+ self.assert_('Python' in f.readline())
+ f.readline() # build info
+ f.readline() # help info
+ self.assert_('InteractiveConsole' in f.readline())
+ self.assertEquals('>>> ', f.read(4))
+ f.write('print("hi")\n')
+ f.flush()
+ self.assertEquals('hi\n', f.readline())
+ self.assertEquals('>>> ', f.read(4))
+ f.write('exit()\n')
+ f.close()
+ client.close()
+ serv.kill()
+ # wait for the console to discover that it's dead
+ eventlet.sleep(0.1)
+
+ def test_server_on_unix_socket(self):
+ silent_unlink(SOCKET_PATH)
+ listener = socket.socket(socket.AF_UNIX)
+ listener.bind(SOCKET_PATH)
+ listener.listen(5)
+ serv = eventlet.spawn(backdoor.backdoor_server, listener)
+ client = socket.socket(socket.AF_UNIX)
+ client.connect(SOCKET_PATH)
+ f = client.makefile('rw')
+ self.assert_('Python' in f.readline())
+ f.readline() # build info
+ f.readline() # help info
+ self.assert_('InteractiveConsole' in f.readline())
+ self.assertEquals('>>> ', f.read(4))
+ f.write('print("hi")\n')
+ f.flush()
+ self.assertEquals('hi\n', f.readline())
+ self.assertEquals('>>> ', f.read(4))
+ f.write('exit()\n')
+ f.close()
+ client.close()
+ serv.kill()
+ # wait for the console to discover that it's dead
+ eventlet.sleep(0.1)
+
if __name__ == '__main__':
- main()
+ tests.main()