diff --git a/mmgen/node_tools/Sound.py b/mmgen/node_tools/Sound.py index cd200e0..ba59a24 100644 --- a/mmgen/node_tools/Sound.py +++ b/mmgen/node_tools/Sound.py @@ -42,22 +42,22 @@ def parse_repeat_spec(rs): def init_sound(): def _restore_sound(): # msg('Restoring sound volume') - do_system('alsactl restore -f ' + _alsa_config_file) + do_system('sudo alsactl restore -f ' + _alsa_config_file) os.unlink(_alsa_config_file) import atexit atexit.register(_restore_sound) - do_system('alsactl store -f ' + _alsa_config_file) + do_system('sudo alsactl store -f ' + _alsa_config_file) def play_sound(fn,vol,repeat_spec='',remote_host='',kill_flg=None,testing=False): if not remote_host: - do_system('alsactl store -f ' + _alsa_config_file) + do_system('sudo alsactl store -f ' + _alsa_config_file) for k in 'Master','Speaker','Headphone': - do_system(('amixer -q set %s on' % k),testing) + do_system(('sudo amixer -q set %s on' % k),testing) # do_system('amixer -q set Headphone off') vols = dict([(k,int(_dvols[k] * float(vol) / 100)) for k in _dvols]) for k in vols: - do_system('amixer -q set %s %s' % (k,vols[k]),testing) + do_system('sudo amixer -q set %s %s' % (k,vols[k]),testing) fn = os.path.expanduser(fn) cmd = ( @@ -72,9 +72,9 @@ def play_sound(fn,vol,repeat_spec='',remote_host='',kill_flg=None,testing=False) do_system(cmd,testing) if kill_flg.wait(interval): if not remote_host: - do_system('alsactl restore -f ' + _alsa_config_file) + do_system('sudo alsactl restore -f ' + _alsa_config_file) return else: # Play once do_system(cmd,testing) if not remote_host: - do_system('alsactl restore -f ' + _alsa_config_file) + do_system('sudo alsactl restore -f ' + _alsa_config_file) diff --git a/mmgen/node_tools/Util.py b/mmgen/node_tools/Util.py index 1fcf34b..fb7e318 100644 --- a/mmgen/node_tools/Util.py +++ b/mmgen/node_tools/Util.py @@ -20,6 +20,7 @@ mmgen.node_tools.Util: utility functions for MMGen node tools """ import time,subprocess +from mmgen.util import msg def get_hms(t=None,utc=False,no_secs=False): secs = t or time.time() diff --git a/mmnode-netrate b/mmnode-netrate index 1266951..a21d7a7 100755 --- a/mmnode-netrate +++ b/mmnode-netrate @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2017 Philemon +# Copyright (C)2013-2020 The MMGen Project # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software @@ -37,24 +37,33 @@ opts_data = { cmd_args = opts.init(opts_data) ERASE_LINE,CUR_UP = '\033[K','\033[1A' -c = rpc_init() -def do_loop(): - def get_data(): - d = c.getnettotals() +async def main(): + from mmgen.rpc import rpc_init + c = await rpc_init() + + async def get_data(): + d = await c.call('getnettotals') return [float(e) for e in (d['totalbytesrecv'],d['totalbytessent'],d['timemillis'])] - r,s,t = get_data() - time.sleep(0.2) + rs = None while True: - rs,ss,ts = r,s,t - r,s,t = get_data() - td = t-ts - sys.stderr.write('\rrcvd: {:9.2f} kB/s\nsent: {:9.2f} kB/s '.format((r-rs)/td,(s-ss)/td)) + r,s,t = await get_data() + + if rs is not None: + sys.stderr.write( + '\rrcvd: {:9.2f} kB/s\nsent: {:9.2f} kB/s '.format( + (r-rs)/(t-ts), + (s-ss)/(t-ts) )) + time.sleep(2) - sys.stderr.write('{}{}{}'.format(ERASE_LINE,CUR_UP,ERASE_LINE)) + + if rs is not None: + sys.stderr.write('{}{}{}'.format(ERASE_LINE,CUR_UP,ERASE_LINE)) + + rs,ss,ts = r,s,t try: - do_loop() + run_session(main(),do_rpc_init=False) except KeyboardInterrupt: sys.stderr.write('\n') diff --git a/mmnode-peerblocks b/mmnode-peerblocks index a3a2f30..71c2fae 100755 --- a/mmnode-peerblocks +++ b/mmnode-peerblocks @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2017 Philemon +# Copyright (C)2013-2020 The MMGen Project # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software @@ -20,7 +20,7 @@ mmgen-peerblocks: List blocks in flight, disconnect stalling nodes """ -import time,threading +import asyncio from mmgen.common import * opts_data = { @@ -34,85 +34,119 @@ opts_data = { } } -cmd_args = opts.init(opts_data) +opts.init(opts_data) -colors = ['\033[38;5;%s;1m' % c for c in (238,240,242,244,246,247,249,251,253,255)] -_red,_reset = '\033[31m','\033[0m' +async def inflight_display(rpc): -ERASE_ALL,ERASE_LINE,CUR_HOME,CUR_HIDE,CUR_SHOW = \ - '\033[J','\033[K','\033[H','\033[?25l','\033[?25h' + def gen_peers(peerinfo): + global min_height + min_height = None + for d in peerinfo: + if 'inflight' in d and d['inflight']: + blks = d['inflight'] + if not min_height or min_height > blks[0]: + min_height = blks[0] + blks_trunc = ' '.join(map(str,blks))[:term_width-6].split() + trim = blks_trunc[-1] != str(blks[len(blks_trunc)-1]) + blks = blks[:len(blks_trunc)-trim] + else: + blks = [] + yield { 'id': d['id'], 'data': blks } -import atexit -def at_exit(): - import os - os.system('stty sane') - sys.stderr.write('\n') -atexit.register(at_exit) + def gen_line(peer): + for blk in peer['data']: + if blk == min_height: + yield RED + str(blk) + RESET + else: + yield COLORS[blk % 10] + str(blk) + RESET -bc = rpc_init() - -msg_r(CUR_HOME+ERASE_ALL) - -def do_display(): from mmgen.term import get_terminal_size - global data + term_width = get_terminal_size()[0] + count = 1 while True: - twid = get_terminal_size()[0] - data = bc.getpeerinfo() - min_t = None - lines = [] - with lock: - msg('{}{}{}ACTIVE PEERS ({}) - poll {}'.format( - CUR_HOME,ERASE_ALL,CUR_HOME,len(data),count)) - for d in data: - line = { 'id': d['id'], 'data': [] } - if 'inflight' in d and d['inflight']: - blks = [str(e) for e in d['inflight']] - min_p = min(e for e in d['inflight']) - if not min_t or min_t > min_p: min_t = min_p - line_d = ' '.join(blks)[:twid-6] - blks = blks[:len(line_d) - len(line_d.replace(' ','')) + 1] - blks[-1] = blks[-1][:len(line_d.split(' ')[-1])] - line['data'] = [[colors[int(i)%10],i,_reset] for i in blks if i] - else: - line['data'] = [] - lines.append(line) - for line in lines: - d = ' '.join([(a,_red)[int(b)==min_t]+b+c for a,b,c in line['data']]) - sys.stderr.write('\r{} {:>3}: {}\n'.format(ERASE_LINE,line['id'],d)) - msg_r(ERASE_ALL+'Hit ENTER for disconnect prompt: ') - time.sleep(2) + info = await rpc.call('getpeerinfo') + + msg_r(CUR_HOME+ERASE_ALL+CUR_HOME) + msg(f'ACTIVE PEERS ({len(info)}) - poll {count}') + + for peer in gen_peers(info): + sys.stderr.write('\r{} {:>3}: {}\n'.format( + ERASE_LINE, + peer['id'], + ' '.join(gen_line(peer)) )) + + msg_r(ERASE_ALL+'Hit ENTER for disconnect prompt: ') + await asyncio.sleep(2) count += 1 -lock = threading.Lock() -data = {} +async def do_inflight(rpc): + task = asyncio.ensure_future(inflight_display(rpc)) # Python 3.7+: create_task() + from select import select -t = threading.Thread(target=do_display,name='display') -t.daemon = True -t.start() - -def do_loop(): - global data while True: - input() - with lock: - ids = [str(d['id']) for d in data] - msg('{}{}{}ACTIVE PEERS ({})'.format(CUR_HOME,ERASE_ALL,CUR_HOME,len(data))) - msg(' '+'\n '.join(['{:>3}: {:30} {}'.format(*[d[k] for k in ('id','addr','subver')]) for d in data])) - reply = input('Enter a peer number to disconnect> ') - if reply == '': - pass - elif reply in ids: - idx = ids.index(reply) - msg("Disconnecting peer {} ('{}')".format(reply,data[idx]['addr'])) - bc.disconnectnode(data[idx]['addr']) - time.sleep(1.5) - else: - msg("'{}': invalid peer number".format(reply)) - time.sleep(0.5) + key = select([sys.stdin], [], [], 0.1)[0] + if key: + sys.stdin.read(1) + task.cancel() + break + await asyncio.sleep(0.1) + + try: + await task + except asyncio.CancelledError: + pass + +async def do_disconnect_menu(rpc): + + while True: + peerinfo = await rpc.call('getpeerinfo') + ids = [str(d['id']) for d in peerinfo] + + msg_r(CUR_HOME+ERASE_ALL+CUR_HOME) + msg(f'ACTIVE PEERS ({len(peerinfo)})') + + if peerinfo: + msg('\n'.join([f"{d['id']:>3}: {d['addr']:30} {d['subver']}" for d in peerinfo])) + + reply = input("Peer number to disconnect, ENTER to quit menu, 'u' to update peer list> ") + + if reply == '': + return + elif reply == 'u': + msg(f'Updating peer list') + await asyncio.sleep(0.5) + elif reply in ids: + addr = peerinfo[ids.index(reply)]['addr'] + msg(f'Disconnecting peer {reply} ({addr})') + try: + await rpc.call('disconnectnode',addr) + except RPCFailure: + msg(f'Unable to disconnect peer {addr}') + await asyncio.sleep(1.5) + else: + msg(f'{reply!r}: invalid peer number') + await asyncio.sleep(0.5) + +async def main(): + + msg_r(CUR_HOME+ERASE_ALL) + + from mmgen.rpc import rpc_init + rpc = await rpc_init() + + while True: + await do_inflight(rpc) + await do_disconnect_menu(rpc) + +RED,RESET = ('\033[31m','\033[0m') +COLORS = ['\033[38;5;%s;1m' % c for c in (238,240,242,244,246,247,249,251,253,255)] +ERASE_ALL,ERASE_LINE,CUR_HOME,CUR_HIDE,CUR_SHOW = ( + '\033[J','\033[K','\033[H','\033[?25l','\033[?25h') try: - do_loop() -except KeyboardInterrupt: - pass + run_session(main(),do_rpc_init=False) +except: + from subprocess import run + run(['stty','sane']) + msg('') diff --git a/mmnode-play-sound b/mmnode-play-sound index 25b4fbc..133bed2 100755 --- a/mmnode-play-sound +++ b/mmnode-play-sound @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C)2013-2016 Philemon +# Copyright (C)2013-2020 The MMGen Project # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -32,6 +32,7 @@ opts_data = { 'usage': '[opts]', 'options': """ -h, --help Print this help message +-t, --testing Test, don't execute shell commands -v, --volume= n Adjust sound volume by percentage 'n' (default: {}) """.format(volume) } @@ -40,10 +41,8 @@ opts_data = { args = opts.init(opts_data) if opt.volume: - volume = opt.volume try: - volume = int(volume) - assert 1 <= volume <= 120 + assert 1 <= int(opt.volume) <= 120 except: die(1,'Sound volume must be an integer between 1 and 120') @@ -53,4 +52,4 @@ if len(args) != 1: try: os.stat(args[0]) except: die(1,"Couldn't stat file '{}'".format(args[0])) -play_sound(args[0],volume) +play_sound(args[0],int(opt.volume),testing=opt.testing)