Skip to content

Commit 5c518f6

Browse files
committed
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 <[email protected]>
1 parent 97fe435 commit 5c518f6

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed

module/obmc/wsgi/apps/rest_dbus.py

+83
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
except ImportError: # python 3
4545
from gi.repository import GObject as gobject
4646
import gevent
47+
from gevent import socket
48+
from gevent import Greenlet
4749

4850
DBUS_UNKNOWN_INTERFACE = 'org.freedesktop.DBus.Error.UnknownInterface'
4951
DBUS_UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod'
@@ -916,6 +918,85 @@ def do_get(self):
916918
notifier = EventNotifier(wsock, filters)
917919

918920

921+
class HostConsoleHandler(RouteHandler):
922+
''' Handles the /console route, for clients to be able
923+
read/write the host serial console. The way this is
924+
done is by exposing a websocket that's mirrored to an
925+
abstract UNIX domain socket, which is the source for
926+
the console data. '''
927+
928+
verbs = ['GET']
929+
# Naming the route console0, because the numbering will help
930+
# on multi-bmc/multi-host systems.
931+
rules = ['/console0']
932+
933+
def __init__(self, app, bus):
934+
super(HostConsoleHandler, self).__init__(
935+
app, bus, self.verbs, self.rules)
936+
937+
def find(self, **kw):
938+
pass
939+
940+
def setup(self, **kw):
941+
pass
942+
943+
def read_wsock(self, wsock, sock):
944+
while True:
945+
try:
946+
incoming = wsock.receive()
947+
if incoming:
948+
# Read websocket, write to UNIX socket
949+
sock.send(incoming)
950+
except Exception as e:
951+
sock.close()
952+
return
953+
954+
def read_sock(self, sock, wsock):
955+
max_sock_read_len = 4096
956+
while True:
957+
try:
958+
outgoing = sock.recv(max_sock_read_len)
959+
if outgoing:
960+
# Read UNIX socket, write to websocket
961+
wsock.send(outgoing)
962+
except Exception as e:
963+
wsock.close()
964+
return
965+
966+
def send_ping(self, wsock) :
967+
# Most webservers close websockets after 60 seconds of
968+
# inactivity. Make sure to send a ping before that.
969+
timeout = 45
970+
payload = "ping"
971+
# the ping payload can be anything, the receiver has to just
972+
# return the same back.
973+
while True:
974+
gevent.sleep(timeout)
975+
wsock.send_frame(payload, wsock.OPCODE_PING)
976+
977+
def do_get(self):
978+
wsock = request.environ.get('wsgi.websocket')
979+
if not wsock:
980+
abort(400, 'Expected WebSocket based request.')
981+
982+
# A UNIX domain socket structure defines a 108-byte pathname. The
983+
# server in this case, obmc-console-server, expects a 108-byte path.
984+
socket_name = "\0obmc-console"
985+
trailing_bytes = "\0" * (108 - len(socket_name))
986+
socket_path = socket_name + trailing_bytes
987+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
988+
989+
try:
990+
sock.connect(socket_path)
991+
except Exception as e:
992+
abort(500, str(e))
993+
994+
wsock_reader = Greenlet.spawn(self.read_wsock, wsock, sock)
995+
sock_reader = Greenlet.spawn(self.read_sock, sock, wsock)
996+
ping_sender = Greenlet.spawn(self.send_ping, wsock)
997+
gevent.joinall([wsock_reader, sock_reader, ping_sender])
998+
999+
9191000
class ImagePutHandler(RouteHandler):
9201001
''' Handles the /upload/image/<filename> route. '''
9211002

@@ -1386,6 +1467,7 @@ def create_handlers(self):
13861467
self.download_dump_get_handler = DownloadDumpHandler(self, self.bus)
13871468
if self.have_wsock:
13881469
self.event_handler = EventHandler(self, self.bus)
1470+
self.host_console_handler = HostConsoleHandler(self, self.bus)
13891471
self.instance_handler = InstanceHandler(self, self.bus)
13901472

13911473
def install_handlers(self):
@@ -1402,6 +1484,7 @@ def install_handlers(self):
14021484
self.download_dump_get_handler.install()
14031485
if self.have_wsock:
14041486
self.event_handler.install()
1487+
self.host_console_handler.install()
14051488
# this has to come last, since it matches everything
14061489
self.instance_handler.install()
14071490

0 commit comments

Comments
 (0)