From 4ca2964d2154d139f51880cd43145715178f5c6a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Mar 2019 13:10:32 +1030 Subject: [PATCH] plugin_hook: special case for db hook. I started by trying to change the current infrastructure, but this is really the only completely sync hook which makes sense; it needs to avoid doing the db_transation, as well as waiting, and using a callback is just overkill. So with some regret, I open coded it. Signed-off-by: Rusty Russell --- lightningd/plugin_hook.c | 72 ++++++++++++++++++++++++++++++++++++++++ lightningd/plugin_hook.h | 4 +++ 2 files changed, 76 insertions(+) diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index 0a5975386..d762139d3 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -89,3 +90,74 @@ void plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook, hook->response_cb(cb_arg, NULL); } } + +/* We open-code this, because it's just different and special enough to be + * annoying, and to make it clear that it's totally synchronous. */ + +/* Special synchronous hook for db */ +static struct plugin_hook db_write_hook = { "db_write", NULL, NULL, NULL, NULL }; +AUTODATA(hooks, &db_write_hook); + +static void db_hook_response(const char *buffer, const jsmntok_t *toks, + const jsmntok_t *idtok, + struct plugin_hook_request *ph_req) +{ + const jsmntok_t *resulttok; + bool resp; + + resulttok = json_get_member(buffer, toks, "result"); + if (!resulttok) + fatal("Plugin returned an invalid response to the db_write " + "hook: %s", buffer); + + /* We expect result: True. Anything else we abort. */ + if (!json_to_bool(buffer, resulttok, &resp)) + fatal("Plugin returned an invalid result to the db_write " + "hook: %s", buffer); + + /* If it fails, we must not commit to our db. */ + if (!resp) + fatal("Plugin returned failed db_write: %s.", buffer); + + /* We're done, exit exclusive loop. */ + io_break(ph_req); +} + +void plugin_hook_db_sync(struct db *db, const char **changes, const char *final) +{ + const struct plugin_hook *hook = &db_write_hook; + struct jsonrpc_request *req; + struct plugin_hook_request *ph_req; + void *ret; + + if (!hook->plugin) + return; + + ph_req = notleak(tal(hook->plugin, struct plugin_hook_request)); + /* FIXME: do IO logging for this! */ + req = jsonrpc_request_start(NULL, hook->name, NULL, db_hook_response, + ph_req); + + ph_req->hook = hook; + ph_req->db = db; + + json_array_start(req->stream, "writes"); + for (size_t i = 0; i < tal_count(changes); i++) + json_add_string(req->stream, NULL, changes[i]); + if (final) + json_add_string(req->stream, NULL, final); + json_array_end(req->stream); + jsonrpc_request_end(req); + + plugin_request_send(hook->plugin, req); + + /* We can be called on way out of an io_loop, which is already breaking. + * That will make this immediately return; save the break value and call + * again, then hand it onwards. */ + ret = plugin_exclusive_loop(hook->plugin); + if (ret != ph_req) { + void *ret2 = plugin_exclusive_loop(hook->plugin); + assert(ret2 == ph_req); + io_break(ret); + } +} diff --git a/lightningd/plugin_hook.h b/lightningd/plugin_hook.h index dcbf53292..059299f16 100644 --- a/lightningd/plugin_hook.h +++ b/lightningd/plugin_hook.h @@ -112,4 +112,8 @@ void plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook, bool plugin_hook_register(struct plugin *plugin, const char *method); +/* Special sync plugin hook for db: changes[] are SQL statements, with optional + * final command appended. */ +void plugin_hook_db_sync(struct db *db, const char **changes, const char *final); + #endif /* LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H */