mirror of
https://github.com/xcat2/confluent.git
synced 2025-01-27 19:37:57 +00:00
Track some particularly key terminal states and replay them for new clients
ESXi requires a distinctly different keypad mode and 'shift in' character set. Track requests for those states. Reset on 'null' character, which seems to only be emitted by UEFI so far. Ideally, things change such that we can remove that workaround.
This commit is contained in:
parent
12d3f91d59
commit
2ba93379b7
@ -26,6 +26,8 @@ class _ConsoleHandler(object):
|
||||
self.buffer = bytearray()
|
||||
self._connect()
|
||||
self.users = {}
|
||||
self.appmodedetected = False
|
||||
self.shiftin = None
|
||||
|
||||
def _connect(self):
|
||||
self._console = plugin.handle_path(
|
||||
@ -45,13 +47,10 @@ class _ConsoleHandler(object):
|
||||
return hdl
|
||||
|
||||
def flushbuffer(self):
|
||||
#TODO:log the old stuff
|
||||
if len(self.buffer) > 1024:
|
||||
self.buffer = bytearray(self.buffer[-1024:])
|
||||
#Will be interesting to keep track of logged but
|
||||
#retained data, must only log data not already
|
||||
#flushed
|
||||
#also, timestamp data...
|
||||
# Logging is handled in a different stream
|
||||
# this buffer is now just for having screen redraw on
|
||||
# connect
|
||||
self.buffer = bytearray(self.buffer[-8192:])
|
||||
|
||||
def get_console_output(self, data):
|
||||
# Spawn as a greenthread, return control as soon as possible
|
||||
@ -87,18 +86,35 @@ class _ConsoleHandler(object):
|
||||
if data == conapi.ConsoleEvent.Disconnect:
|
||||
self._connect()
|
||||
return
|
||||
self.logger.log(data)
|
||||
prefix = ''
|
||||
if '\0' in data: # there is a null in the output
|
||||
# the proper response is to do nothing, but here using it as a cue
|
||||
# that perhaps firmware has reset since that's the only place
|
||||
# observed so far. Lose the shiftin and app mode when detected
|
||||
prefix = '\x1b[?1l'
|
||||
self.shiftin = None
|
||||
self.appmodedetected = False
|
||||
if '\x1b[?1h' in data: # remember the session wants the client to be in
|
||||
# 'application mode' Thus far only observed on esxi
|
||||
self.appmodedetected = True
|
||||
if '\x1b)0' in data:
|
||||
# console indicates it wants access to special drawing characters
|
||||
self.shiftin = '0'
|
||||
eventdata = 0
|
||||
if self.appmodedetected:
|
||||
eventdata = eventdata | 1
|
||||
if self.shiftin is not None:
|
||||
eventdata = eventdata | 2
|
||||
self.logger.log(data, eventdata=eventdata)
|
||||
self.buffer += data
|
||||
#TODO: analyze buffer for registered events, examples:
|
||||
# panics
|
||||
# certificate signing request
|
||||
if len(self.buffer) > 8192:
|
||||
#call to function to get generic data to log if applicable
|
||||
#and shrink buffer
|
||||
if len(self.buffer) > 16384:
|
||||
self.flushbuffer()
|
||||
for rcpt in self.rcpts.itervalues():
|
||||
try:
|
||||
rcpt(data)
|
||||
rcpt(prefix + data)
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -110,23 +126,31 @@ class _ConsoleHandler(object):
|
||||
#For now, just try to seek back in buffer to find a clear screen
|
||||
#If that fails, just return buffer
|
||||
#a scheme always tracking the last clear screen would be too costly
|
||||
retdata = ''
|
||||
if self.shiftin is not None: #detected that terminal requested a
|
||||
#shiftin character set, relay that to the terminal that cannected
|
||||
retdata += '\x1b)' + self.shiftin
|
||||
if self.appmodedetected:
|
||||
retdata += '\x1b[?1h'
|
||||
else:
|
||||
retdata += '\x1b[?1l'
|
||||
#an alternative would be to emulate a VT100 to know what the
|
||||
#whole screen would look like
|
||||
#this is one scheme to clear screen, move cursor then clear
|
||||
bufidx = self.buffer.rfind('\x1b[H\x1b[J')
|
||||
if bufidx >= 0:
|
||||
return str(self.buffer[bufidx:])
|
||||
return retdata + str(self.buffer[bufidx:])
|
||||
#another scheme is the 2J scheme
|
||||
bufidx = self.buffer.rfind('\x1b[2J')
|
||||
if bufidx >= 0:
|
||||
# there was some sort of clear screen event
|
||||
# somewhere in the buffer, replay from that point
|
||||
# in hopes that it reproduces the screen
|
||||
return str(self.buffer[bufidx:])
|
||||
return retdata + str(self.buffer[bufidx:])
|
||||
else:
|
||||
#we have no indication of last erase, play back last kibibyte
|
||||
#to give some sense of context anyway
|
||||
return str(self.buffer[-1024:])
|
||||
return retdata + str(self.buffer[-1024:])
|
||||
|
||||
def write(self, data):
|
||||
#TODO.... take note of data coming in from audit/log perspective?
|
||||
|
@ -118,9 +118,12 @@ class Logger(object):
|
||||
'%b %d %H:%M:%S ', time.localtime(tstamp))
|
||||
offset = self.textfile.tell() + len(textdate)
|
||||
datalen = len(data)
|
||||
eventaux = entry[4]
|
||||
if eventaux is None:
|
||||
eventaux = 0
|
||||
# metadata length is always 16 for this code at the moment
|
||||
binrecord = struct.pack(">BBIHIBBH",
|
||||
16, ltype, offset, datalen, tstamp, evtdata, entry[4], 0)
|
||||
16, ltype, offset, datalen, tstamp, evtdata, eventaux, 0)
|
||||
if self.isconsole:
|
||||
if ltype == 2:
|
||||
textrecord = data
|
||||
@ -136,7 +139,7 @@ class Logger(object):
|
||||
self.closer = eventlet.spawn_after(15, self.closelog)
|
||||
self.writer = None
|
||||
|
||||
def log(self, logdata=None, ltype=None, event=0, eventdata=0):
|
||||
def log(self, logdata=None, ltype=None, event=0, eventdata=None):
|
||||
if type(logdata) not in (str, unicode, dict):
|
||||
raise Exception("Unsupported logdata")
|
||||
if ltype is None:
|
||||
@ -154,6 +157,8 @@ class Logger(object):
|
||||
event == 0 and self.logentries[-1][0] == 2 and
|
||||
self.logentries[-1][1] == timestamp):
|
||||
self.logentries[-1][2] += logdata
|
||||
if eventdata is not None:
|
||||
self.logentries[-1][4] = eventdata
|
||||
else:
|
||||
self.logentries.append([ltype, timestamp, logdata, event, eventdata])
|
||||
if self.writer is None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user