qubesd_query.py 2.9 KB

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