diff --git a/common/json_param.c b/common/json_param.c index dc302b00d..f2f853585 100644 --- a/common/json_param.c +++ b/common/json_param.c @@ -346,15 +346,15 @@ const char *param_subcommand(struct command *cmd, const char *buffer, return NULL; } -bool param(struct command *cmd, const char *buffer, - const jsmntok_t tokens[], ...) +static bool param_core(struct command *cmd, + const char *buffer, + const jsmntok_t tokens[], + va_list ap) { struct param *params = tal_arr(tmpctx, struct param, 0); const char *name; - va_list ap; bool allow_extra = false; - va_start(ap, tokens); while ((name = va_arg(ap, const char *)) != NULL) { enum param_style style = va_arg(ap, enum param_style); param_cbx cbx = va_arg(ap, param_cbx); @@ -365,7 +365,6 @@ bool param(struct command *cmd, const char *buffer, } param_add(¶ms, name, style, cbx, arg); } - va_end(ap); if (command_usage_only(cmd)) { check_params(params); @@ -373,10 +372,38 @@ bool param(struct command *cmd, const char *buffer, return false; } - /* Always return false if we're simply checking command parameters; - * normally this returns true if all parameters are valid. */ - return param_arr(cmd, buffer, tokens, params, allow_extra) == NULL - && !command_check_only(cmd); + return param_arr(cmd, buffer, tokens, params, allow_extra) == NULL; +} + +bool param(struct command *cmd, + const char *buffer, + const jsmntok_t tokens[], ...) +{ + bool ret; + va_list ap; + + va_start(ap, tokens); + ret = param_core(cmd, buffer, tokens, ap); + va_end(ap); + + /* Always fail if we're just checking! */ + if (ret && command_check_only(cmd)) + ret = false; + return ret; +} + +bool param_check(struct command *cmd, + const char *buffer, + const jsmntok_t tokens[], ...) +{ + bool ret; + va_list ap; + + va_start(ap, tokens); + ret = param_core(cmd, buffer, tokens, ap); + va_end(ap); + + return ret; } struct command_result *param_array(struct command *cmd, const char *name, diff --git a/common/json_param.h b/common/json_param.h index fa37dfd03..70262010a 100644 --- a/common/json_param.h +++ b/common/json_param.h @@ -43,12 +43,20 @@ struct command; struct command_result; /* - * Parse the json tokens. @params can be an array of values or an object - * of named values. + * All-in-one: parse the json tokens. @params can be an array of + * values or an object of named values. */ bool param(struct command *cmd, const char *buffer, const jsmntok_t params[], ...) LAST_ARG_NULL; +/* + * Version which *doesn't* fail if command_check_only(cmd) is true: + * allows you can do extra checks after, but MUST still fail with + * command_param_failed(); if command_check_only(cmd) is true! */ +bool param_check(struct command *cmd, + const char *buffer, + const jsmntok_t tokens[], ...) LAST_ARG_NULL; + /* * The callback signature. * diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index e06e6360c..19fbc84e9 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -48,6 +48,14 @@ struct command_result *command_param_failed(void) return ¶m_failed; } +/* For our purposes, the same as command_param_failed: we examine + * cmd->mode to see if it's really done. */ +struct command_result *command_check_done(struct command *cmd) +{ + assert(cmd->mode == CMD_CHECK); + return ¶m_failed; +} + struct command_result *command_its_complicated(const char *relationship_details UNNEEDED) { @@ -459,6 +467,12 @@ struct command_result *command_raw_complete(struct command *cmd, if (cmd->jcon) tal_steal(cmd->jcon, result); + /* Don't free it here if we're doing `check` */ + if (command_check_only(cmd)) { + cmd->mode = CMD_CHECK_FAILED; + return command_param_failed(); + } + tal_free(cmd); return &complete; } @@ -1307,7 +1321,7 @@ void command_set_usage(struct command *cmd, const char *usage TAKES) bool command_check_only(const struct command *cmd) { - return cmd->mode == CMD_CHECK; + return cmd->mode == CMD_CHECK || cmd->mode == CMD_CHECK_FAILED; } void jsonrpc_listen(struct jsonrpc *jsonrpc, struct lightningd *ld) @@ -1479,12 +1493,6 @@ void jsonrpc_request_end(struct jsonrpc_request *r) json_stream_append(r->stream, "\n\n", strlen("\n\n")); } -/* We add this destructor as a canary to detect cmd failing. */ -static void destroy_command_canary(struct command *cmd, bool *failed) -{ - *failed = true; -} - static struct command_result *json_check(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, @@ -1492,7 +1500,6 @@ static struct command_result *json_check(struct command *cmd, { jsmntok_t *mod_params; const jsmntok_t *name_tok; - bool failed; struct json_stream *response; struct command_result *res; @@ -1515,19 +1522,21 @@ static struct command_result *json_check(struct command *cmd, json_tok_remove(&mod_params, mod_params, name_tok, 1); cmd->mode = CMD_CHECK; - failed = false; - tal_add_destructor2(cmd, destroy_command_canary, &failed); res = cmd->json_cmd->dispatch(cmd, buffer, mod_params, mod_params); /* CMD_CHECK always makes it "fail" parameter parsing. */ assert(res == ¶m_failed); - - if (failed) + if (cmd->mode == CMD_CHECK_FAILED) { + tal_free(cmd); return res; + } response = json_stream_success(cmd); json_add_string(response, "command_to_check", cmd->json_cmd->name); - return command_success(cmd, response); + res = command_success(cmd, response); + /* CMD_CHECK means we don't get freed! */ + tal_free(cmd); + return res; } static const struct json_command check_command = { diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 15991fee5..d99bfe756 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -15,7 +15,9 @@ enum command_mode { /* Create command usage string, nothing else. */ CMD_USAGE, /* Check parameters, nothing else. */ - CMD_CHECK + CMD_CHECK, + /* Check parameters, and one failed. */ + CMD_CHECK_FAILED, }; /* Context for a command (from JSON, but might outlive the connection!). */ @@ -149,6 +151,11 @@ struct logger *command_log(struct command *cmd); extern struct command_result *command_param_failed(void) WARN_UNUSED_RESULT; +/* To return after param_check() succeeds but we're still + * command_check_only(cmd). */ +struct command_result *command_check_done(struct command *cmd) + WARN_UNUSED_RESULT; + /* Wrapper for pending commands (ignores return) */ static inline void was_pending(const struct command_result *res) {