basicswap/bin/basicswap_run.py

336 lines
12 KiB
Python
Raw Normal View History

2019-07-21 18:50:32 +00:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
2022-11-28 17:54:41 +00:00
# Copyright (c) 2019-2022 tecnovert
2019-07-21 18:50:32 +00:00
# Distributed under the MIT software license, see the accompanying
2020-10-30 08:55:45 +00:00
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
2019-07-21 18:50:32 +00:00
import os
2020-12-02 21:19:10 +00:00
import sys
2019-07-21 18:50:32 +00:00
import json
2020-12-02 21:19:10 +00:00
import time
import shutil
2019-07-21 18:50:32 +00:00
import signal
import logging
2020-12-02 21:19:10 +00:00
import traceback
import subprocess
2020-02-01 18:57:20 +00:00
import basicswap.config as cfg
2019-07-21 18:50:32 +00:00
from basicswap import __version__
2022-10-24 18:49:36 +00:00
from basicswap.ui.util import getCoinName
from basicswap.basicswap import BasicSwap
2019-07-21 18:50:32 +00:00
from basicswap.http_server import HttpThread
2022-07-31 17:33:01 +00:00
from basicswap.contrib.websocket_server import WebsocketServer
2019-07-21 18:50:32 +00:00
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
logger.addHandler(logging.StreamHandler(sys.stdout))
swap_client = None
def signal_handler(sig, frame):
2019-08-15 22:31:39 +00:00
global swap_client
2019-07-21 18:50:32 +00:00
logger.info('Signal %d detected, ending program.' % (sig))
if swap_client is not None:
swap_client.stopRunning()
2019-07-21 21:27:46 +00:00
def startDaemon(node_dir, bin_dir, daemon_bin, opts=[]):
2019-07-23 14:26:37 +00:00
daemon_bin = os.path.expanduser(os.path.join(bin_dir, daemon_bin))
2019-07-21 18:50:32 +00:00
datadir_path = os.path.expanduser(node_dir)
args = [daemon_bin, '-datadir=' + datadir_path] + opts
2020-11-07 11:08:07 +00:00
logging.info('Starting node ' + daemon_bin + ' ' + '-datadir=' + node_dir)
return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=datadir_path)
2019-07-21 18:50:32 +00:00
def startXmrDaemon(node_dir, bin_dir, daemon_bin, opts=[]):
daemon_bin = os.path.expanduser(os.path.join(bin_dir, daemon_bin))
datadir_path = os.path.expanduser(node_dir)
args = [daemon_bin, '--non-interactive', '--config-file=' + os.path.join(datadir_path, 'monerod.conf')] + opts
logging.info('Starting node {} --data-dir={}'.format(daemon_bin, node_dir))
# return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
file_stdout = open(os.path.join(datadir_path, 'core_stdout.log'), 'w')
file_stderr = open(os.path.join(datadir_path, 'core_stderr.log'), 'w')
return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=file_stdout, stderr=file_stderr, cwd=datadir_path)
def startXmrWalletDaemon(node_dir, bin_dir, wallet_bin, opts=[]):
daemon_bin = os.path.expanduser(os.path.join(bin_dir, wallet_bin))
data_dir = os.path.expanduser(node_dir)
config_path = os.path.join(data_dir, 'monero_wallet.conf')
args = [daemon_bin, '--non-interactive', '--config-file=' + config_path] + opts
# TODO: Remove
# Remove daemon-address
has_daemon_address = False
has_untrusted = False
with open(config_path) as fp:
for line in fp:
if line.startswith('daemon-address'):
has_daemon_address = True
if line.startswith('untrusted-daemon'):
has_untrusted = True
if has_daemon_address:
logging.info('Rewriting monero_wallet.conf')
shutil.copyfile(config_path, config_path + '.last')
with open(config_path + '.last') as fp_from, open(config_path, 'w') as fp_to:
for line in fp_from:
if not line.startswith('daemon-address'):
fp_to.write(line)
if not has_untrusted:
fp_to.write('untrusted-daemon=1\n')
logging.info('Starting wallet daemon {} --wallet-dir={}'.format(daemon_bin, node_dir))
2020-12-04 21:30:20 +00:00
# TODO: return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=data_dir)
wallet_stdout = open(os.path.join(data_dir, 'wallet_stdout.log'), 'w')
wallet_stderr = open(os.path.join(data_dir, 'wallet_stderr.log'), 'w')
return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=wallet_stdout, stderr=wallet_stderr, cwd=data_dir)
2020-12-04 21:30:20 +00:00
2022-07-31 17:33:01 +00:00
def ws_new_client(client, server):
if swap_client:
swap_client.log.debug(f'ws_new_client {client["id"]}')
def ws_client_left(client, server):
if client is None:
return
if swap_client:
swap_client.log.debug(f'ws_client_left {client["id"]}')
def ws_message_received(client, server, message):
if len(message) > 200:
message = message[:200] + '..'
if swap_client:
swap_client.log.debug(f'ws_message_received {client["id"]} {message}')
2019-08-15 22:31:39 +00:00
def runClient(fp, data_dir, chain):
2019-07-21 18:50:32 +00:00
global swap_client
daemons = []
pids = []
threads = []
2020-02-01 18:57:20 +00:00
settings_path = os.path.join(data_dir, cfg.CONFIG_FILENAME)
2019-07-25 13:28:44 +00:00
pids_path = os.path.join(data_dir, '.pids')
2019-07-21 18:50:32 +00:00
if os.getenv('WALLET_ENCRYPTION_PWD', '') != '':
raise ValueError('Please unset the WALLET_ENCRYPTION_PWD environment variable.')
2019-07-21 18:50:32 +00:00
if not os.path.exists(settings_path):
raise ValueError('Settings file not found: ' + str(settings_path))
with open(settings_path) as fs:
settings = json.load(fs)
swap_client = BasicSwap(fp, data_dir, settings, chain)
2019-07-25 13:28:44 +00:00
if os.path.exists(pids_path):
with open(pids_path) as fd:
for ln in fd:
# TODO: try close
logger.warning('Found pid for daemon {} '.format(ln.strip()))
2019-07-21 18:50:32 +00:00
# Ensure daemons are stopped
swap_client.stopDaemons()
2019-07-21 18:50:32 +00:00
try:
# Try start daemons
for c, v in settings['chainclients'].items():
2022-10-24 18:49:36 +00:00
try:
coin_id = swap_client.getCoinIdFromName(c)
display_name = getCoinName(coin_id)
except Exception as e:
logger.warning('Error getting coin display name for {}: {}'.format(c, str(e)))
display_name = 'Unknown'
if c == 'monero':
if v['manage_daemon'] is True:
2022-10-24 18:49:36 +00:00
swap_client.log.info(f'Starting {display_name} daemon')
2022-12-02 23:07:41 +00:00
filename = 'monerod' + ('.exe' if os.name == 'nt' else '')
daemons.append(startXmrDaemon(v['datadir'], v['bindir'], filename))
pid = daemons[-1].pid
2022-12-02 23:07:41 +00:00
swap_client.log.info('Started {} {}'.format(filename, pid))
if v['manage_wallet_daemon'] is True:
2022-10-24 18:49:36 +00:00
swap_client.log.info(f'Starting {display_name} wallet daemon')
daemon_addr = '{}:{}'.format(v['rpchost'], v['rpcport'])
swap_client.log.info('daemon-address: {}'.format(daemon_addr))
opts = ['--daemon-address', daemon_addr, ]
2022-11-28 17:54:41 +00:00
daemon_rpcuser = v.get('rpcuser', '')
daemon_rpcpass = v.get('rpcpassword', '')
if daemon_rpcuser != '':
opts.append('--daemon-login')
opts.append(daemon_rpcuser + ':' + daemon_rpcpass)
2022-12-02 23:07:41 +00:00
filename = 'monero-wallet-rpc' + ('.exe' if os.name == 'nt' else '')
daemons.append(startXmrWalletDaemon(v['datadir'], v['bindir'], filename, opts))
pid = daemons[-1].pid
2022-12-02 23:07:41 +00:00
swap_client.log.info('Started {} {}'.format(filename, pid))
continue
if v['manage_daemon'] is True:
2022-10-24 18:49:36 +00:00
swap_client.log.info(f'Starting {display_name} daemon')
filename = c + 'd' + ('.exe' if os.name == 'nt' else '')
daemons.append(startDaemon(v['datadir'], v['bindir'], filename))
pid = daemons[-1].pid
pids.append((c, pid))
swap_client.setDaemonPID(c, pid)
swap_client.log.info('Started {} {}'.format(filename, pid))
if len(pids) > 0:
with open(pids_path, 'w') as fd:
for p in pids:
fd.write('{}:{}\n'.format(*p))
2019-08-15 22:31:39 +00:00
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
swap_client.start()
if 'htmlhost' in settings:
2022-07-31 17:33:01 +00:00
swap_client.log.info('Starting http server at http://%s:%d.' % (settings['htmlhost'], settings['htmlport']))
allow_cors = settings['allowcors'] if 'allowcors' in settings else cfg.DEFAULT_ALLOW_CORS
2022-07-31 17:33:01 +00:00
thread_http = HttpThread(fp, settings['htmlhost'], settings['htmlport'], allow_cors, swap_client)
threads.append(thread_http)
thread_http.start()
if 'wshost' in settings:
ws_url = 'ws://{}:{}'.format(settings['wshost'], settings['wsport'])
swap_client.log.info(f'Starting ws server at {ws_url}.')
swap_client.ws_server = WebsocketServer(host=settings['wshost'], port=settings['wsport'])
swap_client.ws_server.set_fn_new_client(ws_new_client)
swap_client.ws_server.set_fn_client_left(ws_client_left)
swap_client.ws_server.set_fn_message_received(ws_message_received)
swap_client.ws_server.run_forever(threaded=True)
2019-07-21 18:50:32 +00:00
logger.info('Exit with Ctrl + c.')
while swap_client.is_running:
time.sleep(0.5)
swap_client.update()
2022-07-31 17:33:01 +00:00
except Exception as ex:
2019-07-21 18:50:32 +00:00
traceback.print_exc()
2022-07-31 17:33:01 +00:00
if swap_client.ws_server:
try:
swap_client.log.info('Stopping websocket server.')
swap_client.ws_server.shutdown_gracefully()
except Exception as ex:
traceback.print_exc()
swap_client.finalise()
swap_client.log.info('Stopping HTTP threads.')
2019-07-21 18:50:32 +00:00
for t in threads:
2022-07-31 17:33:01 +00:00
try:
t.stop()
t.join()
except Exception as ex:
traceback.print_exc()
2019-07-21 18:50:32 +00:00
2019-07-25 13:28:44 +00:00
closed_pids = []
2019-07-21 18:50:32 +00:00
for d in daemons:
swap_client.log.info('Interrupting {}'.format(d.pid))
2020-11-07 11:08:07 +00:00
try:
2022-12-02 23:07:41 +00:00
d.send_signal(signal.CTRL_C_EVENT if os.name == 'nt' else signal.SIGINT)
2020-11-07 11:08:07 +00:00
except Exception as e:
swap_client.log.info('Interrupting %d, error %s', d.pid, str(e))
2020-11-07 11:08:07 +00:00
for d in daemons:
2019-07-25 13:28:44 +00:00
try:
d.wait(timeout=120)
for fp in (d.stdout, d.stderr, d.stdin):
if fp:
fp.close()
closed_pids.append(d.pid)
2019-07-25 13:28:44 +00:00
except Exception as ex:
swap_client.log.error('Error: {}'.format(ex))
2019-07-25 13:28:44 +00:00
if os.path.exists(pids_path):
with open(pids_path) as fd:
lines = fd.read().split('\n')
still_running = ''
for ln in lines:
try:
if not int(ln.split(':')[1]) in closed_pids:
still_running += ln + '\n'
except Exception:
pass
with open(pids_path, 'w') as fd:
fd.write(still_running)
2019-07-21 18:50:32 +00:00
def printVersion():
2021-01-10 18:30:07 +00:00
logger.info('Basicswap version: %s', __version__)
2019-07-21 18:50:32 +00:00
def printHelp():
2019-07-27 17:26:06 +00:00
logger.info('Usage: basicswap-run ')
logger.info('\n--help, -h Print help.')
logger.info('--version, -v Print version.')
logger.info('--datadir=PATH Path to basicswap data directory, default:{}.'.format(cfg.BASICSWAP_DATADIR))
2019-07-27 17:26:06 +00:00
logger.info('--mainnet Run in mainnet mode.')
logger.info('--testnet Run in testnet mode.')
logger.info('--regtest Run in regtest mode.')
2019-07-21 18:50:32 +00:00
def main():
data_dir = None
chain = 'mainnet'
for v in sys.argv[1:]:
if len(v) < 2 or v[0] != '-':
2019-07-21 19:20:36 +00:00
logger.warning('Unknown argument %s', v)
2019-07-21 18:50:32 +00:00
continue
s = v.split('=')
name = s[0].strip()
for i in range(2):
if name[0] == '-':
name = name[1:]
if name == 'v' or name == 'version':
printVersion()
return 0
if name == 'h' or name == 'help':
printHelp()
return 0
2019-07-27 17:26:06 +00:00
2019-07-21 18:50:32 +00:00
if name == 'testnet':
chain = 'testnet'
continue
if name == 'regtest':
chain = 'regtest'
continue
if len(s) == 2:
if name == 'datadir':
data_dir = os.path.expanduser(s[1])
continue
2019-07-21 19:20:36 +00:00
logger.warning('Unknown argument %s', v)
2019-07-21 18:50:32 +00:00
if data_dir is None:
data_dir = os.path.join(os.path.expanduser(cfg.BASICSWAP_DATADIR))
2019-07-21 19:20:36 +00:00
logger.info('Using datadir: %s', data_dir)
logger.info('Chain: %s', chain)
2019-07-21 18:50:32 +00:00
if not os.path.exists(data_dir):
os.makedirs(data_dir)
with open(os.path.join(data_dir, 'basicswap.log'), 'a') as fp:
logger.info(os.path.basename(sys.argv[0]) + ', version: ' + __version__ + '\n\n')
2019-08-15 22:31:39 +00:00
runClient(fp, data_dir, chain)
2019-07-21 18:50:32 +00:00
logger.info('Done.')
return swap_client.fail_code if swap_client is not None else 0
if __name__ == '__main__':
main()