|
|
|
#ifndef LIGHTNING_LIGHTNINGD_PLUGIN_H
|
|
|
|
#define LIGHTNING_LIGHTNINGD_PLUGIN_H
|
|
|
|
#include "config.h"
|
|
|
|
#include <ccan/intmap/intmap.h>
|
|
|
|
#include <ccan/io/io.h>
|
|
|
|
#include <ccan/pipecmd/pipecmd.h>
|
|
|
|
#include <ccan/take/take.h>
|
|
|
|
#include <ccan/tal/path/path.h>
|
|
|
|
#include <ccan/tal/tal.h>
|
|
|
|
#include <common/json_command.h>
|
|
|
|
#include <common/jsonrpc_errors.h>
|
|
|
|
#include <common/memleak.h>
|
|
|
|
#include <common/param.h>
|
|
|
|
#include <common/timeout.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <lightningd/io_loop_with_timers.h>
|
|
|
|
#include <lightningd/jsonrpc.h>
|
|
|
|
#include <lightningd/lightningd.h>
|
|
|
|
#include <lightningd/log.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
|
|
enum plugin_state {
|
|
|
|
/* We have to ask getmanifest */
|
|
|
|
UNCONFIGURED,
|
|
|
|
/* We sent getmanifest, need response. */
|
|
|
|
AWAITING_GETMANIFEST_RESPONSE,
|
|
|
|
/* Got `getmanifest` reply, now we need to send `init`. */
|
|
|
|
NEEDS_INIT,
|
|
|
|
/* We have to get `init` response */
|
|
|
|
AWAITING_INIT_RESPONSE,
|
|
|
|
/* We have `init` response. */
|
|
|
|
INIT_COMPLETE
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A plugin, exposed as a stub so we can pass it as an argument.
|
|
|
|
*/
|
|
|
|
struct plugin {
|
|
|
|
struct list_node list;
|
|
|
|
|
|
|
|
pid_t pid;
|
|
|
|
char *cmd;
|
|
|
|
struct io_conn *stdin_conn, *stdout_conn;
|
|
|
|
struct plugins *plugins;
|
|
|
|
const char **plugin_path;
|
|
|
|
/* If there's a json command which ordered this to start */
|
|
|
|
struct command *start_cmd;
|
|
|
|
|
|
|
|
enum plugin_state plugin_state;
|
|
|
|
|
|
|
|
/* Our unique index, which is default hook ordering. */
|
|
|
|
u64 index;
|
|
|
|
|
|
|
|
/* If this plugin can be restarted without restarting lightningd */
|
|
|
|
bool dynamic;
|
|
|
|
|
|
|
|
/* Stuff we read */
|
|
|
|
char *buffer;
|
|
|
|
size_t used, len_read;
|
|
|
|
jsmn_parser parser;
|
|
|
|
jsmntok_t *toks;
|
|
|
|
|
|
|
|
/* Our json_streams. Since multiple streams could start
|
|
|
|
* returning data at once, we always service these in order,
|
|
|
|
* freeing once empty. */
|
|
|
|
struct json_stream **js_arr;
|
|
|
|
|
|
|
|
struct log *log;
|
|
|
|
|
|
|
|
/* List of options that this plugin registered */
|
|
|
|
struct list_head plugin_opts;
|
|
|
|
|
|
|
|
const char **methods;
|
|
|
|
|
|
|
|
/* Timer to add a timeout to some plugin RPC calls. Used to
|
|
|
|
* guarantee that `getmanifest` doesn't block indefinitely. */
|
|
|
|
const struct oneshot *timeout_timer;
|
|
|
|
|
|
|
|
/* An array of subscribed topics */
|
|
|
|
char **subscriptions;
|
|
|
|
|
|
|
|
/* An array of currently pending RPC method calls, to be killed if the
|
|
|
|
* plugin exits. */
|
|
|
|
struct list_head pending_rpccalls;
|
|
|
|
|
|
|
|
/* If set, the plugin is so important that if it terminates early,
|
|
|
|
* C-lightning should terminate as well. */
|
|
|
|
bool important;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A collection of plugins, and some associated information.
|
|
|
|
*
|
|
|
|
* Mainly used as root context for calls in the plugin subsystem.
|
|
|
|
*/
|
|
|
|
struct plugins {
|
|
|
|
struct list_head plugins;
|
|
|
|
bool startup;
|
|
|
|
|
|
|
|
/* Currently pending requests by their request ID */
|
|
|
|
UINTMAP(struct jsonrpc_request *) pending_requests;
|
|
|
|
struct log *log;
|
|
|
|
struct log_book *log_book;
|
|
|
|
|
|
|
|
struct lightningd *ld;
|
|
|
|
const char *default_dir;
|
|
|
|
|
|
|
|
/* If there are json commands waiting for plugin resolutions. */
|
|
|
|
struct command **json_cmds;
|
|
|
|
|
|
|
|
/* Blacklist of plugins from --disable-plugin */
|
|
|
|
const char **blacklist;
|
|
|
|
|
|
|
|
/* Whether we are shutting down (`plugins_free` is called) */
|
|
|
|
bool shutdown;
|
|
|
|
|
|
|
|
/* Index to show what order they were added in */
|
|
|
|
u64 plugin_idx;
|
|
|
|
|
|
|
|
#if DEVELOPER
|
|
|
|
/* Whether builtin plugins should be overridden as unimportant. */
|
|
|
|
bool dev_builtin_plugins_unimportant;
|
|
|
|
#endif /* DEVELOPER */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* The value of a plugin option, which can have different types.
|
|
|
|
* The presence of the integer and boolean values will depend of
|
|
|
|
* the option type, but the string value will always be filled.
|
|
|
|
*/
|
|
|
|
struct plugin_opt_value {
|
|
|
|
char *as_str;
|
|
|
|
s64 *as_int;
|
|
|
|
bool *as_bool;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Simple storage for plugin options inbetween registering them on the
|
|
|
|
* command line and passing them off to the plugin
|
|
|
|
*/
|
|
|
|
struct plugin_opt {
|
|
|
|
struct list_node list;
|
|
|
|
const char *name;
|
|
|
|
const char *type;
|
|
|
|
const char *description;
|
|
|
|
struct plugin_opt_value *value;
|
|
|
|
bool deprecated;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new plugins context.
|
|
|
|
*/
|
|
|
|
struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book,
|
|
|
|
struct lightningd *ld);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recursively add all plugins from the default plugins directory.
|
|
|
|
*/
|
|
|
|
void plugins_add_default_dir(struct plugins *plugins);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize the registered plugins.
|
|
|
|
*
|
|
|
|
* Initialization includes spinning up the plugins, reading their
|
|
|
|
* manifest, and registering the JSON-RPC passthrough and command line
|
|
|
|
* arguments. In order to read the getmanifest reply from the plugins
|
|
|
|
* we spin up our own io_loop that exits once all plugins have
|
|
|
|
* responded.
|
|
|
|
*/
|
|
|
|
void plugins_init(struct plugins *plugins);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free all resources that are held by plugins in the correct order.
|
|
|
|
*
|
|
|
|
* This function ensures that the resources dangling off of the plugins struct
|
|
|
|
* are freed in the correct order. This is necessary since the struct manages
|
|
|
|
* two orthogonal sets of resources:
|
|
|
|
*
|
|
|
|
* - Plugins
|
|
|
|
* - Hook calls and notifications
|
|
|
|
*
|
|
|
|
* The tal hierarchy is organized in a plugin centric way, i.e., the plugins
|
|
|
|
* may exit in an arbitrary order and they'll unregister pointers in the other
|
|
|
|
* resources. However this will fail if `tal_free` decides to free one of the
|
|
|
|
* non-plugin resources (technically a sibling in the allocation tree) before
|
|
|
|
* the plugins we will get a use-after-free. This function fixes this by
|
|
|
|
* freeing in the correct order without adding additional child-relationships
|
|
|
|
* in the allocation structure and without adding destructors.
|
|
|
|
*/
|
|
|
|
void plugins_free(struct plugins *plugins);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register a plugin for initialization and execution.
|
|
|
|
*
|
|
|
|
* @param plugins: Plugin context
|
|
|
|
* @param path: The path of the executable for this plugin
|
|
|
|
* @param start_cmd: The optional JSON command which caused this.
|
|
|
|
* @param important: The plugin is important.
|
|
|
|
*
|
|
|
|
* If @start_cmd, then plugin_cmd_killed or plugin_cmd_succeeded will be called
|
|
|
|
* on it eventually.
|
|
|
|
*/
|
|
|
|
struct plugin *plugin_register(struct plugins *plugins,
|
|
|
|
const char* path TAKES,
|
|
|
|
struct command *start_cmd,
|
|
|
|
bool important);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the provided name matches a plugin command
|
|
|
|
*/
|
|
|
|
bool plugin_paths_match(const char *cmd, const char *name);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a plugin registered for initialization.
|
|
|
|
*
|
|
|
|
* @param plugins: Plugin context
|
|
|
|
* @param arg: The basename or fullname of the executable for this plugin
|
|
|
|
*/
|
|
|
|
void plugin_blacklist(struct plugins *plugins, const char *name);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is a plugin disabled?.
|
|
|
|
*
|
|
|
|
* @param plugins: Plugin context
|
|
|
|
* @param arg: The basename or fullname of the executable for this plugin
|
|
|
|
*/
|
|
|
|
bool plugin_blacklisted(struct plugins *plugins, const char *name);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Kick off initialization of a plugin.
|
|
|
|
*
|
|
|
|
* Returns error string, or NULL.
|
|
|
|
*/
|
|
|
|
const char *plugin_send_getmanifest(struct plugin *p);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Kick of initialization of all plugins which need it/
|
|
|
|
*
|
|
|
|
* Return true if any were started.
|
|
|
|
*/
|
|
|
|
bool plugins_send_getmanifest(struct plugins *plugins);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Kill a plugin process and free @plugin, with an error message.
|
|
|
|
*/
|
|
|
|
void plugin_kill(struct plugin *plugin, const char *msg);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the plugin which registers the command with name {cmd_name}
|
|
|
|
*/
|
|
|
|
struct plugin *find_plugin_for_command(struct lightningd *ld,
|
|
|
|
const char *cmd_name);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call plugin_cmd_all_complete once all plugins are init or killed.
|
|
|
|
*
|
|
|
|
* Returns NULL if it's still pending. otherwise, returns
|
|
|
|
* plugin_cmd_all_complete().
|
|
|
|
*/
|
|
|
|
struct command_result *plugin_register_all_complete(struct lightningd *ld,
|
|
|
|
struct command *cmd);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send the configure message to all plugins.
|
|
|
|
*
|
|
|
|
* Once we've collected all the command line arguments we can go ahead
|
|
|
|
* and send them over to the plugin. This finalizes the initialization
|
|
|
|
* of the plugins and signals that lightningd is now ready to process
|
|
|
|
* incoming JSON-RPC calls and messages.
|
|
|
|
*/
|
|
|
|
void plugins_config(struct plugins *plugins);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Are any plugins at this state still?
|
|
|
|
*/
|
|
|
|
bool plugins_any_in_state(const struct plugins *plugins, enum plugin_state state);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Are all plugins at this state?
|
|
|
|
*/
|
|
|
|
bool plugins_all_in_state(const struct plugins *plugins, enum plugin_state state);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This populates the jsonrpc request with the plugin/lightningd specifications
|
|
|
|
*/
|
|
|
|
void plugin_populate_init_request(struct plugin *p, struct jsonrpc_request *req);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the plugin option and their respective options to listconfigs.
|
|
|
|
*
|
|
|
|
* This adds a dict that maps the plugin name to a dict of configuration options
|
|
|
|
* for the corresponding plugins.
|
|
|
|
*/
|
|
|
|
void json_add_opt_plugins(struct json_stream *response,
|
|
|
|
const struct plugins *plugins);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the disable-plugins options to listconfigs.
|
|
|
|
*/
|
|
|
|
void json_add_opt_disable_plugins(struct json_stream *response,
|
|
|
|
const struct plugins *plugins);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used by db hooks which can't have any other I/O while talking to
|
|
|
|
* hooked plugins.
|
|
|
|
*
|
|
|
|
* @param plugins - a `tal`-allocated array of plugins that are the
|
|
|
|
* only ones we talk to.
|
|
|
|
*
|
|
|
|
* @return output of io_loop() (ie. whatever gets passed to io_break()
|
|
|
|
* to end exclusive loop).
|
|
|
|
*/
|
|
|
|
void *plugins_exclusive_loop(struct plugin **plugins);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a directory to the plugin path to automatically load plugins.
|
|
|
|
*/
|
|
|
|
char *add_plugin_dir(struct plugins *plugins, const char *dir,
|
|
|
|
bool error_ok);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clear all plugins registered so far.
|
|
|
|
*/
|
|
|
|
void clear_plugins(struct plugins *plugins);
|
|
|
|
|
|
|
|
void plugins_notify(struct plugins *plugins,
|
|
|
|
const struct jsonrpc_notification *n TAKES);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send a jsonrpc_request to the specified plugin
|
|
|
|
*/
|
|
|
|
void plugin_request_send(struct plugin *plugin,
|
|
|
|
struct jsonrpc_request *req TAKES);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback called when parsing options. It just stores the value in
|
|
|
|
* the plugin_opt
|
|
|
|
*/
|
|
|
|
char *plugin_opt_set(const char *arg, struct plugin_opt *popt);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback called when plugin flag-type options.It just stores
|
|
|
|
* the value in the plugin_opt
|
|
|
|
*/
|
|
|
|
char *plugin_opt_flag_set(struct plugin_opt *popt);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helpers to initialize a connection to a plugin; we read from their
|
|
|
|
* stdout, and write to their stdin.
|
|
|
|
*/
|
|
|
|
struct io_plan *plugin_stdin_conn_init(struct io_conn *conn,
|
|
|
|
struct plugin *plugin);
|
|
|
|
struct io_plan *plugin_stdout_conn_init(struct io_conn *conn,
|
|
|
|
struct plugin *plugin);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Needed for I/O logging for plugin messages.
|
|
|
|
*/
|
|
|
|
struct log *plugin_get_log(struct plugin *plugin);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tells the plugin system the directory for builtin plugins.
|
|
|
|
*/
|
|
|
|
void plugins_set_builtin_plugins_dir(struct plugins *plugins,
|
|
|
|
const char *dir);
|
|
|
|
|
|
|
|
/* Pair of functions to detect if plugin destroys itself: must always
|
|
|
|
* call both! */
|
|
|
|
struct plugin_destroyed *plugin_detect_destruction(const struct plugin *plugin);
|
|
|
|
bool was_plugin_destroyed(struct plugin_destroyed *destroyed);
|
|
|
|
#endif /* LIGHTNING_LIGHTNINGD_PLUGIN_H */
|