mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
plugin: Add hook registration
I might have gone a bit overboard with the type-checking, but typesafe_cb_cast is quite nice to use, so why not. The macro to register a new hook encapsulates the entire flow from param serialization, to dispatch, parsing and callback dispatch in one bundle. I was tempted to have the callback outside of the registration, but it's unlikely that we'll have two calls to the same hook with different callbacks. Signed-off-by: Christian Decker <decker.christian@gmail.com>
This commit is contained in:
parent
025fbbc9ea
commit
f8f76e3d31
@ -84,6 +84,7 @@ LIGHTNINGD_SRC := \
|
||||
lightningd/peer_htlcs.c \
|
||||
lightningd/ping.c \
|
||||
lightningd/plugin.c \
|
||||
lightningd/plugin_hook.c \
|
||||
lightningd/subd.c \
|
||||
lightningd/watch.c
|
||||
|
||||
|
@ -13,6 +13,11 @@
|
||||
*/
|
||||
struct plugins;
|
||||
|
||||
/**
|
||||
* A plugin, exposed as a stub so we can pass it as an argument.
|
||||
*/
|
||||
struct plugin;
|
||||
|
||||
/**
|
||||
* Create a new plugins context.
|
||||
*/
|
||||
|
6
lightningd/plugin_hook.c
Normal file
6
lightningd/plugin_hook.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <lightningd/plugin_hook.h>
|
||||
|
||||
void plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook,
|
||||
void *payload, void *cb_arg)
|
||||
{
|
||||
}
|
115
lightningd/plugin_hook.h
Normal file
115
lightningd/plugin_hook.h
Normal file
@ -0,0 +1,115 @@
|
||||
#ifndef LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H
|
||||
#define LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H
|
||||
|
||||
#include "config.h"
|
||||
#include <ccan/autodata/autodata.h>
|
||||
#include <ccan/tal/tal.h>
|
||||
#include <ccan/typesafe_cb/typesafe_cb.h>
|
||||
#include <lightningd/json_stream.h>
|
||||
#include <lightningd/lightningd.h>
|
||||
#include <lightningd/plugin.h>
|
||||
|
||||
/**
|
||||
* Plugin hooks are a way for plugins to implement custom behavior and
|
||||
* reactions to certain things in `lightningd`. `lightningd` will ask
|
||||
* plugins that have registered a hook for a given event how it'd like
|
||||
* to proceed. This allows plugins to deviate from the default
|
||||
* behavior that `lightningd` otherwise implements.
|
||||
*
|
||||
* Examples include storing an additional backup of important
|
||||
* information belonging to the wallet before committing to it, or
|
||||
* holding an incoming payment that is guaranteed to succeed for some
|
||||
* time in order to check that the delivery of goods works correctly,
|
||||
* giving the option of instantly refunding should something go wrong.
|
||||
*
|
||||
* Hooks are commonly structured into a number of converter functions
|
||||
* and a callback. The converter functions convert from an internal
|
||||
* struct representation of the method arguments to a JSON-object for
|
||||
* delivery to the plugin, and from a JSON-object to the internal
|
||||
* representation:
|
||||
*
|
||||
* - `serialize_payload` which takes a payload of type `payload_type`
|
||||
* and serializes it into the given `json_stream`. `
|
||||
*
|
||||
* - `deserialize_response` takes a `json_stream` and parses it into a
|
||||
* new struct of type `response_type`,
|
||||
*
|
||||
* - `response_cb` is called once the plugin has responded and the
|
||||
* response has been parsed by `deserialize_response`. In addition
|
||||
* an arbitrary additional argument of type `cb_arg_type` can be
|
||||
* passed along that may contain any additional context necessary.
|
||||
*
|
||||
*
|
||||
* To make hook invocations easier, each hook registered with
|
||||
* `REGISTER_PLUGIN_HOOK` provides a `plugin_hook_call_hookname`
|
||||
* function that performs typechecking at compile time, and makes sure
|
||||
* that all the provided functions for serialization, deserialization
|
||||
* and callback have the correct type.
|
||||
*/
|
||||
|
||||
struct plugin_hook {
|
||||
const char *name;
|
||||
void (*response_cb)(void *arg, void *response);
|
||||
void (*serialize_payload)(void *src, struct json_stream *dest);
|
||||
void *(*deserialize_response)(const tal_t *, const char *buffer,
|
||||
const jsmntok_t *toks);
|
||||
|
||||
/* Which plugin has registered this hook? */
|
||||
struct plugin *plugin;
|
||||
};
|
||||
AUTODATA_TYPE(hooks, struct plugin_hook);
|
||||
|
||||
/* Do not call this directly, rather use the `plugin_hook_call_name`
|
||||
* wrappers generated by the `PLUGIN_HOOK_REGISTER` macro.
|
||||
*/
|
||||
void plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook,
|
||||
void *payload, void *cb_arg);
|
||||
|
||||
|
||||
/* Create a small facade in from of `plugin_hook_call_` to make sure
|
||||
* arguments are of the correct type before downcasting them to `void
|
||||
* *`. Not really necessary, but nice since it also makes sure that
|
||||
* the method-name is correct for the call.
|
||||
*/
|
||||
/* FIXME: Find a way to avoid back-to-back declaration and definition */
|
||||
#define PLUGIN_HOOK_CALL_DEF(name, payload_type, response_cb_arg_type) \
|
||||
static inline void plugin_hook_call_##name( \
|
||||
struct lightningd *ld, payload_type payload, \
|
||||
response_cb_arg_type cb_arg) \
|
||||
{ \
|
||||
plugin_hook_call_(ld, &name##_hook_gen, (void *)payload, \
|
||||
(void *)cb_arg); \
|
||||
}
|
||||
|
||||
/* Typechecked registration of a plugin hook. We check that the
|
||||
* serialize_payload function converts an object of type payload_type
|
||||
* to a json_stream (.params object in the JSON-RPC request), that the
|
||||
* deserialize_response function converts from the JSON-RPC response
|
||||
* json_stream to an object of type response_type and that the
|
||||
* response_cb function accepts the deserialized response format and
|
||||
* an arbitrary extra argument used to maintain context.
|
||||
*/
|
||||
#define REGISTER_PLUGIN_HOOK(name, response_cb, response_cb_arg_type, \
|
||||
serialize_payload, payload_type, \
|
||||
deserialize_response, response_type) \
|
||||
struct plugin_hook name##_hook_gen = { \
|
||||
stringify(name), \
|
||||
typesafe_cb_cast(void (*)(void *, void *), \
|
||||
void (*)(response_cb_arg_type, response_type), \
|
||||
response_cb), \
|
||||
typesafe_cb_cast(void (*)(void *, struct json_stream *), \
|
||||
void (*)(payload_type, struct json_stream *), \
|
||||
serialize_payload), \
|
||||
typesafe_cb_cast( \
|
||||
void *(*)(const tal_t *, const char *, const jsmntok_t *), \
|
||||
response_type (*)(const tal_t *, const char *, \
|
||||
const jsmntok_t *), \
|
||||
deserialize_response), \
|
||||
NULL, /* .plugin */ \
|
||||
}; \
|
||||
AUTODATA(hooks, &name##_hook_gen); \
|
||||
PLUGIN_HOOK_CALL_DEF(name, payload_type, response_cb_arg_type);
|
||||
|
||||
bool plugin_hook_register(struct plugin *plugin, const char *method);
|
||||
|
||||
#endif /* LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H */
|
Loading…
Reference in New Issue
Block a user