diff --git a/cli/lightning-cli.c b/cli/lightning-cli.c index 517283584..83d35422a 100644 --- a/cli/lightning-cli.c +++ b/cli/lightning-cli.c @@ -601,6 +601,18 @@ static void opt_show_level(char buf[OPT_SHOW_LEN], const enum log_level *level) strncpy(buf, log_level_name(*level), OPT_SHOW_LEN-1); } +/* The standard opt_log_stderr_exit exits with status 1 */ +static void opt_log_stderr_exit_usage(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(ERROR_USAGE); +} + int main(int argc, char *argv[]) { setup_locale(); @@ -656,8 +668,8 @@ int main(int argc, char *argv[]) opt_register_version(); - opt_early_parse(argc, argv, opt_log_stderr_exit); - opt_parse(&argc, argv, opt_log_stderr_exit); + opt_early_parse(argc, argv, opt_log_stderr_exit_usage); + opt_parse(&argc, argv, opt_log_stderr_exit_usage); method = argv[1]; if (!method) { @@ -872,7 +884,7 @@ int main(int argc, char *argv[]) } tal_free(ctx); opt_free_table(); - return 0; + return NO_ERROR; } if (format == RAW) @@ -884,5 +896,5 @@ int main(int argc, char *argv[]) } tal_free(ctx); opt_free_table(); - return 1; + return ERROR_FROM_LIGHTNINGD; } diff --git a/doc/lightning-cli.1.md b/doc/lightning-cli.1.md index 29d2845c4..9a41cf32c 100644 --- a/doc/lightning-cli.1.md +++ b/doc/lightning-cli.1.md @@ -130,6 +130,15 @@ BUGS This manpage documents how it should work, not how it does work. The pretty printing of results isn't pretty. +EXIT STATUS +----------- + +If the command succeeds, the exit status is 0. Otherwise: + +* `1`: lightningd(7) returned an error reply (which is printed). +* `2`: we could not talk to lightningd. +* `3`: usage error, such as bad arguments or malformed JSON in the parameters. + AUTHOR ------ diff --git a/tests/test_misc.py b/tests/test_misc.py index 13316dd32..d7ecdca98 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -937,6 +937,39 @@ def test_cli(node_factory): j, _ = json.JSONDecoder().raw_decode(out) assert j == {'help': [{'command': 'help [command]'}]} + # lightningd errors should exit with status 1. + ret = subprocess.run(['cli/lightning-cli', + '--network={}'.format(TEST_NETWORK), + '--lightning-dir={}' + .format(l1.daemon.lightning_dir), + 'unknown-command']) + assert ret.returncode == 1 + + # Can't contact will exit with status code 2. + ret = subprocess.run(['cli/lightning-cli', + '--network={}'.format(TEST_NETWORK), + '--lightning-dir=xxx', + 'help']) + assert ret.returncode == 2 + + # Malformed parameter (invalid json) will exit with status code 3. + ret = subprocess.run(['cli/lightning-cli', + '--network={}'.format(TEST_NETWORK), + '--lightning-dir={}' + .format(l1.daemon.lightning_dir), + 'listpeers', + '[xxx]']) + assert ret.returncode == 3 + + # Bad usage should exit with status 3. + ret = subprocess.run(['cli/lightning-cli', + '--bad-param', + '--network={}'.format(TEST_NETWORK), + '--lightning-dir={}' + .format(l1.daemon.lightning_dir), + 'help']) + assert ret.returncode == 3 + # Test missing parameters. try: # This will error due to missing parameters.