qubesd_query.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. #!/usr/bin/env python3.6
  2. import argparse
  3. import asyncio
  4. import signal
  5. import sys
  6. QUBESD_SOCK = '/var/run/qubesd.sock'
  7. try:
  8. asyncio.ensure_future
  9. except AttributeError:
  10. asyncio.ensure_future = asyncio.async
  11. parser = argparse.ArgumentParser(
  12. description='low-level qubesd interrogation tool')
  13. parser.add_argument('--connect', '-c', metavar='PATH',
  14. dest='socket',
  15. default=QUBESD_SOCK,
  16. help='path to qubesd UNIX socket (default: %(default)s)')
  17. parser.add_argument('--empty', '-e',
  18. dest='payload',
  19. action='store_false', default=True,
  20. help='do not read from stdin and send empty payload')
  21. parser.add_argument('--fail',
  22. dest='fail',
  23. action='store_true',
  24. help='Should non-OK qubesd response result in non-zero exit code')
  25. parser.add_argument('src', metavar='SRC',
  26. help='source qube')
  27. parser.add_argument('method', metavar='METHOD',
  28. help='method name')
  29. parser.add_argument('dest', metavar='DEST',
  30. help='destination qube')
  31. parser.add_argument('arg', metavar='ARGUMENT',
  32. nargs='?', default='',
  33. help='argument to method')
  34. def sighandler(loop, signame, coro):
  35. print('caught {}, exiting'.format(signame))
  36. coro.cancel()
  37. loop.stop()
  38. @asyncio.coroutine
  39. def qubesd_client(socket, payload, *args):
  40. '''
  41. Connect to qubesd, send request and passthrough response to stdout
  42. :param socket: path to qubesd socket
  43. :param payload: payload of the request
  44. :param args: request to qubesd
  45. :return:
  46. '''
  47. try:
  48. reader, writer = yield from asyncio.open_unix_connection(socket)
  49. except asyncio.CancelledError:
  50. return 1
  51. for arg in args:
  52. writer.write(arg.encode('ascii'))
  53. writer.write(b'\0')
  54. writer.write(payload)
  55. writer.write_eof()
  56. try:
  57. header_data = yield from reader.read(1)
  58. returncode = int(header_data)
  59. sys.stdout.buffer.write(header_data) # pylint: disable=no-member
  60. while not reader.at_eof():
  61. data = yield from reader.read(4096)
  62. sys.stdout.buffer.write(data) # pylint: disable=no-member
  63. sys.stdout.flush()
  64. return returncode
  65. except asyncio.CancelledError:
  66. return 1
  67. finally:
  68. writer.close()
  69. def main(args=None):
  70. args = parser.parse_args(args)
  71. loop = asyncio.get_event_loop()
  72. # pylint: disable=no-member
  73. payload = sys.stdin.buffer.read() if args.payload else b''
  74. # pylint: enable=no-member
  75. coro = asyncio.ensure_future(qubesd_client(args.socket, payload,
  76. args.src, args.method, args.dest, args.arg))
  77. for signame in ('SIGINT', 'SIGTERM'):
  78. loop.add_signal_handler(getattr(signal, signame),
  79. sighandler, loop, signame, coro)
  80. try:
  81. returncode = loop.run_until_complete(coro)
  82. finally:
  83. loop.close()
  84. if args.fail:
  85. return returncode
  86. return 0
  87. if __name__ == '__main__':
  88. sys.exit(main())