2017-02-09 21:11:41 +01:00
|
|
|
#!/usr/bin/env python3.6
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import asyncio
|
|
|
|
import signal
|
|
|
|
import sys
|
|
|
|
|
|
|
|
QUBESD_SOCK = '/var/run/qubesd.sock'
|
2021-03-02 02:33:21 +01:00
|
|
|
MAX_PAYLOAD_SIZE = 65536
|
2017-02-09 21:11:41 +01:00
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
description='low-level qubesd interrogation tool')
|
|
|
|
|
|
|
|
parser.add_argument('--connect', '-c', metavar='PATH',
|
|
|
|
dest='socket',
|
|
|
|
default=QUBESD_SOCK,
|
|
|
|
help='path to qubesd UNIX socket (default: %(default)s)')
|
|
|
|
|
|
|
|
parser.add_argument('--empty', '-e',
|
|
|
|
dest='payload',
|
|
|
|
action='store_false', default=True,
|
|
|
|
help='do not read from stdin and send empty payload')
|
|
|
|
|
2017-06-02 20:35:36 +02:00
|
|
|
parser.add_argument('--fail',
|
|
|
|
dest='fail',
|
|
|
|
action='store_true',
|
|
|
|
help='Should non-OK qubesd response result in non-zero exit code')
|
|
|
|
|
2017-02-09 21:11:41 +01:00
|
|
|
parser.add_argument('src', metavar='SRC',
|
|
|
|
help='source qube')
|
|
|
|
parser.add_argument('method', metavar='METHOD',
|
|
|
|
help='method name')
|
|
|
|
parser.add_argument('dest', metavar='DEST',
|
|
|
|
help='destination qube')
|
|
|
|
parser.add_argument('arg', metavar='ARGUMENT',
|
|
|
|
nargs='?', default='',
|
|
|
|
help='argument to method')
|
|
|
|
|
|
|
|
def sighandler(loop, signame, coro):
|
|
|
|
print('caught {}, exiting'.format(signame))
|
|
|
|
coro.cancel()
|
|
|
|
loop.stop()
|
|
|
|
|
|
|
|
@asyncio.coroutine
|
|
|
|
def qubesd_client(socket, payload, *args):
|
2017-06-02 20:35:36 +02:00
|
|
|
'''
|
|
|
|
Connect to qubesd, send request and passthrough response to stdout
|
|
|
|
|
|
|
|
:param socket: path to qubesd socket
|
|
|
|
:param payload: payload of the request
|
|
|
|
:param args: request to qubesd
|
|
|
|
:return:
|
|
|
|
'''
|
2017-02-09 21:11:41 +01:00
|
|
|
try:
|
|
|
|
reader, writer = yield from asyncio.open_unix_connection(socket)
|
|
|
|
except asyncio.CancelledError:
|
2017-06-02 20:35:36 +02:00
|
|
|
return 1
|
2017-02-09 21:11:41 +01:00
|
|
|
|
|
|
|
for arg in args:
|
|
|
|
writer.write(arg.encode('ascii'))
|
|
|
|
writer.write(b'\0')
|
|
|
|
writer.write(payload)
|
|
|
|
writer.write_eof()
|
|
|
|
|
|
|
|
try:
|
2017-06-02 20:35:36 +02:00
|
|
|
header_data = yield from reader.read(1)
|
|
|
|
returncode = int(header_data)
|
|
|
|
sys.stdout.buffer.write(header_data) # pylint: disable=no-member
|
2017-04-10 01:47:04 +02:00
|
|
|
while not reader.at_eof():
|
|
|
|
data = yield from reader.read(4096)
|
|
|
|
sys.stdout.buffer.write(data) # pylint: disable=no-member
|
|
|
|
sys.stdout.flush()
|
2017-06-02 20:35:36 +02:00
|
|
|
return returncode
|
2017-02-09 21:11:41 +01:00
|
|
|
except asyncio.CancelledError:
|
2017-06-02 20:35:36 +02:00
|
|
|
return 1
|
2017-02-09 21:11:41 +01:00
|
|
|
finally:
|
|
|
|
writer.close()
|
|
|
|
|
|
|
|
def main(args=None):
|
|
|
|
args = parser.parse_args(args)
|
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
|
|
|
|
# pylint: disable=no-member
|
2021-03-02 02:33:21 +01:00
|
|
|
if args.payload:
|
|
|
|
# read one byte more to check for too long payload,
|
|
|
|
# instead of silently truncating
|
|
|
|
payload = sys.stdin.buffer.read(MAX_PAYLOAD_SIZE + 1)
|
|
|
|
if len(payload) > MAX_PAYLOAD_SIZE:
|
|
|
|
parser.error('Payload too long (max {})'.format(MAX_PAYLOAD_SIZE))
|
|
|
|
# make sure to terminate, even if parser.error() would return
|
|
|
|
# for some reason
|
|
|
|
return 1
|
|
|
|
else:
|
|
|
|
payload = b''
|
2017-02-09 21:11:41 +01:00
|
|
|
# pylint: enable=no-member
|
|
|
|
|
2020-04-20 03:29:29 +02:00
|
|
|
coro = asyncio.ensure_future(qubesd_client(
|
|
|
|
args.socket, payload,
|
|
|
|
f'{args.method}+{args.arg} {args.src} name {args.dest}'))
|
2017-02-09 21:11:41 +01:00
|
|
|
|
|
|
|
for signame in ('SIGINT', 'SIGTERM'):
|
|
|
|
loop.add_signal_handler(getattr(signal, signame),
|
|
|
|
sighandler, loop, signame, coro)
|
|
|
|
|
|
|
|
try:
|
2017-06-02 20:35:36 +02:00
|
|
|
returncode = loop.run_until_complete(coro)
|
2017-02-09 21:11:41 +01:00
|
|
|
finally:
|
|
|
|
loop.close()
|
|
|
|
|
2017-06-02 20:35:36 +02:00
|
|
|
if args.fail:
|
|
|
|
return returncode
|
|
|
|
return 0
|
|
|
|
|
2017-02-09 21:11:41 +01:00
|
|
|
if __name__ == '__main__':
|
2017-06-02 20:35:36 +02:00
|
|
|
sys.exit(main())
|