summaryrefslogtreecommitdiff
path: root/voctocore/lib/controlserver.py
blob: 835cc8eee3c445db9f1694992a40fd3be853650d (plain)
  1. #!/usr/bin/python3
  2. import socket, logging, traceback
  3. from gi.repository import GObject
  4. from lib.commands import ControlServerCommands
  5. from lib.tcpmulticonnection import TCPMultiConnection
  6. class ControlServer(TCPMultiConnection):
  7. log = logging.getLogger('ControlServer')
  8. boundSocket = None
  9. def __init__(self, pipeline):
  10. '''Initialize server and start listening.'''
  11. super().__init__(port=9999)
  12. self.commands = ControlServerCommands(pipeline)
  13. def on_accepted(self, conn, addr):
  14. '''Asynchronous connection listener. Starts a handler for each connection.'''
  15. self.log.debug('Setting GObject io-watch on Connection')
  16. GObject.io_add_watch(conn, GObject.IO_IN, self.on_data)
  17. def on_data(self, conn, *args):
  18. '''Asynchronous connection handler. Processes each line from the socket.'''
  19. # construct a file-like object fro mthe socket
  20. # to be able to read linewise and in utf-8
  21. filelike = conn.makefile('rw')
  22. # read a line from the socket
  23. line = ''
  24. try:
  25. line = filelike.readline().strip()
  26. except Exception as e:
  27. self.log.warn("Can't read from socket: %s", e)
  28. # no data = remote closed connection
  29. if len(line) == 0:
  30. self.close_connection(conn)
  31. return False
  32. # 'quit' = remote wants us to close the connection
  33. if line == 'quit':
  34. self.log.info("Client asked us to close the Connection")
  35. self.close_connection(conn)
  36. return False
  37. # process the received line
  38. success, msg = self.processLine(line)
  39. # success = False -> error
  40. if success == False:
  41. # on error-responses the message is mandatory
  42. if msg is None:
  43. msg = '<no message>'
  44. # respond with 'error' and the message
  45. filelike.write('error '+msg+'\n')
  46. self.log.info("Function-Call returned an Error: %s", msg)
  47. # keep on listening on that connection
  48. return True
  49. # success = True and not message
  50. if msg is None:
  51. # respond with a simple 'ok'
  52. filelike.write('ok\n')
  53. else:
  54. # respond with the returned message
  55. filelike.write('ok '+msg+'\n')
  56. return True
  57. def processLine(self, line):
  58. # split line into command and optional args
  59. command, argstring = (line+' ').split(' ', 1)
  60. args = argstring.strip().split()
  61. # log function-call as parsed
  62. self.log.info("Read Function-Call from Socket: %s( %s )", command, args)
  63. # check that the function-call is a known Command
  64. if not hasattr(self.commands, command):
  65. return False, 'unknown command %s' % command
  66. try:
  67. # fetch the function-pointer
  68. f = getattr(self.commands, command)
  69. # call the function
  70. ret = f(*args)
  71. # if it returned an iterable, probably (Success, Message), pass that on
  72. if hasattr(ret, '__iter__'):
  73. return ret
  74. else:
  75. # otherwise construct a tuple
  76. return (ret, None)
  77. except Exception as e:
  78. self.log.error("Trapped Exception in Remote-Communication: %s", e)
  79. traceback.print_exc()
  80. # In case of an Exception, return that
  81. return False, str(e)