ec3po: Add graceful exit.

The console and interpreter are usually killed by KeyboardInterrupt
whether or not it's run standalone or by servod.  This commit tries to
make the exit graceful by closing pipes, file descriptors, and exiting
each process.

BUG=chromium:570526
BRANCH=None
TEST=Run ec3po standalone and hit Ctrl+C to kill it.  Observe no
traceback and no leftover processes.
TEST=Repeat above test, but inside servod
TEST=cros lint --debug util/ec3po/console.py
TEST=cros lint --debug util/ec3po/interpreter.py
TEST=python2 -b util/ec3po/console_interpreter.py
TEST=python2 -b util/ec3po/console_interpreter.py

Change-Id: Ia151b9ede8adf7f8dec6c07277f62d097c13e63e
Signed-off-by: Aseda Aboagye <aaboagye@google.com>
Reviewed-on: https://chromium-review.googlesource.com/319252
Commit-Ready: Aseda Aboagye <aaboagye@chromium.org>
Tested-by: Aseda Aboagye <aaboagye@chromium.org>
Reviewed-by: Wai-Hong Tam <waihong@chromium.org>
This commit is contained in:
Aseda Aboagye
2015-12-17 16:55:01 -08:00
committed by chrome-bot
parent 6a29ca187a
commit 4bad633cb9
2 changed files with 57 additions and 37 deletions

View File

@@ -635,32 +635,42 @@ def StartLoop(console):
"""
console.logger.info('EC Console is being served on %s.', console.user_pty)
console.logger.debug(console)
while True:
# Check to see if pipes or the console are ready for reading.
read_list = [console.master_pty, console.cmd_pipe, console.dbg_pipe]
ready_for_reading = select.select(read_list, [], [])[0]
try:
while True:
# Check to see if pipes or the console are ready for reading.
read_list = [console.master_pty, console.cmd_pipe, console.dbg_pipe]
ready_for_reading = select.select(read_list, [], [])[0]
for obj in ready_for_reading:
if obj is console.master_pty:
console.logger.debug('Input from user')
# Convert to bytes so we can look for non-printable chars such as
# Ctrl+A, Ctrl+E, etc.
line = bytearray(os.read(console.master_pty, CONSOLE_MAX_READ))
for i in line:
# Handle each character as it arrives.
console.HandleChar(i)
for obj in ready_for_reading:
if obj is console.master_pty:
console.logger.debug('Input from user')
# Convert to bytes so we can look for non-printable chars such as
# Ctrl+A, Ctrl+E, etc.
line = bytearray(os.read(console.master_pty, CONSOLE_MAX_READ))
for i in line:
# Handle each character as it arrives.
console.HandleChar(i)
elif obj is console.cmd_pipe:
data = console.cmd_pipe.recv()
# Write it to the user console.
console.logger.debug('|CMD|->\'%s\'', data)
os.write(console.master_pty, data)
elif obj is console.cmd_pipe:
data = console.cmd_pipe.recv()
# Write it to the user console.
console.logger.debug('|CMD|->\'%s\'', data)
os.write(console.master_pty, data)
elif obj is console.dbg_pipe:
data = console.dbg_pipe.recv()
# Write it to the user console.
console.logger.debug('|DBG|->\'%s\'', data)
os.write(console.master_pty, data)
elif obj is console.dbg_pipe:
data = console.dbg_pipe.recv()
# Write it to the user console.
console.logger.debug('|DBG|->\'%s\'', data)
os.write(console.master_pty, data)
finally:
# Close pipes.
console.dbg_pipe.close()
console.cmd_pipe.close()
# Close file descriptor.
os.close(console.master_pty)
# Exit.
sys.exit(0)
def main(argv):
@@ -676,7 +686,6 @@ def main(argv):
# Set up argument parser.
parser = argparse.ArgumentParser(description=('Start interactive EC console '
'and interpreter.'))
# TODO(aaboagye): Eventually get this from servod.
parser.add_argument('ec_uart_pty',
help=('The full PTY name that the EC UART'
' is present on. eg: /dev/pts/12'))

View File

@@ -20,6 +20,7 @@ import logging
import os
import Queue
import select
import sys
COMMAND_RETRIES = 3 # Number of attempts to retry a command.
@@ -312,19 +313,29 @@ def StartLoop(interp):
Args:
interp: An Interpreter object that has been properly initialised.
"""
while True:
readable, writeable, _ = select.select(interp.inputs, interp.outputs, [])
try:
while True:
readable, writeable, _ = select.select(interp.inputs, interp.outputs, [])
for obj in readable:
# Handle any debug prints from the EC.
if obj is interp.ec_uart_pty:
interp.HandleECData()
for obj in readable:
# Handle any debug prints from the EC.
if obj is interp.ec_uart_pty:
interp.HandleECData()
# Handle any commands from the user.
elif obj is interp.cmd_pipe:
interp.HandleUserData()
# Handle any commands from the user.
elif obj is interp.cmd_pipe:
interp.HandleUserData()
for obj in writeable:
# Send a command to the EC.
if obj is interp.ec_uart_pty:
interp.SendCmdToEC()
for obj in writeable:
# Send a command to the EC.
if obj is interp.ec_uart_pty:
interp.SendCmdToEC()
finally:
# Close pipes.
interp.cmd_pipe.close()
interp.dbg_pipe.close()
# Close file descriptor.
interp.ec_uart_pty.close()
# Exit.
sys.exit(0)