diff options
author | Deepak Kodihalli <dkodihal@in.ibm.com> | 2018-04-23 03:26:38 -0500 |
---|---|---|
committer | Deepak Kodihalli <dkodihal@in.ibm.com> | 2018-04-30 09:17:07 -0500 |
commit | 5c518f63d7a169326b572cfa5886b15a6876f4f1 (patch) | |
tree | 3a58cc215aacdf47b1c085d7f72c8425f8cb7a30 /module/obmc | |
parent | 97fe435fc04b401eab197adcd15d212971ed6e0c (diff) | |
download | phosphor-rest-server-5c518f63d7a169326b572cfa5886b15a6876f4f1.tar.gz phosphor-rest-server-5c518f63d7a169326b572cfa5886b15a6876f4f1.zip |
Expose host serial console over a websocket
Expose host serial console over a "/console" route, using a
websocket. An authenticated client can access the host serial console
via reads/writes to this websocket.
Change-Id: I0f63a3844e777d4f4c45194c85a63c9f10a91744
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Diffstat (limited to 'module/obmc')
-rw-r--r-- | module/obmc/wsgi/apps/rest_dbus.py | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/module/obmc/wsgi/apps/rest_dbus.py b/module/obmc/wsgi/apps/rest_dbus.py index 6374add..3eac4f4 100644 --- a/module/obmc/wsgi/apps/rest_dbus.py +++ b/module/obmc/wsgi/apps/rest_dbus.py @@ -44,6 +44,8 @@ if have_wsock: except ImportError: # python 3 from gi.repository import GObject as gobject import gevent + from gevent import socket + from gevent import Greenlet DBUS_UNKNOWN_INTERFACE = 'org.freedesktop.DBus.Error.UnknownInterface' DBUS_UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod' @@ -916,6 +918,85 @@ class EventHandler(RouteHandler): notifier = EventNotifier(wsock, filters) +class HostConsoleHandler(RouteHandler): + ''' Handles the /console route, for clients to be able + read/write the host serial console. The way this is + done is by exposing a websocket that's mirrored to an + abstract UNIX domain socket, which is the source for + the console data. ''' + + verbs = ['GET'] + # Naming the route console0, because the numbering will help + # on multi-bmc/multi-host systems. + rules = ['/console0'] + + def __init__(self, app, bus): + super(HostConsoleHandler, self).__init__( + app, bus, self.verbs, self.rules) + + def find(self, **kw): + pass + + def setup(self, **kw): + pass + + def read_wsock(self, wsock, sock): + while True: + try: + incoming = wsock.receive() + if incoming: + # Read websocket, write to UNIX socket + sock.send(incoming) + except Exception as e: + sock.close() + return + + def read_sock(self, sock, wsock): + max_sock_read_len = 4096 + while True: + try: + outgoing = sock.recv(max_sock_read_len) + if outgoing: + # Read UNIX socket, write to websocket + wsock.send(outgoing) + except Exception as e: + wsock.close() + return + + def send_ping(self, wsock) : + # Most webservers close websockets after 60 seconds of + # inactivity. Make sure to send a ping before that. + timeout = 45 + payload = "ping" + # the ping payload can be anything, the receiver has to just + # return the same back. + while True: + gevent.sleep(timeout) + wsock.send_frame(payload, wsock.OPCODE_PING) + + def do_get(self): + wsock = request.environ.get('wsgi.websocket') + if not wsock: + abort(400, 'Expected WebSocket based request.') + + # A UNIX domain socket structure defines a 108-byte pathname. The + # server in this case, obmc-console-server, expects a 108-byte path. + socket_name = "\0obmc-console" + trailing_bytes = "\0" * (108 - len(socket_name)) + socket_path = socket_name + trailing_bytes + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + + try: + sock.connect(socket_path) + except Exception as e: + abort(500, str(e)) + + wsock_reader = Greenlet.spawn(self.read_wsock, wsock, sock) + sock_reader = Greenlet.spawn(self.read_sock, sock, wsock) + ping_sender = Greenlet.spawn(self.send_ping, wsock) + gevent.joinall([wsock_reader, sock_reader, ping_sender]) + + class ImagePutHandler(RouteHandler): ''' Handles the /upload/image/<filename> route. ''' @@ -1386,6 +1467,7 @@ class App(Bottle): self.download_dump_get_handler = DownloadDumpHandler(self, self.bus) if self.have_wsock: self.event_handler = EventHandler(self, self.bus) + self.host_console_handler = HostConsoleHandler(self, self.bus) self.instance_handler = InstanceHandler(self, self.bus) def install_handlers(self): @@ -1402,6 +1484,7 @@ class App(Bottle): self.download_dump_get_handler.install() if self.have_wsock: self.event_handler.install() + self.host_console_handler.install() # this has to come last, since it matches everything self.instance_handler.install() |