diff --git a/plugins/libplugin.c b/plugins/libplugin.c index d47f18ef7..b338a930d 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -421,11 +422,25 @@ send_outreq_(struct command *cmd, static struct command_result * handle_getmanifest(struct command *getmanifest_cmd, const struct plugin_command *commands, - size_t num_commands) + size_t num_commands, + const struct plugin_option *opts) { char *params = tal_strdup(getmanifest_cmd, - "'options': [],\n" - "'rpcmethods': [ "); + "'options': ["); + + for (size_t i = 0; i < tal_count(opts); i++) { + tal_append_fmt(¶ms, "{ 'name': '%s'," + " 'type': 'string'," + " 'description': '%s' }%s", + opts[i].name, + opts[i].description, + i == tal_count(opts) - 1 ? "" : ",\n"); + } + + tal_append_fmt(¶ms, + "],\n" + "'rpcmethods': [ "); + for (size_t i = 0; i < num_commands; i++) { tal_append_fmt(¶ms, "{ 'name': '%s'," " 'usage': '%s'," @@ -447,10 +462,12 @@ handle_getmanifest(struct command *getmanifest_cmd, static struct command_result *handle_init(struct command *init_cmd, const char *buf, const jsmntok_t *params, + const struct plugin_option *opts, void (*init)(struct plugin_conn *)) { - const jsmntok_t *rpctok, *dirtok; + const jsmntok_t *rpctok, *dirtok, *opttok, *t; struct sockaddr_un addr; + size_t i; char *dir; /* Move into lightning directory: other files are relative */ @@ -480,12 +497,49 @@ static struct command_result *handle_init(struct command *init_cmd, ".allow-deprecated-apis"), "true"); + opttok = json_get_member(buf, params, "options"); + json_for_each_obj(i, t, opttok) { + char *opt = json_strdup(NULL, buf, t); + for (size_t i = 0; i < tal_count(opts); i++) { + char *problem; + if (!streq(opts[i].name, opt)) + continue; + problem = opts[i].handle(json_strdup(opt, buf, t+1), + opts[i].arg); + if (problem) + plugin_err("option '%s': %s", + opts[i].name, problem); + break; + } + tal_free(opt); + } + if (init) init(init_cmd->rpc); return command_done_ok(init_cmd, ""); } +char *u64_option(const char *arg, u64 *i) +{ + char *endp; + + /* This is how the manpage says to do it. Yech. */ + errno = 0; + *i = strtol(arg, &endp, 0); + if (*endp || !arg[0]) + return tal_fmt(NULL, "'%s' is not a number", arg); + if (errno) + return tal_fmt(NULL, "'%s' is out of range", arg); + return NULL; +} + +char *charp_option(const char *arg, char **p) +{ + *p = tal_strdup(NULL, arg); + return NULL; +} + static void handle_new_command(const tal_t *ctx, struct plugin_conn *request_conn, struct plugin_conn *rpc_conn, @@ -529,7 +583,7 @@ static void setup_command_usage(const struct plugin_command *commands, void plugin_main(char *argv[], void (*init)(struct plugin_conn *rpc), const struct plugin_command *commands, - size_t num_commands) + size_t num_commands, ...) { struct plugin_conn request_conn, rpc_conn; const tal_t *ctx = tal(NULL, char); @@ -537,6 +591,9 @@ void plugin_main(char *argv[], const jsmntok_t *params; int reqlen; struct pollfd fds[2]; + struct plugin_option *opts = tal_arr(ctx, struct plugin_option, 0); + va_list ap; + const char *optname; setup_locale(); @@ -556,13 +613,24 @@ void plugin_main(char *argv[], membuf_tal_realloc); uintmap_init(&out_reqs); + va_start(ap, num_commands); + while ((optname = va_arg(ap, const char *)) != NULL) { + struct plugin_option o; + o.name = optname; + o.description = va_arg(ap, const char *); + o.handle = va_arg(ap, char *(*)(const char *str, void *arg)); + o.arg = va_arg(ap, void *); + tal_arr_expand(&opts, o); + } + va_end(ap); + cmd = read_json_request(tmpctx, &request_conn, NULL, ¶ms, &reqlen); if (!streq(cmd->methodname, "getmanifest")) plugin_err("Expected getmanifest not %s", cmd->methodname); membuf_consume(&request_conn.mb, reqlen); - handle_getmanifest(cmd, commands, num_commands); + handle_getmanifest(cmd, commands, num_commands, opts); cmd = read_json_request(tmpctx, &request_conn, &rpc_conn, ¶ms, &reqlen); @@ -570,7 +638,7 @@ void plugin_main(char *argv[], plugin_err("Expected init not %s", cmd->methodname); handle_init(cmd, membuf_elems(&request_conn.mb), - params, init); + params, opts, init); membuf_consume(&request_conn.mb, reqlen); /* Set up fds for poll. */ diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 036e18ca7..87623a901 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -24,6 +24,14 @@ struct plugin_command { const jsmntok_t *params); }; +/* Create an array of these, one for each --option you support. */ +struct plugin_option { + const char *name; + const char *description; + char *(*handle)(const char *str, void *arg); + void *arg; +}; + /* Return this iff the param() call failed in your handler. */ struct command_result *command_param_failed(void); @@ -88,9 +96,20 @@ struct command_result *forward_result(struct command *cmd, const jsmntok_t *result, void *arg); -/* The main plugin runner. */ -void NORETURN plugin_main(char *argv[], - void (*init)(struct plugin_conn *rpc), - const struct plugin_command *commands, - size_t num_commands); +/* Macro to define arguments */ +#define plugin_option(name, description, set, arg) \ + (name), \ + (description), \ + typesafe_cb_preargs(char *, void *, (set), (arg), const char *), \ + (arg) + +/* Standard helpers */ +char *u64_option(const char *arg, u64 *i); +char *charp_option(const char *arg, char **p); + +/* The main plugin runner: append with 0 or more plugin_option(), then NULL. */ +void NORETURN LAST_ARG_NULL plugin_main(char *argv[], + void (*init)(struct plugin_conn *rpc), + const struct plugin_command *commands, + size_t num_commands, ...); #endif /* LIGHTNING_PLUGINS_LIBPLUGIN_H */ diff --git a/plugins/pay.c b/plugins/pay.c index fbd3924d8..c80702d75 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1213,5 +1213,5 @@ static const struct plugin_command commands[] = { { int main(int argc, char *argv[]) { setup_locale(); - plugin_main(argv, init, commands, ARRAY_SIZE(commands)); + plugin_main(argv, init, commands, ARRAY_SIZE(commands), NULL); }