diff --git a/plugins/autoclean.c b/plugins/autoclean.c index d51eaa3e9..128be76e3 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -8,7 +8,7 @@ static u64 cycle_seconds = 0, expired_by = 86400; static struct plugin_timer *cleantimer; -static struct plugin_conn *rpc; +static struct rpc_conn *rpc; static struct command_result *do_clean(void); @@ -65,10 +65,10 @@ static struct command_result *json_autocleaninvoice(struct command *cmd, expired_by, cycle_seconds)); } -static void init(struct plugin_conn *prpc, +static void init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { - rpc = prpc; + rpc = &p->rpc_conn; if (cycle_seconds) { plugin_log(LOG_INFORM, "autocleaning every %"PRIu64" seconds", cycle_seconds); diff --git a/plugins/fundchannel.c b/plugins/fundchannel.c index ea59e9033..e50ee3f8f 100644 --- a/plugins/fundchannel.c +++ b/plugins/fundchannel.c @@ -485,17 +485,17 @@ static struct command_result *json_fundchannel(struct command *cmd, return connect_to_peer(cmd, fr); } -static void init(struct plugin_conn *rpc, +static void init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { /* Figure out what the 'placeholder' addr is */ const char *network_name; u8 *placeholder = tal_hexdata(tmpctx, placeholder_script, strlen(placeholder_script)); - network_name = rpc_delve(tmpctx, "listconfigs", + network_name = rpc_delve(tmpctx, p, "listconfigs", take(json_out_obj(NULL, "config", "network")), - rpc, ".network"); + ".network"); chainparams = chainparams_for_network(network_name); placeholder_funding_addr = encode_scriptpubkey_to_addr(NULL, chainparams, placeholder); diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 4eaa8f8b0..3b2f5d008 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -39,56 +38,11 @@ bool deprecated_apis; extern const struct chainparams *chainparams; -struct plugin { - /* lightningd interaction */ - struct io_conn *stdin_conn; - struct io_conn *stdout_conn; - - /* To read from lightningd */ - char *buffer; - size_t used, len_read; - - /* To write to lightningd */ - struct json_stream **js_arr; - - enum plugin_restartability restartability; - const struct plugin_command *commands; - size_t num_commands; - const struct plugin_notification *notif_subs; - size_t num_notif_subs; - const struct plugin_hook *hook_subs; - size_t num_hook_subs; - struct plugin_option *opts; - - /* Anything special to do at init ? */ - void (*init)(struct plugin_conn *, - const char *buf, const jsmntok_t *); - /* Has the manifest been sent already ? */ - bool manifested; - /* Has init been received ? */ - bool initialized; -}; - struct plugin_timer { struct timer timer; struct command_result *(*cb)(void); }; -struct plugin_conn { - int fd; - MEMBUF(char) mb; -}; - -/* Connection to make RPC requests. */ -static struct plugin_conn rpc_conn; - -struct command { - u64 *id; - const char *methodname; - bool usage_only; - struct plugin *plugin; -}; - struct out_req { /* The unique id of this request. */ u64 id; @@ -109,7 +63,7 @@ struct out_req { /* command_result is mainly used as a compile-time check to encourage you * to return as soon as you get one (and not risk use-after-free of command). - * Here we use two values: complete (cmd freed) an pending (still going) */ + * Here we use two values: complete (cmd freed) and pending (still going) */ struct command_result { char c; }; @@ -177,30 +131,30 @@ static void *membuf_tal_realloc(struct membuf *mb, void *rawelems, return p; } -static int read_json(struct plugin_conn *conn) +static int read_json_from_rpc(struct plugin *p) { char *end; /* We rely on the double-\n marker which only terminates JSON top * levels. Thanks lightningd! */ - while ((end = memmem(membuf_elems(&conn->mb), - membuf_num_elems(&conn->mb), "\n\n", 2)) + while ((end = memmem(membuf_elems(&p->rpc_conn.mb), + membuf_num_elems(&p->rpc_conn.mb), "\n\n", 2)) == NULL) { ssize_t r; /* Make sure we've room for at least READ_CHUNKSIZE. */ - membuf_prepare_space(&conn->mb, READ_CHUNKSIZE); - r = read(conn->fd, membuf_space(&conn->mb), - membuf_num_space(&conn->mb)); + membuf_prepare_space(&p->rpc_conn.mb, READ_CHUNKSIZE); + r = read(p->rpc_conn.fd, membuf_space(&p->rpc_conn.mb), + membuf_num_space(&p->rpc_conn.mb)); /* lightningd goes away, we go away. */ if (r == 0) exit(0); if (r < 0) plugin_err("Reading JSON input: %s", strerror(errno)); - membuf_added(&conn->mb, r); + membuf_added(&p->rpc_conn.mb, r); } - return end + 2 - membuf_elems(&conn->mb); + return end + 2 - membuf_elems(&p->rpc_conn.mb); } /* This starts a JSON RPC message with boilerplate */ @@ -355,7 +309,7 @@ void command_set_usage(struct command *cmd, const char *usage TAKES) /* Reads rpc reply and returns tokens, setting contents to 'error' or * 'result' (depending on *error). */ static const jsmntok_t *read_rpc_reply(const tal_t *ctx, - struct plugin_conn *rpc, + struct plugin *plugin, const jsmntok_t **contents, bool *error, int *reqlen) @@ -363,22 +317,22 @@ static const jsmntok_t *read_rpc_reply(const tal_t *ctx, const jsmntok_t *toks; bool valid; - *reqlen = read_json(rpc); + *reqlen = read_json_from_rpc(plugin); - toks = json_parse_input(ctx, membuf_elems(&rpc->mb), *reqlen, &valid); + toks = json_parse_input(ctx, membuf_elems(&plugin->rpc_conn.mb), *reqlen, &valid); if (!valid) plugin_err("Malformed JSON reply '%.*s'", - *reqlen, membuf_elems(&rpc->mb)); + *reqlen, membuf_elems(&plugin->rpc_conn.mb)); - *contents = json_get_member(membuf_elems(&rpc->mb), toks, "error"); + *contents = json_get_member(membuf_elems(&plugin->rpc_conn.mb), toks, "error"); if (*contents) *error = true; else { - *contents = json_get_member(membuf_elems(&rpc->mb), toks, + *contents = json_get_member(membuf_elems(&plugin->rpc_conn.mb), toks, "result"); if (!*contents) plugin_err("JSON reply with no 'result' nor 'error'? '%.*s'", - *reqlen, membuf_elems(&rpc->mb)); + *reqlen, membuf_elems(&plugin->rpc_conn.mb)); *error = false; } return toks; @@ -402,9 +356,10 @@ static struct json_out *start_json_request(const tal_t *ctx, /* Synchronous routine to send command and extract single field from response */ const char *rpc_delve(const tal_t *ctx, + struct plugin *plugin, const char *method, const struct json_out *params TAKES, - struct plugin_conn *rpc, const char *guide) + const char *guide) { bool error; const jsmntok_t *contents, *t; @@ -413,24 +368,24 @@ const char *rpc_delve(const tal_t *ctx, struct json_out *jout; jout = start_json_request(tmpctx, 0, method, params); - finish_and_send_json(rpc->fd, jout); + finish_and_send_json(plugin->rpc_conn.fd, jout); - read_rpc_reply(tmpctx, rpc, &contents, &error, &reqlen); + read_rpc_reply(tmpctx, plugin, &contents, &error, &reqlen); if (error) plugin_err("Got error reply to %s: '%.*s'", - method, reqlen, membuf_elems(&rpc->mb)); + method, reqlen, membuf_elems(&plugin->rpc_conn.mb)); - t = json_delve(membuf_elems(&rpc->mb), contents, guide); + t = json_delve(membuf_elems(&plugin->rpc_conn.mb), contents, guide); if (!t) plugin_err("Could not find %s in reply to %s: '%.*s'", - guide, method, reqlen, membuf_elems(&rpc->mb)); + guide, method, reqlen, membuf_elems(&plugin->rpc_conn.mb)); - ret = json_strdup(ctx, membuf_elems(&rpc->mb), t); - membuf_consume(&rpc->mb, reqlen); + ret = json_strdup(ctx, membuf_elems(&plugin->rpc_conn.mb), t); + membuf_consume(&plugin->rpc_conn.mb, reqlen); return ret; } -static void handle_rpc_reply(struct plugin_conn *rpc) +static void handle_rpc_reply(struct plugin *plugin) { int reqlen; const jsmntok_t *toks, *contents, *t; @@ -439,33 +394,33 @@ static void handle_rpc_reply(struct plugin_conn *rpc) u64 id; bool error; - toks = read_rpc_reply(tmpctx, rpc, &contents, &error, &reqlen); + toks = read_rpc_reply(tmpctx, plugin, &contents, &error, &reqlen); - t = json_get_member(membuf_elems(&rpc->mb), toks, "id"); + t = json_get_member(membuf_elems(&plugin->rpc_conn.mb), toks, "id"); if (!t) plugin_err("JSON reply without id '%.*s'", - reqlen, membuf_elems(&rpc->mb)); - if (!json_to_u64(membuf_elems(&rpc->mb), t, &id)) + reqlen, membuf_elems(&plugin->rpc_conn.mb)); + if (!json_to_u64(membuf_elems(&plugin->rpc_conn.mb), t, &id)) plugin_err("JSON reply without numeric id '%.*s'", - reqlen, membuf_elems(&rpc->mb)); + reqlen, membuf_elems(&plugin->rpc_conn.mb)); out = uintmap_get(&out_reqs, id); if (!out) plugin_err("JSON reply with unknown id '%.*s' (%"PRIu64")", - reqlen, membuf_elems(&rpc->mb), id); + reqlen, membuf_elems(&plugin->rpc_conn.mb), id); /* We want to free this if callback doesn't. */ tal_steal(tmpctx, out); uintmap_del(&out_reqs, out->id); if (error) - res = out->errcb(out->cmd, membuf_elems(&rpc->mb), contents, - out->arg); + res = out->errcb(out->cmd, membuf_elems(&plugin->rpc_conn.mb), + contents, out->arg); else - res = out->cb(out->cmd, membuf_elems(&rpc->mb), contents, - out->arg); + res = out->cb(out->cmd, membuf_elems(&plugin->rpc_conn.mb), + contents, out->arg); assert(res == &pending || res == &complete); - membuf_consume(&rpc->mb, reqlen); + membuf_consume(&plugin->rpc_conn.mb, reqlen); } struct command_result * @@ -494,7 +449,7 @@ send_outreq_(struct command *cmd, uintmap_add(&out_reqs, out->id, out); jout = start_json_request(tmpctx, out->id, method, params); - finish_and_send_json(rpc_conn.fd, jout); + finish_and_send_json(cmd->plugin->rpc_conn.fd, jout); return &pending; } @@ -572,7 +527,7 @@ static struct command_result *handle_init(struct command *cmd, chainparams = chainparams_for_network(network); rpctok = json_delve(buf, configtok, ".rpc-file"); - rpc_conn.fd = socket(AF_UNIX, SOCK_STREAM, 0); + p->rpc_conn.fd = socket(AF_UNIX, SOCK_STREAM, 0); if (rpctok->end - rpctok->start + 1 > sizeof(addr.sun_path)) plugin_err("rpc filename '%.*s' too long", rpctok->end - rpctok->start, @@ -581,15 +536,14 @@ static struct command_result *handle_init(struct command *cmd, addr.sun_path[rpctok->end - rpctok->start] = '\0'; addr.sun_family = AF_UNIX; - if (connect(rpc_conn.fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) + if (connect(p->rpc_conn.fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) plugin_err("Connecting to '%.*s': %s", rpctok->end - rpctok->start, buf + rpctok->start, strerror(errno)); param_obj = json_out_obj(NULL, "config", "allow-deprecated-apis"); - deprecated_apis = streq(rpc_delve(tmpctx, "listconfigs", + deprecated_apis = streq(rpc_delve(tmpctx, p, "listconfigs", take(param_obj), - &rpc_conn, ".allow-deprecated-apis"), "true"); opttok = json_get_member(buf, params, "options"); @@ -610,7 +564,7 @@ static struct command_result *handle_init(struct command *cmd, } if (p->init) - p->init(&rpc_conn, buf, configtok); + p->init(p, buf, configtok); return command_success_str(cmd, NULL); } @@ -652,7 +606,7 @@ static void setup_command_usage(const struct plugin_command *commands, } } -static void call_plugin_timer(struct plugin_conn *rpc, struct timer *timer) +static void call_plugin_timer(struct rpc_conn *rpc, struct timer *timer) { struct plugin_timer *t = container_of(timer, struct plugin_timer, timer); @@ -667,7 +621,7 @@ static void destroy_plugin_timer(struct plugin_timer *timer) timer_del(&timers, &timer->timer); } -struct plugin_timer *plugin_timer(struct plugin_conn *rpc, struct timerel t, +struct plugin_timer *plugin_timer(struct rpc_conn *rpc, struct timerel t, struct command_result *(*cb)(void)) { struct plugin_timer *timer = tal(NULL, struct plugin_timer); @@ -927,7 +881,7 @@ static struct io_plan *stdout_conn_init(struct io_conn *conn, } static struct plugin *new_plugin(const tal_t *ctx, - void (*init)(struct plugin_conn *rpc, + void (*init)(struct plugin *p, const char *buf, const jsmntok_t *), const enum plugin_restartability restartability, const struct plugin_command *commands, @@ -945,6 +899,10 @@ static struct plugin *new_plugin(const tal_t *ctx, p->js_arr = tal_arr(p, struct json_stream *, 0); p->used = 0; p->len_read = 0; + /* rpc. TODO: use ccan/io also for RPC */ + membuf_init(&p->rpc_conn.mb, + tal_arr(p, char, READ_CHUNKSIZE), READ_CHUNKSIZE, + membuf_tal_realloc); p->init = init; p->manifested = p->initialized = false; @@ -972,7 +930,7 @@ static struct plugin *new_plugin(const tal_t *ctx, } void plugin_main(char *argv[], - void (*init)(struct plugin_conn *rpc, + void (*init)(struct plugin *p, const char *buf, const jsmntok_t *), const enum plugin_restartability restartability, const struct plugin_command *commands, @@ -1000,12 +958,9 @@ void plugin_main(char *argv[], notif_subs, num_notif_subs, hook_subs, num_hook_subs, ap); va_end(ap); + uintmap_init(&out_reqs); timers_init(&timers, time_mono()); - membuf_init(&rpc_conn.mb, - tal_arr(plugin, char, READ_CHUNKSIZE), READ_CHUNKSIZE, - membuf_tal_realloc); - uintmap_init(&out_reqs); io_new_conn(plugin, STDIN_FILENO, stdin_conn_init, plugin); io_new_conn(plugin, STDOUT_FILENO, stdout_conn_init, plugin); @@ -1015,14 +970,14 @@ void plugin_main(char *argv[], clean_tmpctx(); - if (membuf_num_elems(&rpc_conn.mb) != 0) { - handle_rpc_reply(&rpc_conn); + if (membuf_num_elems(&plugin->rpc_conn.mb) != 0) { + handle_rpc_reply(plugin); continue; } /* Will only exit if a timer has expired. */ io_loop(&timers, &expired); - call_plugin_timer(&rpc_conn, expired); + call_plugin_timer(&plugin->rpc_conn, expired); } tal_free(plugin); diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 63c9a7a36..2839b1115 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -3,6 +3,7 @@ #define LIGHTNING_PLUGINS_LIBPLUGIN_H #include "config.h" +#include #include #include #include @@ -12,17 +13,59 @@ #include #include -struct command; struct json_out; -struct plugin_conn; - -extern bool deprecated_apis; enum plugin_restartability { PLUGIN_STATIC, PLUGIN_RESTARTABLE }; +struct rpc_conn { + int fd; + MEMBUF(char) mb; +}; + +struct plugin { + /* lightningd interaction */ + struct io_conn *stdin_conn; + struct io_conn *stdout_conn; + + /* To read from lightningd */ + char *buffer; + size_t used, len_read; + + /* To write to lightningd */ + struct json_stream **js_arr; + + struct rpc_conn rpc_conn; + + enum plugin_restartability restartability; + const struct plugin_command *commands; + size_t num_commands; + const struct plugin_notification *notif_subs; + size_t num_notif_subs; + const struct plugin_hook *hook_subs; + size_t num_hook_subs; + struct plugin_option *opts; + + /* Anything special to do at init ? */ + void (*init)(struct plugin *p, + const char *buf, const jsmntok_t *); + /* Has the manifest been sent already ? */ + bool manifested; + /* Has init been received ? */ + bool initialized; +}; + +struct command { + u64 *id; + const char *methodname; + bool usage_only; + struct plugin *plugin; +}; + +extern bool deprecated_apis; + /* Create an array of these, one for each command you support. */ struct plugin_command { const char *name; @@ -96,9 +139,10 @@ command_success_str(struct command *cmd, const char *str); /* Synchronous helper to send command and extract single field from * response; can only be used in init callback. */ const char *rpc_delve(const tal_t *ctx, + struct plugin *plugin, const char *method, const struct json_out *params TAKES, - struct plugin_conn *rpc, const char *guide); + const char *guide); /* Async rpc request. * @cmd can be NULL if we're coming from a timer callback. @@ -154,7 +198,7 @@ struct command_result *timer_complete(void); * Freeing this releases the timer, otherwise it's freed after @cb * if it hasn't been freed already. */ -struct plugin_timer *plugin_timer(struct plugin_conn *rpc, +struct plugin_timer *plugin_timer(struct rpc_conn *rpc, struct timerel t, struct command_result *(*cb)(void)); @@ -175,7 +219,7 @@ 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, + void (*init)(struct plugin *p, const char *buf, const jsmntok_t *), const enum plugin_restartability restartability, const struct plugin_command *commands, diff --git a/plugins/pay.c b/plugins/pay.c index 97f3a4088..b84fbb940 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1666,20 +1666,20 @@ static struct command_result *json_listpays(struct command *cmd, take(json_out_obj(NULL, "bolt11", b11str))); } -static void init(struct plugin_conn *rpc, +static void init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { const char *field; - field = rpc_delve(tmpctx, "getinfo", - take(json_out_obj(NULL, NULL, NULL)), rpc, ".id"); + field = rpc_delve(tmpctx, p, "getinfo", + take(json_out_obj(NULL, NULL, NULL)), ".id"); if (!node_id_from_hexstr(field, strlen(field), &my_id)) plugin_err("getinfo didn't contain valid id: '%s'", field); - field = rpc_delve(tmpctx, "listconfigs", + field = rpc_delve(tmpctx, p, "listconfigs", take(json_out_obj(NULL, "config", "max-locktime-blocks")), - rpc, ".max-locktime-blocks"); + ".max-locktime-blocks"); maxdelay_default = atoi(field); }