diff -uNr a/gbw-node/command/gbw-node b/gbw-node/command/gbw-node --- a/gbw-node/command/gbw-node 8265c25fc3dd65a66078c2bc3a8e7111afa94e76b679de94d245c9da1a2cb3484e72d3bcfbd2889e7fb06c9c99448175d31b841bed91f5b5c5755f7afbbdde17 +++ b/gbw-node/command/gbw-node f514fbccaed6c2dc6071b65e61e1836e7db159342ef92550e8f4132cce90787f75598212eaeedd80d707d9b9ea370055529a356b98ceaf8b9097ad0e21a113d9 @@ -86,19 +86,15 @@ try: mkdir(path) except OSError, e: - if e.errno != errno.EEXIST: + if not (e.errno == errno.EEXIST and S_ISDIR(stat(path).st_mode)): raise - if not S_ISDIR(stat(path).st_mode): - die('not a directory: %r' % path) def require_fifo(path): try: mkfifo(path) except OSError, e: - if e.errno != errno.EEXIST: + if not (e.errno == errno.EEXIST and S_ISFIFO(stat(path).st_mode)): raise - if not S_ISFIFO(stat(path).st_mode): - die('not a fifo: %r' % path) ################################################## # RPC client @@ -462,16 +458,13 @@ n_tx += 1 stdout.write(' spent-outs %s\n' % count_in) -def die(msg, help=False): +def log_err(msg): stderr.write('gbw-node: %s\n' % msg) - if help: - cmd_help([]) - exit(-1) def require_tag(name): i = get_tag_id(name) if i is None: - die('tag not found: %r' % name) + raise ValueError('tag not found: %r' % name) return i def cmd_scan(): @@ -515,7 +508,8 @@ if addr: addr_id = get_address_id(parse_address(addr)) if addr_id is None: - die('address not found: %r' % addr) + log_err('address not found: %r' % addr) + return -1 r = db.execute('SELECT name FROM tag \ JOIN address_tag ON tag.tag_id=address_tag.tag_id \ WHERE address_id=? ORDER BY name', (addr_id,)) @@ -660,13 +654,22 @@ tag_id = None if tag: if '\n' in tag: - die('newline not allowed in tag name') + log_err('newline not allowed in tag name') + return -1 tag_id = insert_or_get_tag_id(tag) + status = 0 while True: l = stdin.readline() if len(l) == 0: break - addr_id = insert_or_get_address_id(parse_address(l.rstrip('\n'))) + try: + addr = parse_address(l.rstrip('\n')) + except ValueError, e: + # Be forgiving of bad lines: show the error but continue processing + log_err(repr(e)) + status = -1 + continue + addr_id = insert_or_get_address_id(addr) if tag_id is not None: try: db.execute('INSERT INTO address_tag (address_id, tag_id) VALUES (?,?)', @@ -674,6 +677,7 @@ except IntegrityError: pass db.commit() + return status def cmd_push(): ''' @@ -681,12 +685,21 @@ Import raw hex transactions linewise from stdin (i.e. one rawtx per line of input) and send to bitcoind. ''' + status = 0 while True: line = stdin.readline() if len(line) == 0: break tx_hex = line.rstrip('\n') - stdout.write('txid %s\n' % rpc('sendrawtransaction', tx_hex)) + try: + r = rpc('sendrawtransaction', tx_hex) + except JSONRPCError, e: + # Be forgiving of bad lines: show the error but continue processing + log_err(repr(e)) + status = -1 + continue + stdout.write('txid %s\n' % r) + return status def cmd_unlock_wallet(timeout=60): ''' @@ -695,7 +708,11 @@ Read encryption passphrase from the terminal and unlock the bitcoind internal wallet for TIMEOUT seconds (default 60). ''' timeout = int(timeout) - r = rpc('walletpassphrase', getpass('Passphrase: '), timeout) + try: + r = rpc('walletpassphrase', getpass('Passphrase: '), timeout) + except JSONRPCError, e: + log_err(repr(e)) + return -1 if r is not None: stdout.write('%r\n' % r) else: @@ -736,12 +753,16 @@ ('unlock-wallet', cmd_unlock_wallet), ) +class UsageError(Exception): + pass + def get_command(name): rows = [r for r in cmds if r[0].startswith(name)] if len(rows) == 0: - die('command not found: %s' % name) + raise UsageError('command not found: %s' % name) if len(rows) > 1: - die('ambiguous command %s. Completions: %s' % (name, ' '.join([r[0] for r in rows]))) + raise UsageError('ambiguous command %s. Completions: %s' % + (name, ' '.join([r[0] for r in rows]))) return rows[0] def main(): @@ -752,9 +773,15 @@ db.execute('PRAGMA foreign_keys=ON') db.execute('PRAGMA cache_size=-8000') # negative means in KiB db.execute('PRAGMA wal_autocheckpoint=10000') # in pages (4k) - if len(argv) < 2: - die('missing command', help=True) - get_command(argv[1])[1](*argv[2:]) + try: + if len(argv) < 2: + raise UsageError('missing command') + return get_command(argv[1])[1](*argv[2:]) + except UsageError, e: + log_err(str(e)) + cmd_help() + return -1 if __name__ == '__main__': - main() + # main() can return None if the subcommand's function doesn't return an explicit status; this becomes 0 (success). + exit(main()) diff -uNr a/gbw-node/manifest b/gbw-node/manifest --- a/gbw-node/manifest dbfe25de2dd81d46762679b185ec93b7ef0f71c395089b3e6e5278c1c8af6482f79a35c8d9400bb471f8ef480294af82ec9003333b3ce95401cca4d692d131f0 +++ b/gbw-node/manifest 54264d11fdcea1b9d42ac2f5766bcca743b517d6028aefd4f56532b1311654b98bfc085b22fe3d0eac1a85ba3b04cd50191de3295d228f90d265b2e7282fb01c @@ -1,3 +1,4 @@ 711740 gbw-node_subdir_genesis jfw Online database frontend and schema for gbw, the Gales Bitcoin Wallet. Reissued to follow various conventions: top-level project subdir, lowercase manifest filename, README at project level. (File renaming only; pending content changes are to follow.) 711740 gbw-node_usrbin jfw As seen also with gscm, change command symlink from /command/gbw-node to /usr/bin/gbw-node, and correct the install script for the case of replacing an existing version. Update README and bump version to reflect the packaging changes. 783265 gbw-node_error_reporting jfw Fix undefined reference on reporting a bad address in cmd_tags. More generally, simplify arglist processing and fix the silent ignoring of excess CLI arguments, by having Python unpack the arglist automatically into named parameters of each command handler function. Report more context for address decoding errors. Clarify help text regarding input format for watch and push commands. +783269 gbw-node_error_tolerance jfw Don't die immediately on bad input lines.