mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-03 18:57:06 +01:00
One is called on every plugin return, and tells us whether to continue; the other is only called if every plugin says ok. This works for things like payload replacement, where we need to process the results from each plugin, not just the final one! We should probably turn everything into a chained callback next release. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
166 lines
7.7 KiB
C
166 lines
7.7 KiB
C
#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 <common/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 `cb_arg_type`
|
|
* and serializes it into the given `json_stream`. `
|
|
*
|
|
* For single-plugin hooks:
|
|
* - `single_response_cb` is called once the plugin has responded (or with
|
|
* buffer == NULL if there's no plugin). In addition an arbitrary
|
|
* additional argument of type `cb_arg_type` can be passed along
|
|
* that may contain any additional context necessary. It must free
|
|
* or otherwise take ownership of the cb_arg_type argument.
|
|
*
|
|
* For chained-plugin hooks:
|
|
* - `deserialize_cb` is called for each plugin, if it returns true the
|
|
* next one is called, otherwise the cb_arg_type argument is free.
|
|
* - If all `deserialize_cb` return true, `final_cb` is called. It must free
|
|
* or otherwise take ownership of the cb_arg_type argument.
|
|
*
|
|
* To make hook invocations easier, each 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.
|
|
*/
|
|
|
|
enum plugin_hook_type {
|
|
PLUGIN_HOOK_SINGLE,
|
|
PLUGIN_HOOK_CHAIN,
|
|
};
|
|
|
|
struct plugin_hook {
|
|
const char *name;
|
|
|
|
/* Which type of plugin is this? It'll determine how many plugins can
|
|
* register this hook, and how the hooks are called. */
|
|
enum plugin_hook_type type;
|
|
|
|
/* For PLUGIN_HOOK_SINGLE hooks */
|
|
void (*single_response_cb)(void *arg,
|
|
const char *buffer, const jsmntok_t *toks);
|
|
|
|
/* For PLUGIN_HOOK_CHAIN hooks: */
|
|
/* Returns false if we should stop iterating (and free arg). */
|
|
bool (*deserialize_cb)(void *arg,
|
|
const char *buffer, const jsmntok_t *toks);
|
|
void (*final_cb)(void *arg);
|
|
|
|
/* To send the payload to the plugin */
|
|
void (*serialize_payload)(void *src, struct json_stream *dest);
|
|
|
|
/* Which plugins have registered this hook? This is a `tal_arr`
|
|
* initialized at creation. */
|
|
struct plugin **plugins;
|
|
};
|
|
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,
|
|
tal_t *cb_arg STEALS);
|
|
|
|
/* Generic deserialize_cb: returns true iff 'result': 'continue' */
|
|
bool plugin_hook_continue(void *arg, const char *buffer, const jsmntok_t *toks);
|
|
|
|
/* 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, cb_arg_type) \
|
|
UNNEEDED static inline void plugin_hook_call_##name( \
|
|
struct lightningd *ld, cb_arg_type cb_arg STEALS) \
|
|
{ \
|
|
plugin_hook_call_(ld, &name##_hook_gen, 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_SINGLE_PLUGIN_HOOK(name, response_cb, \
|
|
serialize_payload, cb_arg_type) \
|
|
struct plugin_hook name##_hook_gen = { \
|
|
stringify(name), \
|
|
PLUGIN_HOOK_SINGLE, \
|
|
typesafe_cb_cast( \
|
|
void (*)(void *STEALS, const char *, const jsmntok_t *), \
|
|
void (*)(cb_arg_type STEALS, const char *, const jsmntok_t *), \
|
|
response_cb), \
|
|
NULL, NULL, \
|
|
typesafe_cb_cast(void (*)(void *, struct json_stream *), \
|
|
void (*)(cb_arg_type, struct json_stream *), \
|
|
serialize_payload), \
|
|
NULL, /* .plugins */ \
|
|
}; \
|
|
AUTODATA(hooks, &name##_hook_gen); \
|
|
PLUGIN_HOOK_CALL_DEF(name, cb_arg_type)
|
|
|
|
|
|
#define REGISTER_PLUGIN_HOOK(name, deserialize_cb, final_cb, \
|
|
serialize_payload, cb_arg_type) \
|
|
struct plugin_hook name##_hook_gen = { \
|
|
stringify(name), \
|
|
PLUGIN_HOOK_CHAIN, \
|
|
NULL, \
|
|
typesafe_cb_cast( \
|
|
bool (*)(void *, const char *, const jsmntok_t *), \
|
|
bool (*)(cb_arg_type, const char *, const jsmntok_t *), \
|
|
deserialize_cb), \
|
|
typesafe_cb_cast( \
|
|
void (*)(void *STEALS), \
|
|
void (*)(cb_arg_type STEALS), \
|
|
final_cb), \
|
|
typesafe_cb_cast(void (*)(void *, struct json_stream *), \
|
|
void (*)(cb_arg_type, struct json_stream *), \
|
|
serialize_payload), \
|
|
NULL, /* .plugins */ \
|
|
}; \
|
|
AUTODATA(hooks, &name##_hook_gen); \
|
|
PLUGIN_HOOK_CALL_DEF(name, cb_arg_type)
|
|
|
|
bool plugin_hook_register(struct plugin *plugin, const char *method);
|
|
|
|
/* Unregister a hook a plugin has registered for */
|
|
bool plugin_hook_unregister(struct plugin *plugin, const char *method);
|
|
|
|
/* Unregister all hooks a plugin has registered for */
|
|
void plugin_hook_unregister_all(struct plugin *plugin);
|
|
|
|
/* Special sync plugin hook for db. */
|
|
void plugin_hook_db_sync(struct db *db);
|
|
|
|
#endif /* LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H */
|