#include "config.h"
#include <ccan/io/io.h>
#include <ccan/json_out/json_out.h>
#include <ccan/read_write_all/read_write_all.h>
#include <ccan/tal/path/path.h>
#include <ccan/tal/str/str.h>
#include <common/daemon.h>
#include <common/deprecation.h>
#include <common/features.h>
#include <common/json_filter.h>
#include <common/json_param.h>
#include <common/json_stream.h>
#include <common/jsonrpc_io.h>
#include <common/memleak.h>
#include <common/plugin.h>
#include <common/route.h>
#include <common/trace.h>
#include <errno.h>
#include <inttypes.h>
#include <plugins/libplugin.h>
#include <stdio.h>
#include <unistd.h>

#define READ_CHUNKSIZE 4096

struct plugin_timer {
	struct timer timer;
	const char *id;
	struct command_result *(*cb)(struct command *cmd, void *cb_arg);
	void *cb_arg;
};

/* We can have more than one of these pending at once. */
struct jstream {
	struct list_node list;
	struct json_stream *js;
};

/* Create an array of these, one for each --option you support. */
struct plugin_option {
	const char *name;
	const char *type;
	const char *description;
	/* Handle an option.  If dynamic, check_only may be set to check
	 * for validity (but must not make any changes!) */
	char *(*handle)(struct command *cmd, const char *str, bool check_only,
			void *arg);
	/* Print an option (used to show the default value, if returns true) */
	bool (*jsonfmt)(struct command *cmd, struct json_stream *js, const char *fieldname,
			void *arg);
	/* Arg for handle and jsonfmt */
	void *arg;
	/* If true, this option requires --developer to be enabled */
	bool dev_only;
	/* If it's deprecated from a particular release (or NULL) */
	const char *depr_start, *depr_end;
	/* If true, allow setting after plugin has initialized */
	bool dynamic;
	/* If true, allow multiple settings. */
	bool multi;
};

struct plugin {
	/* lightningd interaction */
	struct io_conn *stdin_conn;
	struct io_conn *stdout_conn;

	/* Are we in developer mode? */
	bool developer;

	/* Global deprecations enabled? */
	bool deprecated_ok;

	/* Is this command overriding global deprecated_ok? */
	bool *deprecated_ok_override;

	/* to append to all our command ids */
	const char *id;

	/* Data for the plugin user */
	void *data;

	/* options to i-promise-to-fix-broken-api-user */
	const char **beglist;

	/* To read from lightningd */
	struct jsonrpc_io *lightningd_in;

	/* To write to lightningd */
	struct list_head js_list;

	/* Asynchronous RPC interaction. */
	struct io_conn *io_rpc_conn;
	struct list_head rpc_js_list;
	struct jsonrpc_io *jsonrpc_in;

	/* Tracking async RPC requests */
	STRMAP(struct out_req *) out_reqs;
	u64 next_outreq_id;

	/* Synchronous RPC interaction: sync_io is NULL if they didn't want it. */
	int sync_fd;
	struct jsonrpc_io *sync_io;

	/* Plugin information details */
	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 ? */
	const char *(*init)(struct command *init_cmd,
			    const char *buf, const jsmntok_t *);
	/* Has the manifest been sent already ? */
	bool manifested;
	/* Has init been received ? */
	bool initialized;
	/* Are we exiting? */
	bool exiting;

	/* Map from json command names to usage strings: we don't put this inside
	 * struct json_command as it's good practice to have those const. */
	STRMAP(const char *) usagemap;
	/* Timers */
	struct timers timers;

	/* Feature set for lightningd */
	struct feature_set *our_features;
	/* Features we want to add to lightningd */
	const struct feature_set *desired_features;

	/* Location of the RPC filename in case we need to defer RPC
	 * initialization or need to recover from a disconnect. */
	const char *rpc_location;

	const char **notif_topics;
	size_t num_notif_topics;

	/* Lets them remove ptrs from leak detection. */
	void (*mark_mem)(struct plugin *plugin, struct htable *memtable);
};

/* 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) and pending (still going) */
struct command_result {
	char c;
};
static struct command_result complete, pending;

struct command_result *command_param_failed(void)
{
	return &complete;
}

struct json_filter **command_filter_ptr(struct command *cmd)
{
	return &cmd->filter;
}

/* New command, without a filter */
static struct command *new_command(const tal_t *ctx,
				   struct plugin *plugin,
				   const char *id TAKES,
				   const char *methodname TAKES,
				   enum command_type type)
{
	struct command *cmd = tal(ctx, struct command);

	cmd->plugin = plugin;
	cmd->type = type;
	cmd->filter = NULL;
	cmd->methodname = tal_strdup(cmd, methodname);
	cmd->id = tal_strdup(cmd, id);
	return cmd;
}

static int rpc_open(struct plugin *plugin)
{
	struct sockaddr_un addr;
	int fd = socket(AF_UNIX, SOCK_STREAM, 0);

	if (strlen(plugin->rpc_location) + 1 > sizeof(addr.sun_path))
		plugin_err(plugin, "rpc filename '%s' too long",
			   plugin->rpc_location);
	strcpy(addr.sun_path, plugin->rpc_location);
	addr.sun_family = AF_UNIX;

	if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
		plugin_log(plugin, LOG_UNUSUAL,
			   "Could not connect to '%s': %s",
			   plugin->rpc_location, strerror(errno));
		close(fd);
		fd = -1;
	}
	return fd;
}

static void complain_deprecated(const char *feature,
				bool allowing,
				struct command *cmd)
{
	if (!allowing) {
		/* Mild log message for disallowing */
		plugin_log(cmd->plugin, LOG_DBG,
			   "Note: disallowing deprecated %s for %s",
			   feature, cmd->id);
	} else {
		plugin_log(cmd->plugin, LOG_BROKEN,
			   "DEPRECATED API USED: %s by %s",
			   feature, cmd->id);
	}
}

bool command_deprecated_in_named_ok(struct command *cmd,
				    const char *cmdname,
				    const char *param,
				    const char *depr_start,
				    const char *depr_end)
{
	return deprecated_ok(command_deprecated_ok_flag(cmd),
			     param
			     ? tal_fmt(tmpctx, "%s.%s", cmdname, param)
			     : cmdname,
			     depr_start, depr_end,
			     cmd->plugin->beglist,
			     complain_deprecated, cmd);
}

bool command_deprecated_in_ok(struct command *cmd,
			      const char *param,
			      const char *depr_start,
			      const char *depr_end)
{
	return command_deprecated_in_named_ok(cmd, cmd->methodname, param,
					      depr_start, depr_end);
}

bool command_deprecated_out_ok(struct command *cmd,
			       const char *fieldname,
			       const char *depr_start,
			       const char *depr_end)
{
	return deprecated_ok(command_deprecated_ok_flag(cmd),
			     tal_fmt(tmpctx, "%s.%s", cmd->methodname, fieldname),
			     depr_start, depr_end,
			     cmd->plugin->beglist,
			     NULL, NULL);
}

bool notification_deprecated_out_ok(struct plugin *plugin,
				    const char *method,
				    const char *fieldname,
				    const char *depr_start,
				    const char *depr_end)
{
	return deprecated_ok(plugin->deprecated_ok,
			     tal_fmt(tmpctx, "%s.%s", method, fieldname),
			     depr_start, depr_end,
			     plugin->beglist,
			     NULL, NULL);
}

static void ld_send(struct plugin *plugin, struct json_stream *stream)
{
	struct jstream *jstr = tal(plugin, struct jstream);
	jstr->js = tal_steal(jstr, stream);
	list_add_tail(&plugin->js_list, &jstr->list);
	io_wake(plugin);
}

static void ld_rpc_send(struct plugin *plugin, struct json_stream *stream)
{
	struct jstream *jstr = tal(plugin, struct jstream);
	jstr->js = tal_steal(jstr, stream);
	list_add_tail(&plugin->rpc_js_list, &jstr->list);
	if (plugin->io_rpc_conn)
		io_wake(plugin->io_rpc_conn);
}

/* Ignore the result, and terminate the timer/aux/hook */
struct command_result *ignore_and_complete(struct command *cmd,
					   const char *method,
					   const char *buf,
					   const jsmntok_t *result,
					   void *arg)
{
	switch (cmd->type) {
	case COMMAND_TYPE_NORMAL:
	case COMMAND_TYPE_CHECK:
	case COMMAND_TYPE_USAGE_ONLY:
		plugin_err(cmd->plugin,
			   "%s: cannot ignore_and_complete type %u",
			   method, cmd->type);
	case COMMAND_TYPE_HOOK:
		return command_hook_success(cmd);
	/* Terminate with aux_command_done */
	case COMMAND_TYPE_AUX:
		return aux_command_done(cmd);
	case COMMAND_TYPE_NOTIFICATION:
		return notification_handled(cmd);
	case COMMAND_TYPE_TIMER:
		return timer_complete(cmd);
	}
	abort();
}

/* Broken the result, and terminate the command */
struct command_result *log_broken_and_complete(struct command *cmd,
					       const char *method,
					       const char *buf,
					       const jsmntok_t *result,
					       void *arg)
{
	plugin_log(cmd->plugin, LOG_BROKEN,
		   "%s failed with %.*s",
		   method,
		   json_tok_full_len(result),
		   json_tok_full(buf, result));
	return ignore_and_complete(cmd, method, buf, result, arg);
}

/* Call plugin_err */
struct command_result *plugin_broken_cb(struct command *cmd,
					const char *method,
					const char *buf,
					const jsmntok_t *result,
					void *arg)
{
	plugin_err(cmd->plugin,
		   "%s failed with %.*s",
		   method,
		   json_tok_full_len(result),
		   json_tok_full(buf, result));
}

/* Prefix is usually a cmd->id */
static const char *json_id(const tal_t *ctx, struct plugin *plugin,
			   const char *method, const char *prefix)
{
	const char *rawid;
	int rawidlen;

	/* Strip quotes! */
	if (strstarts(prefix, "\"")) {
		assert(strlen(prefix) >= 2);
		assert(strends(prefix, "\""));
		rawid = prefix + 1;
		rawidlen = strlen(prefix) - 2;
	} else {
		rawid = prefix;
		rawidlen = strlen(prefix);
	}

	return tal_fmt(ctx, "\"%.*s/%s:%s#%"PRIu64"\"",
		       rawidlen, rawid, plugin->id, method, plugin->next_outreq_id++);
}

static void destroy_out_req(struct out_req *out_req, struct plugin *plugin)
{
	strmap_del(&plugin->out_reqs, out_req->id, NULL);
}

/* FIXME: Move lightningd/jsonrpc to common/ ? */

struct out_req *
jsonrpc_request_start_(struct command *cmd,
		       const char *method,
		       const char *id_prefix,
		       const char *filter,
		       struct command_result *(*cb)(struct command *command,
						    const char *methodname,
						    const char *buf,
						    const jsmntok_t *result,
						    void *arg),
		       struct command_result *(*errcb)(struct command *command,
						       const char *methodname,
						       const char *buf,
						       const jsmntok_t *result,
						       void *arg),
		       void *arg)
{
	struct out_req *out;

	assert(cmd);
	out = tal(cmd, struct out_req);
	out->method = tal_strdup(out, method);
	out->id = json_id(out, cmd->plugin, method, id_prefix ? id_prefix : cmd->id);
	out->cmd = cmd;
	out->cb = cb;
	out->errcb = errcb;
	out->arg = arg;
	strmap_add(&cmd->plugin->out_reqs, out->id, out);
	tal_add_destructor2(out, destroy_out_req, cmd->plugin);

	out->js = new_json_stream(NULL, cmd, NULL);
	json_object_start(out->js, NULL);
	json_add_string(out->js, "jsonrpc", "2.0");
	json_add_id(out->js, out->id);
	json_add_string(out->js, "method", method);
	if (filter) {
		/* This is raw JSON, so paste, don't escape! */
		size_t len = strlen(filter);
		char *p = json_out_member_direct(out->js->jout, "filter", len);
		memcpy(p, filter, len);
	}
	if (out->errcb)
		json_object_start(out->js, "params");

	return out;
}

const struct feature_set *plugin_feature_set(const struct plugin *p)
{
	return p->our_features;
}

static void jsonrpc_finish_and_send(struct plugin *p, struct json_stream *js)
{
	json_object_end(js);
	json_stream_close(js, NULL);
	ld_send(p, js);
}

static struct json_stream *jsonrpc_stream_start(struct command *cmd)
{
	struct json_stream *js = new_json_stream(cmd, cmd, NULL);

	json_object_start(js, NULL);
	json_add_string(js, "jsonrpc", "2.0");
	json_add_id(js, cmd->id);

	return js;
}

struct json_stream *jsonrpc_stream_success(struct command *cmd)
{
	struct json_stream *js = jsonrpc_stream_start(cmd);
	assert(cmd->type == COMMAND_TYPE_NORMAL
	       || cmd->type == COMMAND_TYPE_HOOK);

	json_object_start(js, "result");
	if (cmd->filter)
		json_stream_attach_filter(js, cmd->filter);
	return js;
}

struct json_stream *jsonrpc_stream_fail(struct command *cmd,
					int code,
					const char *err)
{
	struct json_stream *js = jsonrpc_stream_start(cmd);

	assert(cmd->type == COMMAND_TYPE_NORMAL
	       || cmd->type == COMMAND_TYPE_CHECK);
	json_object_start(js, "error");
	json_add_primitive_fmt(js, "code", "%d", code);
	json_add_string(js, "message", err);
	cmd->filter = tal_free(cmd->filter);

	return js;
}

struct json_stream *jsonrpc_stream_fail_data(struct command *cmd,
					     int code,
					     const char *err)
{
	struct json_stream *js = jsonrpc_stream_fail(cmd, code, err);

	json_object_start(js, "data");
	return js;
}

static struct command_result *command_complete_nojson(struct command *cmd,
						      struct json_stream *result)
{
	json_stream_close(result, cmd);
	ld_send(cmd->plugin, result);
	tal_free(cmd);

	return &complete;
}

static struct command_result *command_complete(struct command *cmd,
					       struct json_stream *result)
{
	/* Global object */
	json_object_end(result);
	return command_complete_nojson(cmd, result);
}

struct command_result *command_finished(struct command *cmd,
					struct json_stream *response)
{
	assert(cmd->type == COMMAND_TYPE_NORMAL
	       || cmd->type == COMMAND_TYPE_HOOK
	       || cmd->type == COMMAND_TYPE_CHECK);

	/* Detach filter before it complains about closing object it never saw */
	if (cmd->filter) {
		const char *err = json_stream_detach_filter(tmpctx, response);
		if (err)
			json_add_string(response, "warning_parameter_filter",
					err);
	}

	/* "result" or "error" object */
	json_object_end(response);

	return command_complete(cmd, response);
}

struct command_result *command_finish_rawstr(struct command *cmd,
					     const char *json,
					     size_t json_len)
{
	struct json_stream *js = new_json_stream(cmd, cmd, NULL);
	char *raw;

	assert(cmd->type == COMMAND_TYPE_NORMAL
	       || cmd->type == COMMAND_TYPE_HOOK);
	raw = json_out_direct(js->jout, json_len);
	memcpy(raw, json, json_len);
	return command_complete_nojson(cmd, js);
}

struct command_result *WARN_UNUSED_RESULT
command_still_pending(struct command *cmd)
{
	notleak_with_children(cmd);
	return &pending;
}

struct json_out *json_out_obj(const tal_t *ctx,
			      const char *fieldname,
			      const char *str)
{
	struct json_out *jout = json_out_new(ctx);
	json_out_start(jout, NULL, '{');
	if (str)
		json_out_addstr(jout, fieldname, str);
	tal_free_if_taken(str);
	json_out_end(jout, '}');
	json_out_finished(jout);

	return jout;
}

/* This closes a JSON response and writes it out. */
static void finish_and_send_json(int fd, struct json_out *jout)
{
	size_t len;
	const char *p;

	json_out_end(jout, '}');
	/* We double-\n terminate.  Don't need to, but it's more readable. */
	memcpy(json_out_direct(jout, 2), "\n\n", 2);
	json_out_finished(jout);

	p = json_out_contents(jout, &len);
	write_all(fd, p, len);
	json_out_consume(jout, len);
}

/* str is raw JSON from RPC output. */
static struct command_result *WARN_UNUSED_RESULT
command_done_raw(struct command *cmd,
		 const char *label,
		 const char *str, int size)
{
	struct json_stream *js = jsonrpc_stream_start(cmd);

	memcpy(json_out_member_direct(js->jout, label, size), str, size);

	return command_complete(cmd, js);
}

struct command_result *WARN_UNUSED_RESULT
command_success(struct command *cmd, const struct json_out *result)
{
	struct json_stream *js = jsonrpc_stream_start(cmd);
	assert(cmd->type == COMMAND_TYPE_NORMAL
	       || cmd->type == COMMAND_TYPE_HOOK);

	json_out_add_splice(js->jout, "result", result);
	return command_complete(cmd, js);
}

struct command_result *command_done_err(struct command *cmd,
					enum jsonrpc_errcode code,
					const char *errmsg,
					const struct json_out *data)
{
	struct json_stream *js = jsonrpc_stream_start(cmd);
	assert(cmd->type == COMMAND_TYPE_NORMAL
	       || cmd->type == COMMAND_TYPE_CHECK);

	json_object_start(js, "error");
	json_add_jsonrpc_errcode(js, "code", code);
	json_add_string(js, "message", errmsg);

	if (data)
		json_out_add_splice(js->jout, "data", data);
	json_object_end(js);

	return command_complete(cmd, js);
}

struct command_result *command_err_raw(struct command *cmd,
				       const char *json_str)
{
	assert(cmd->type == COMMAND_TYPE_NORMAL
	       || cmd->type == COMMAND_TYPE_CHECK);
	return command_done_raw(cmd, "error",
				json_str, strlen(json_str));
}

struct command_result *timer_complete(struct command *cmd)
{
	assert(cmd->type == COMMAND_TYPE_TIMER);
	tal_free(cmd);
	return &complete;
}

struct command_result *forward_error(struct command *cmd,
				     const char *method,
				     const char *buf,
				     const jsmntok_t *error,
				     void *arg UNNEEDED)
{
	/* Push through any errors. */
	return command_done_raw(cmd, "error",
				buf + error->start, error->end - error->start);
}

struct command_result *forward_result(struct command *cmd,
				      const char *method,
				      const char *buf,
				      const jsmntok_t *result,
				      void *arg UNNEEDED)
{
	/* Push through the result. */
	return command_done_raw(cmd, "result",
				buf + result->start, result->end - result->start);
}

/* Called by param() directly if it's malformed. */
struct command_result *command_fail(struct command *cmd,
				    enum jsonrpc_errcode code, const char *fmt, ...)
{
	va_list ap;
	struct command_result *res;

	va_start(ap, fmt);
	res = command_done_err(cmd, code, tal_vfmt(cmd, fmt, ap), NULL);
	va_end(ap);
	return res;
}

/* We invoke param for usage at registration time. */
bool command_usage_only(const struct command *cmd)
{
	return cmd->type == COMMAND_TYPE_USAGE_ONLY;
}

bool command_dev_apis(const struct command *cmd)
{
	return cmd->plugin->developer;
}

bool command_check_only(const struct command *cmd)
{
	return cmd->type == COMMAND_TYPE_CHECK;
}

void command_log(struct command *cmd, enum log_level level,
		 const char *fmt, ...)
{
	const char *msg;
	va_list ap;

	va_start(ap, fmt);
	msg = tal_vfmt(cmd, fmt, ap);
	plugin_log(cmd->plugin, level, "JSON COMMAND %s: %s",
		   cmd->methodname, msg);
	va_end(ap);
}

struct command_result *command_check_done(struct command *cmd)
{
	struct json_stream *js = jsonrpc_stream_start(cmd);
	assert(command_check_only(cmd));

	json_out_add_splice(js->jout, "result",
			    json_out_obj(cmd, "command_to_check",
					 cmd->methodname));
	return command_complete(cmd, js);
}

void command_set_usage(struct command *cmd, const char *usage TAKES)
{
	usage = tal_strdup(NULL, usage);
	if (!strmap_add(&cmd->plugin->usagemap, cmd->methodname, usage))
		plugin_err(cmd->plugin, "Two usages for command %s?",
			   cmd->methodname);
}

static const char *read_one_json_sync(struct plugin *p, const jsmntok_t **toks)
{
	for (;;) {
		const char *buf, *error;

		error = jsonrpc_io_parse(tmpctx, p->sync_io, toks, &buf);
		if (error)
			plugin_err(p, "Parsing sync lightningd: %s", error);
		if (*toks)
			return buf;

		/* lightningd goes away, we go away. */
		if (!jsonrpc_sync_read(p->sync_io, p->sync_fd)) {
			if (errno == 0)
				exit(0);
			else
				plugin_err(p, "Reading sync lightningd: %s",
					   strerror(errno));
		}
	}
}

/* Reads rpc reply and returns result tokens */
static const jsmntok_t *read_sync_rpc_reply(const tal_t *ctx,
					    struct plugin *plugin,
					    const char *method,
					    const char **final_buffer)
{
	const jsmntok_t *errtok, *resulttok, *toks;
	const char *buffer;

	for (;;) {
		buffer = read_one_json_sync(plugin, &toks);
		/* FIXME: Don't simply ignore notifications here! */
		if (json_get_member(buffer, toks, "id"))
			break;
		jsonrpc_io_parse_done(plugin->sync_io);
	}

	errtok = json_get_member(buffer, toks, "error");
	if (errtok) {
		plugin_err(plugin, "Got error result to %s: '%.*s'",
			   method,
			   json_tok_full_len(toks),
			   json_tok_full(buffer, toks));
	}
	resulttok = json_get_member(buffer, toks, "result");
	if (!resulttok) {
		plugin_err(plugin, "JSON reply with no 'result' nor 'error'? '%.*s'",
			   json_tok_full_len(toks),
			   json_tok_full(buffer, toks));
	}

	/* Make the returned pointers valid tal object */
	json_dup_contents(ctx, buffer, resulttok, final_buffer, &toks);
	jsonrpc_io_parse_done(plugin->sync_io);

	return toks;
}

/* Send request, return response, set resp/len to reponse */
static const jsmntok_t *sync_req(const tal_t *ctx,
				 struct plugin *plugin,
				 const char *method,
				 const struct json_out *params TAKES,
				 const char **resp)
{
	struct json_out *jout = json_out_new(tmpctx);
	const char *id = json_id(tmpctx, plugin, "init/", method);

	json_out_start(jout, NULL, '{');
	json_out_addstr(jout, "jsonrpc", "2.0");
	/* Copy in id *literally* */
	memcpy(json_out_member_direct(jout, "id", strlen(id)), id, strlen(id));
	json_out_addstr(jout, "method", method);
	if (params)
		json_out_add_splice(jout, "params", params);
	else {
		json_out_start(jout, "params", '{');
		json_out_end(jout, '}');
	}
	tal_free_if_taken(params);

	/* If we're past init, we may need a new fd (the old one
	 * is being used for async comms). */
	if (plugin->sync_fd == -1) {
		plugin->sync_fd = rpc_open(plugin);
		if (!plugin->sync_io)
			plugin->sync_io = jsonrpc_io_new(plugin);
	}

	finish_and_send_json(plugin->sync_fd, jout);

	return read_sync_rpc_reply(ctx, plugin, method, resp);
}

const jsmntok_t *jsonrpc_request_sync(const tal_t *ctx,
				      struct command *cmd,
				      const char *method,
				      const struct json_out *params TAKES,
				      const char **resp)
{
	return sync_req(ctx, cmd->plugin, method, params, resp);
}

/* Returns contents of scanning guide on 'result' */
static const char *rpc_scan_core(const tal_t *ctx,
				 struct plugin *plugin,
				 const char *method,
				 const struct json_out *params TAKES,
				 const char *guide,
				 va_list ap)
{
	const jsmntok_t *contents;
	const char *p;

	contents = sync_req(tmpctx, plugin, method, params, &p);
	return json_scanv(ctx, p, contents, guide, ap);
}

/* Synchronous routine to send command and extract fields from response */
void rpc_scan(struct command *cmd,
	      const char *method,
	      const struct json_out *params TAKES,
	      const char *guide,
	      ...)
{
	const char *err;
	va_list ap;

	va_start(ap, guide);
	err = rpc_scan_core(tmpctx, cmd->plugin, method, params, guide, ap);
	va_end(ap);

	if (err)
		plugin_err(cmd->plugin, "Could not parse %s in reply to %s: %s",
			   guide, method, err);
}

void json_add_keypath(struct json_out *jout,
		      const char *fieldname,
		      const char **keys)
{
	json_out_start(jout, fieldname, '[');
	for (size_t i = 0; i < tal_count(keys); i++)
		json_out_addstr(jout, NULL, keys[i]);
	json_out_end(jout, ']');
}

static const char *rpc_scan_datastore(const tal_t *ctx,
				      struct command *cmd,
				      const char **keys,
				      const char *hex_or_string,
				      va_list ap)
{
	const char *guide;
	struct json_out *params;

	params = json_out_new(NULL);
	json_out_start(params, NULL, '{');
	json_add_keypath(params, "key", keys);
	json_out_end(params, '}');
	json_out_finished(params);

	guide = tal_fmt(tmpctx, "{datastore:[0:{%s:%%}]}", hex_or_string);
	return rpc_scan_core(ctx, cmd->plugin, "listdatastore", take(params),
			     guide, ap);
}

const char *rpc_scan_datastore_str(const tal_t *ctx,
				   struct command *cmd,
				   const char **keys,
				   ...)
{
	const char *ret;
	va_list ap;

	va_start(ap, keys);
	ret = rpc_scan_datastore(ctx, cmd, keys, "string", ap);
	va_end(ap);
	return ret;
}

/* This variant scans the hex encoding, not the string */
const char *rpc_scan_datastore_hex(const tal_t *ctx,
				   struct command *cmd,
				   const char **keys,
				   ...)
{
	const char *ret;
	va_list ap;

	va_start(ap, keys);
	ret = rpc_scan_datastore(ctx, cmd, keys, "hex", ap);
	va_end(ap);
	return ret;
}

void rpc_enable_batching(struct plugin *plugin)
{
	const char *p;
	struct json_out *params;

	params = json_out_new(NULL);
	json_out_start(params, NULL, '{');
	json_out_add(params, "enable", false, "true");
	json_out_end(params, '}');
	json_out_finished(params);

	/* We don't actually care about (empty) response */
	sync_req(tmpctx, plugin, "batching", take(params), &p);
}

struct command_result *jsonrpc_set_datastore_(struct command *cmd,
					      const char **keys,
					      const void *value,
					      int len_or_str,
					      const char *mode,
					      struct command_result *(*cb)(struct command *command,
									   const char *method,
									   const char *buf,
									   const jsmntok_t *result,
									   void *arg),
					      struct command_result *(*errcb)(struct command *command,
									      const char *method,
									      const char *buf,
									      const jsmntok_t *result,
									      void *arg),
					      void *arg)
{
	struct out_req *req;

	if (!cb)
		cb = ignore_and_complete;
	if (!errcb)
		errcb = plugin_broken_cb;

	req = jsonrpc_request_start(cmd, "datastore", cb, errcb, arg);

	json_add_keypath(req->js->jout, "key", keys);
	if (len_or_str == -1)
		json_add_string(req->js, "string", value);
	else
		json_add_hex(req->js, "hex", value, len_or_str);
	json_add_string(req->js, "mode", mode);
	return send_outreq(req);
}

struct get_ds_info {
	struct command_result *(*string_cb)(struct command *command,
					    const char *val,
					    void *arg);
	struct command_result *(*binary_cb)(struct command *command,
					    const u8 *val,
					    void *arg);
	void *arg;
};

static struct command_result *listdatastore_done(struct command *cmd,
						 const char *method,
						 const char *buf,
						 const jsmntok_t *result,
						 struct get_ds_info *dsi)
{
	const jsmntok_t *ds = json_get_member(buf, result, "datastore");
	void *val;

	if (ds->size == 0)
		val = NULL;
	else {
		/* First element in array is object */
		ds = ds + 1;
		if (dsi->string_cb) {
			const jsmntok_t *s;
			s = json_get_member(buf, ds, "string");
			if (!s) {
				/* Complain loudly, since they
				 * expected string! */
				plugin_log(cmd->plugin, LOG_BROKEN,
					   "Datastore gave nonstring result %.*s",
					   json_tok_full_len(result),
					   json_tok_full(buf, result));
				val = NULL;
			} else {
				val = json_strdup(cmd, buf, s);
			}
		} else {
			const jsmntok_t *hex;
			hex = json_get_member(buf, ds, "hex");
			val = json_tok_bin_from_hex(cmd, buf, hex);
		}
	}

	if (dsi->string_cb)
		return dsi->string_cb(cmd, val, dsi->arg);
	return dsi->binary_cb(cmd, val, dsi->arg);
}

struct command_result *jsonrpc_get_datastore_(struct command *cmd,
					      const char **keys,
					      struct command_result *(*string_cb)(struct command *command,
									   const char *val,
									   void *arg),
					      struct command_result *(*binary_cb)(struct command *command,
									   const u8 *val,
									   void *arg),
					      void *arg)
{
	struct out_req *req;
	struct get_ds_info *dsi = tal(NULL, struct get_ds_info);

	dsi->string_cb = string_cb;
	dsi->binary_cb = binary_cb;
	dsi->arg = arg;

	/* listdatastore doesn't fail (except API misuse) */
	req = jsonrpc_request_start(cmd, "listdatastore",
				    listdatastore_done, plugin_broken_cb, dsi);
	tal_steal(req, dsi);

	json_add_keypath(req->js->jout, "key", keys);
	return send_outreq(req);
}

static void destroy_cmd_mark_freed(struct command *cmd, bool *cmd_freed)
{
	*cmd_freed = true;
}

static void handle_rpc_reply(const tal_t *working_ctx,
			     struct plugin *plugin, const char *buf, const jsmntok_t *toks)
{
	const jsmntok_t *idtok, *contenttok;
	struct out_req *out;
	struct command_result *res;
	bool cmd_freed;

	idtok = json_get_member(buf, toks, "id");
	if (!idtok)
		/* FIXME: Don't simply ignore notifications! */
		return;

	out = strmap_getn(&plugin->out_reqs,
			  json_tok_full(buf, idtok),
			  json_tok_full_len(idtok));
	if (!out) {
		/* This can actually happen, if they free req! */
		plugin_log(plugin, LOG_DBG, "JSON reply with unknown id '%.*s'",
			   json_tok_full_len(toks),
			   json_tok_full(buf, toks));
		return;
	}

	/* We want to free this if callback doesn't. */
	tal_steal(working_ctx, out);

	/* If they return complete, cmd should have been freed! */
	cmd_freed = false;
	tal_add_destructor2(out->cmd, destroy_cmd_mark_freed, &cmd_freed);

	trace_span_resume(out);
	contenttok = json_get_member(buf, toks, "error");

	/* Annotate the JSON-RPC span whether it succeeded or failed,
	 * and then emit it. */
	trace_span_tag(out, "error", contenttok ? "true" : "false");
	trace_span_end(out);

	if (contenttok) {
		if (out->errcb)
			res = out->errcb(out->cmd, out->method, buf, contenttok, out->arg);
		else
			res = out->cb(out->cmd, out->method, buf, toks, out->arg);
	} else {
		contenttok = json_get_member(buf, toks, "result");
		if (!contenttok)
			plugin_err(plugin, "Bad JSONRPC, no 'error' nor 'result': '%.*s'",
				   json_tok_full_len(toks),
				   json_tok_full(buf, toks));
		/* errcb is NULL if it's a single whole-object callback */
		if (out->errcb)
			res = out->cb(out->cmd, out->method, buf, contenttok, out->arg);
		else
			res = out->cb(out->cmd, out->method, buf, toks, out->arg);
	}

	if (res == &complete) {
		assert(cmd_freed);
	} else {
		assert(res == &pending);
		assert(!cmd_freed);
		tal_del_destructor2(out->cmd, destroy_cmd_mark_freed,
				    &cmd_freed);
	}
}

struct command_result *
send_outreq(const struct out_req *req)
{
	/* The "param" object. */
	if (req->errcb)
		json_object_end(req->js);
	json_object_end(req->js);
	json_stream_close(req->js, req->cmd);

	/* We are about to hand control over to the RPC, so suspend
	 * the current span. It'll be resumed as soon as we have a
	 * result to pass to either the error or the success
	 * callback. */
	trace_span_start("jsonrpc", req);
	trace_span_tag(req, "id", req->id);
	trace_span_suspend_may_free(req);

	ld_rpc_send(req->cmd->plugin, req->js);
	notleak_with_children(req->cmd);
	return &pending;
}

struct request_batch {
	size_t num_remaining;

	struct command_result *(*cb)(struct command *,
				     const char *,
				     const char *,
				     const jsmntok_t *,
				     void *);
	struct command_result *(*errcb)(struct command *,
					const char *,
					const char *,
					const jsmntok_t *,
					void *);
	struct command_result *(*finalcb)(struct command *,
					  void *);
	void *arg;
};

struct request_batch *request_batch_new_(const tal_t *ctx,
					 struct command_result *(*cb)(struct command *,
								      const char *,
								      const char *,
								      const jsmntok_t *,
								      void *),
					 struct command_result *(*errcb)(struct command *,
									 const char *,
									 const char *,
									 const jsmntok_t *,
									 void *),
					 struct command_result *(*finalcb)(struct command *,
									   void *),
					 void *arg)
{
	struct request_batch *batch = tal(ctx, struct request_batch);

	batch->num_remaining = 0;
	batch->cb = cb;
	batch->errcb = errcb;
	batch->finalcb = finalcb;
	batch->arg = arg;
	return batch;
}

static struct command_result *batch_one_complete(struct command *cmd,
						 struct request_batch *batch)
{
	void *arg;
	struct command_result *(*finalcb)(struct command *, void *);

	assert(batch->num_remaining);

	if (--batch->num_remaining != 0)
		return command_still_pending(cmd);

	arg = batch->arg;
	finalcb = batch->finalcb;
	tal_free(batch);
	return finalcb(cmd, arg);
}

static struct command_result *batch_one_success(struct command *cmd,
						const char *method,
						const char *buf,
						const jsmntok_t *result,
						struct request_batch *batch)
{
	/* If this frees stuff (e.g. fails), just return */
	if (batch->cb && batch->cb(cmd, method, buf, result, batch->arg) == &complete)
		return &complete;
	return batch_one_complete(cmd, batch);
}

static struct command_result *batch_one_failed(struct command *cmd,
					       const char *method,
					       const char *buf,
					       const jsmntok_t *result,
					       struct request_batch *batch)
{
	/* If this frees stuff (e.g. fails), just return */
	if (batch->errcb && batch->errcb(cmd, method, buf, result, batch->arg) == &complete)
		return &complete;
	return batch_one_complete(cmd, batch);
}

struct out_req *add_to_batch(struct command *cmd,
			     struct request_batch *batch,
			     const char *cmdname)
{
	batch->num_remaining++;

	return jsonrpc_request_start(cmd, cmdname,
				     batch_one_success,
				     batch_one_failed,
				     batch);
}

/* Runs finalcb immediately if batch is empty. */
struct command_result *batch_done(struct command *cmd,
				  struct request_batch *batch)
{
	/* Same path as completion */
	batch->num_remaining++;
	return batch_one_complete(cmd, batch);
}

static void json_add_deprecated(struct json_stream *js,
				const char *fieldname,
				const char *depr_start, const char *depr_end)
{
	if (!depr_start)
		return;
	json_array_start(js, fieldname);
	json_add_string(js, NULL, depr_start);
	if (depr_end)
		json_add_string(js, NULL, depr_end);
	json_array_end(js);
}

static struct command_result *
handle_getmanifest(struct command *getmanifest_cmd,
		   const char *buf,
		   const jsmntok_t *getmanifest_params)
{
	struct json_stream *params = jsonrpc_stream_success(getmanifest_cmd);
	struct plugin *p = getmanifest_cmd->plugin;
	bool has_shutdown_notif;

	if (json_scan(tmpctx, buf, getmanifest_params,
		      "{allow-deprecated-apis:%}",
		      JSON_SCAN(json_to_bool, &p->deprecated_ok)) != NULL) {
		plugin_err(p, "Invalid allow-deprecated-apis in '%.*s'",
			   json_tok_full_len(getmanifest_params),
			   json_tok_full(buf, getmanifest_params));
	}

	json_array_start(params, "options");
	for (size_t i = 0; i < tal_count(p->opts); i++) {
		if (p->opts[i].dev_only && !p->developer)
			continue;
		json_object_start(params, NULL);
		json_add_string(params, "name", p->opts[i].name);
		json_add_string(params, "type", p->opts[i].type);
		json_add_string(params, "description", p->opts[i].description);
		json_add_deprecated(params, "deprecated", p->opts[i].depr_start, p->opts[i].depr_end);
		json_add_bool(params, "dynamic", p->opts[i].dynamic);
		json_add_bool(params, "multi", p->opts[i].multi);
		if (p->opts[i].jsonfmt)
			p->opts[i].jsonfmt(getmanifest_cmd, params, "default", p->opts[i].arg);
		json_object_end(params);
	}
	json_array_end(params);

	json_array_start(params, "rpcmethods");
	for (size_t i = 0; i < p->num_commands; i++) {
		if (p->commands[i].dev_only && !p->developer)
			continue;
		json_object_start(params, NULL);
		json_add_string(params, "name", p->commands[i].name);
		json_add_string(params, "usage",
				strmap_get(&p->usagemap, p->commands[i].name));
		json_add_deprecated(params, "deprecated",
				    p->commands[i].depr_start, p->commands[i].depr_end);
		json_object_end(params);
	}
	json_array_end(params);

	json_array_start(params, "subscriptions");
	has_shutdown_notif = false;
	for (size_t i = 0; i < p->num_notif_subs; i++) {
		json_add_string(params, NULL, p->notif_subs[i].name);
		if (streq(p->notif_subs[i].name, "shutdown"))
			has_shutdown_notif = true;
	}
	/* For memleak detection, always get notified of shutdown. */
	if (!has_shutdown_notif && p->developer)
		json_add_string(params, NULL, "shutdown");
	json_add_string(params, NULL, "deprecated_oneshot");
	json_array_end(params);

	json_array_start(params, "hooks");
	for (size_t i = 0; i < p->num_hook_subs; i++) {
		json_object_start(params, NULL);
		json_add_string(params, "name", p->hook_subs[i].name);
		if (p->hook_subs[i].before) {
			json_array_start(params, "before");
			for (size_t j = 0; p->hook_subs[i].before[j]; j++)
				json_add_string(params, NULL,
						p->hook_subs[i].before[j]);
			json_array_end(params);
		}
		if (p->hook_subs[i].after) {
			json_array_start(params, "after");
			for (size_t j = 0; p->hook_subs[i].after[j]; j++)
				json_add_string(params, NULL,
						p->hook_subs[i].after[j]);
			json_array_end(params);
		}
		if (p->hook_subs[i].num_strfilters) {
			json_array_start(params, "filters");
			for (size_t j = 0; j < p->hook_subs[i].num_strfilters; j++)
				json_add_string(params, NULL,
						p->hook_subs[i].strfilters[j]);
			json_array_end(params);
		} else if (p->hook_subs[i].num_intfilters) {
			json_array_start(params, "filters");
			for (size_t j = 0; j < p->hook_subs[i].num_intfilters; j++)
				json_add_u64(params, NULL,
					     p->hook_subs[i].intfilters[j]);
			json_array_end(params);
		}
		json_object_end(params);
	}
	json_array_end(params);

	if (p->desired_features != NULL) {
		json_object_start(params, "featurebits");
		for (size_t i = 0; i < NUM_FEATURE_PLACE; i++) {
			u8 *f = p->desired_features->bits[i];
			const char *fieldname = feature_place_names[i];
			if (fieldname == NULL)
				continue;
			json_add_hex(params, fieldname, f, tal_bytelen(f));
		}
		json_object_end(params);
	}

	json_add_bool(params, "dynamic", p->restartability == PLUGIN_RESTARTABLE);
	json_add_bool(params, "nonnumericids", true);
	json_add_bool(params, "cancheck", true);

	json_array_start(params, "notifications");
	for (size_t i = 0; p->notif_topics && i < p->num_notif_topics; i++) {
		json_object_start(params, NULL);
		json_add_string(params, "method", p->notif_topics[i]);
		json_object_end(params);
	}
	json_array_end(params);

	return command_finished(getmanifest_cmd, params);
}

static void rpc_conn_finished(struct io_conn *conn,
			      struct plugin *plugin)
{
	plugin_err(plugin, "Lost connection to the RPC socket.");
}


static struct io_plan *rpc_conn_read_response(struct io_conn *conn,
					      struct plugin *plugin)
{
	const tal_t *working_ctx = tal(NULL, char);

	/* Gather an parse any new bytes */
	for (;;) {
		const jsmntok_t *toks;
		const char *buf;
		const char *err;

		err = jsonrpc_io_parse(tmpctx,
				       plugin->jsonrpc_in,
				       &toks, &buf);
		if (err)
			plugin_err(plugin, "%s", err);

		if (!toks)
			break;

		handle_rpc_reply(working_ctx, plugin, buf, toks);
		jsonrpc_io_parse_done(plugin->jsonrpc_in);
	}

	/* Explicitly free any expired requests now; xpay uses this to
	 * fire more commands! */
	tal_free(working_ctx);

	/* Read more */
	return jsonrpc_io_read(conn, plugin->jsonrpc_in,
			       rpc_conn_read_response,
			       plugin);
}

static struct io_plan *rpc_conn_write_request(struct io_conn *conn,
					      struct plugin *plugin);

static struct io_plan *
rpc_stream_complete(struct io_conn *conn, struct json_stream *js,
		    struct plugin *plugin)
{
	struct jstream *jstr = list_pop(&plugin->rpc_js_list, struct jstream, list);
	assert(jstr);
	assert(jstr->js == js);
	tal_free(jstr);

	return rpc_conn_write_request(conn, plugin);
}

static struct io_plan *rpc_conn_write_request(struct io_conn *conn,
					      struct plugin *plugin)
{
	struct jstream *jstr = list_top(&plugin->rpc_js_list, struct jstream, list);
	if (jstr)
		return json_stream_output(jstr->js, conn,
					  rpc_stream_complete, plugin);

	return io_out_wait(conn, plugin->io_rpc_conn,
			   rpc_conn_write_request, plugin);
}

static struct io_plan *rpc_conn_init(struct io_conn *conn,
				     struct plugin *plugin)
{
	plugin->io_rpc_conn = conn;
	io_set_finish(conn, rpc_conn_finished, plugin);
	return io_duplex(conn,
			 rpc_conn_read_response(conn, plugin),
			 rpc_conn_write_request(conn, plugin));
}

static struct feature_set *json_to_feature_set(struct plugin *plugin,
					       const char *buf,
					       const jsmntok_t *features)
{
	struct feature_set *fset = talz(plugin, struct feature_set);
	const jsmntok_t *t;
	size_t i;

	json_for_each_obj(i, t, features) {
		enum feature_place p;
		if (json_tok_streq(buf, t, "init"))
			p = INIT_FEATURE;
		else if (json_tok_streq(buf, t, "node"))
			p = NODE_ANNOUNCE_FEATURE;
		else if (json_tok_streq(buf, t, "channel"))
			p = CHANNEL_FEATURE;
		else if (json_tok_streq(buf, t, "invoice"))
			p = BOLT11_FEATURE;
		else if (json_tok_streq(buf, t, "bolt12_offer"))
			p = BOLT12_OFFER_FEATURE;
		else if (json_tok_streq(buf, t, "bolt12_invreq"))
			p = BOLT12_INVREQ_FEATURE;
		else if (json_tok_streq(buf, t, "bolt12_invoice"))
			p = BOLT12_INVOICE_FEATURE;
		else
			continue;
		fset->bits[p] = json_tok_bin_from_hex(fset, buf, t + 1);
	}
	return fset;
}

static struct plugin_option *find_opt(struct plugin *plugin, const char *name)
{
	for (size_t i = 0; i < tal_count(plugin->opts); i++) {
		if (streq(plugin->opts[i].name, name))
			return &plugin->opts[i];
	}
	return NULL;
}

static const char **json_to_apilist(const tal_t *ctx, const char *buffer, const jsmntok_t *tok)
{
	size_t i;
	const jsmntok_t *t;
	const char **ret = tal_arr(ctx, const char *, tok->size);

	json_for_each_arr(i, t, tok)
		ret[i] = json_strdup(ret, buffer, t);

	return ret;
}

static struct command_result *get_beglist(struct command *aux_cmd,
					  const char *method,
					  const char *buf,
					  const jsmntok_t *result,
					  void *unused)
{
	struct plugin *plugin = aux_cmd->plugin;
	const char *err;

	err = json_scan(tmpctx, buf, result,
			"{configs:{i-promise-to-fix-broken-api-user:{values_str:%}}}",
			JSON_SCAN_TAL(plugin, json_to_apilist, &plugin->beglist));
	if (err)
		plugin_err(aux_cmd->plugin, "bad listconfigs '%.*s': %s",
			   json_tok_full_len(result),
			   json_tok_full(buf, result),
			   err);

	return aux_command_done(aux_cmd);
}

static struct command_result *handle_init(struct command *cmd,
					  const char *buf,
					  const jsmntok_t *params)
{
	const jsmntok_t *configtok, *opttok, *t;
	size_t i;
	char *dir, *network;
	struct plugin *p = cmd->plugin;
	const char *err;

	configtok = json_get_member(buf, params, "configuration");
	err = json_scan(tmpctx, buf, configtok,
			"{lightning-dir:%"
			",network:%"
			",feature_set:%"
			",rpc-file:%}",
			JSON_SCAN_TAL(tmpctx, json_strdup, &dir),
			JSON_SCAN_TAL(tmpctx, json_strdup, &network),
			JSON_SCAN_TAL(p, json_to_feature_set, &p->our_features),
			JSON_SCAN_TAL(p, json_strdup, &p->rpc_location));
	if (err)
		plugin_err(p, "cannot scan init params: %s: %.*s",
			   err, json_tok_full_len(params),
			   json_tok_full(buf, params));

	/* Move into lightning directory: other files are relative */
	if (chdir(dir) != 0)
		plugin_err(p, "chdir to %s: %s", dir, strerror(errno));

	chainparams = chainparams_for_network(network);

	/* Only attempt to connect if the plugin has configured the rpc_conn
	 * already, if that's not the case we were told to run without an RPC
	 * connection, so don't even log an error. */
	if (p->sync_io)
		p->sync_fd = rpc_open(p);
	else
		p->sync_fd = -1;

	opttok = json_get_member(buf, params, "options");
	json_for_each_obj(i, t, opttok) {
		const char *name, *problem;
		struct plugin_option *popt;

		name = json_strdup(tmpctx, buf, t);
		popt = find_opt(p, name);
		if (!popt)
			plugin_err(p, "lightningd specified unknown option '%s'?", name);

		if (popt->multi) {
			size_t j;
			const jsmntok_t *opt;
			json_for_each_arr(j, opt, t+1) {
				problem = popt->handle(cmd, json_strdup(tmpctx, buf, opt), false, popt->arg);
				if (problem)
					plugin_err(p, "option '%s': %s", popt->name, problem);
			}
		} else {
			problem = popt->handle(cmd, json_strdup(tmpctx, buf, t+1), false, popt->arg);
			if (problem)
				plugin_err(p, "option '%s': %s", popt->name, problem);
		}
	}

	if (p->init) {
		const char *disable = p->init(cmd, buf, configtok);
		if (disable)
			return command_success(cmd, json_out_obj(cmd, "disable",
								 disable));
	}

	/* Now set up async. */
	if (p->sync_fd != -1) {
		struct out_req *req;
		struct command *aux_cmd = aux_command(cmd);

		io_new_conn(p, p->sync_fd, rpc_conn_init, p);
		/* In case they intercept rpc_command, we can't do this sync. */
		req = jsonrpc_request_start(aux_cmd, "listconfigs",
					    get_beglist, plugin_broken_cb, NULL);
		json_add_string(req->js, "config", "i-promise-to-fix-broken-api-user");
		send_outreq(req);

		/* We will open a new one if we want to be sync. */
		p->sync_fd = -1;
	}

	return command_success(cmd, json_out_obj(cmd, NULL, NULL));
}

char *u64_option(struct command *cmd, const char *arg, bool check_only, u64 *i)
{
	char *endp;
	u64 v;

	/* This is how the manpage says to do it.  Yech. */
	errno = 0;
	v = strtoul(arg, &endp, 0);
	if (*endp || !arg[0])
		return tal_fmt(tmpctx, "'%s' is not a number", arg);
	if (errno)
		return tal_fmt(tmpctx, "'%s' is out of range", arg);
	if (!check_only)
		*i = v;
	return NULL;
}

char *u32_option(struct command *cmd, const char *arg, bool check_only, u32 *i)
{
	u64 n;
	char *problem = u64_option(cmd, arg, false, &n);

	if (problem)
		return problem;

	if ((u32)n != n)
		return tal_fmt(tmpctx, "'%s' is too large (overflow)", arg);

	if (!check_only)
		*i = n;
	return NULL;
}

char *u16_option(struct command *cmd, const char *arg, bool check_only, u16 *i)
{
	u64 n;
	char *problem = u64_option(cmd, arg, false, &n);

	if (problem)
		return problem;

	if ((u16)n != n)
		return tal_fmt(tmpctx, "'%s' is too large (overflow)", arg);

	if (!check_only)
		*i = n;
	return NULL;
}

char *bool_option(struct command *cmd, const char *arg, bool check_only, bool *i)
{
	if (!streq(arg, "true") && !streq(arg, "false"))
		return tal_fmt(tmpctx, "'%s' is not a bool, must be \"true\" or \"false\"", arg);

	if (!check_only)
		*i = streq(arg, "true");
	return NULL;
}

char *flag_option(struct command *cmd, const char *arg, bool check_only, bool *i)
{
	/* We only get called if the flag was provided, so *i should be false
	 * by default */
	assert(check_only || *i == false);
	if (!streq(arg, "true"))
		return tal_fmt(tmpctx, "Invalid argument '%s' passed to a flag", arg);

	if (!check_only)
		*i = true;
	return NULL;
}

char *charp_option(struct command *cmd, const char *arg, bool check_only, char **p)
{
	if (!check_only)
		*p = tal_strdup(NULL, arg);
	return NULL;
}

char *multi_string_option(struct command *cmd, const char *arg, bool check_only,
			  const char ***arr)
{
	if (!check_only)
		tal_arr_expand(arr, tal_strdup(*arr, arg));
	return NULL;
}

bool u64_jsonfmt(struct command *cmd, struct json_stream *js, const char *fieldname, u64 *i)
{
	json_add_u64(js, fieldname, *i);
	return true;
}

bool u32_jsonfmt(struct command *cmd, struct json_stream *js, const char *fieldname, u32 *i)
{
	json_add_u32(js, fieldname, *i);
	return true;
}

bool u16_jsonfmt(struct command *cmd, struct json_stream *js, const char *fieldname, u16 *i)
{
	json_add_u32(js, fieldname, *i);
	return true;
}

bool bool_jsonfmt(struct command *cmd, struct json_stream *js, const char *fieldname, bool *i)
{
	json_add_bool(js, fieldname, *i);
	return true;
}

bool charp_jsonfmt(struct command *cmd, struct json_stream *js, const char *fieldname, char **p)
{
	if (!*p)
		return false;
	json_add_string(js, fieldname, *p);
	return true;
}

bool string_array_jsonfmt(struct command *cmd, struct json_stream *js,
			  const char *fieldname, const char ***arr)
{
	if (tal_count(*arr) == 0)
		return false;
	json_array_start(js, fieldname);
	for (size_t i = 0; i < tal_count(*arr); i++)
		json_add_string(js, NULL, (*arr)[i]);
	json_array_end(js);
	return true;
}

bool flag_jsonfmt(struct command *cmd, struct json_stream *js, const char *fieldname, bool *i)
{
	/* Don't print if the default (false) */
	if (!*i)
		return false;
	return bool_jsonfmt(cmd, js, fieldname, i);
}

static void setup_command_usage(struct plugin *p)
{
	struct command *usage_cmd = new_command(tmpctx, p, "usage",
						"check-usage",
						COMMAND_TYPE_USAGE_ONLY);

	/* This is how common/param can tell it's just a usage request */
	for (size_t i = 0; i < p->num_commands; i++) {
		struct command_result *res;

		usage_cmd->methodname = p->commands[i].name;
		res = p->commands[i].handle(usage_cmd, NULL, NULL);
		assert(res == &complete);
		assert(strmap_get(&p->usagemap, p->commands[i].name));
	}
}

static void call_plugin_timer(struct plugin *p, struct timer *timer)
{
	struct plugin_timer *t = container_of(timer, struct plugin_timer, timer);
	struct command *timer_cmd;
	struct command_result *res;
	bool cmd_freed = false;

	/* This *isn't* owned by timer, which is owned by original command,
	 * since they may free that in callback */
	timer_cmd = new_command(p, p, t->id, "timer", COMMAND_TYPE_TIMER);
	tal_add_destructor2(timer_cmd, destroy_cmd_mark_freed, &cmd_freed);

	res = t->cb(timer_cmd, t->cb_arg);
	if (res == &pending) {
		assert(!cmd_freed);
		tal_del_destructor2(timer_cmd, destroy_cmd_mark_freed,
				    &cmd_freed);
	} else {
		assert(res == &complete);
		assert(cmd_freed);
	}
}

static void destroy_plugin_timer(struct plugin_timer *timer, struct plugin *p)
{
	timer_del(&p->timers, &timer->timer);
}

static struct plugin_timer *new_timer(const tal_t *ctx,
				      struct plugin *p,
				      const char *id TAKES,
				      struct timerel t,
				      struct command_result *(*cb)(struct command *, void *),
				      void *cb_arg)
{
	struct plugin_timer *timer = notleak(tal(ctx, struct plugin_timer));
	timer->id = tal_strdup(timer, id);
	timer->cb = cb;
	timer->cb_arg = cb_arg;
	timer_init(&timer->timer);
	timer_addrel(&p->timers, &timer->timer, t);
	tal_add_destructor2(timer, destroy_plugin_timer, p);
	return timer;
}

struct plugin_timer *global_timer_(struct plugin *p,
				   struct timerel t,
				   struct command_result *(*cb)(struct command *cmd, void *cb_arg),
				   void *cb_arg)
{
	return new_timer(p, p, "timer", t, cb, cb_arg);
}

struct plugin_timer *command_timer_(struct command *cmd,
				    struct timerel t,
				    struct command_result *(*cb)(struct command *cmd, void *cb_arg),
				    void *cb_arg)
{
	return new_timer(cmd, cmd->plugin,
			 take(tal_fmt(NULL, "%s-timer", cmd->id)),
			 t, cb, cb_arg);
}

void plugin_logv(struct plugin *p, enum log_level l,
		 const char *fmt, va_list ap)
{
	struct json_stream *js = new_json_stream(NULL, NULL, NULL);

	json_object_start(js, NULL);
	json_add_string(js, "jsonrpc", "2.0");
	json_add_string(js, "method", "log");

	json_object_start(js, "params");
	json_add_string(js, "level",
			l == LOG_TRACE ? "trace"
			: l == LOG_DBG ? "debug"
			: l == LOG_INFORM ? "info"
			: l == LOG_UNUSUAL ? "warn"
			: l == LOG_TRACE ? "trace"
			: "error");
	json_out_addv(js->jout, "message", true, fmt, ap);
	json_object_end(js);

	jsonrpc_finish_and_send(p, js);
}

void plugin_gossmap_logcb(struct plugin *plugin,
			  enum log_level level,
			  const char *fmt,
			  ...)
{
	va_list ap;

	va_start(ap, fmt);
	plugin_logv(plugin, level, fmt, ap);
	va_end(ap);
}

struct json_stream *plugin_notification_start_obs(const tal_t *ctx,
						  const char *method)
{
	struct json_stream *js = new_json_stream(ctx, NULL, NULL);

	json_object_start(js, NULL);
	json_add_string(js, "jsonrpc", "2.0");
	json_add_string(js, "method", method);

	json_object_start(js, "params");
	return js;
}

void plugin_notification_end_obs(struct plugin *plugin,
				 struct json_stream *stream STEALS)
{
	json_object_end(stream);
	jsonrpc_finish_and_send(plugin, stream);
}

struct json_stream *plugin_notification_start(const tal_t *ctx,
					      const char *method)
{
	struct json_stream *js = plugin_notification_start_obs(ctx, method);
	json_object_start(js, method);
	return js;
}

void plugin_notification_end(struct plugin *plugin,
			     struct json_stream *stream STEALS)
{
	json_object_end(stream);
	plugin_notification_end_obs(plugin, stream);
}

struct json_stream *plugin_notify_start(struct command *cmd, const char *method)
{
	struct json_stream *js = new_json_stream(cmd, NULL, NULL);

	json_object_start(js, NULL);
	json_add_string(js, "jsonrpc", "2.0");
	json_add_string(js, "method", method);

	json_object_start(js, "params");
	json_add_id(js, cmd->id);

	return js;
}

void plugin_notify_end(struct command *cmd, struct json_stream *js)
{
	json_object_end(js);

	jsonrpc_finish_and_send(cmd->plugin, js);
}

/* Convenience wrapper for notify with "message" */
void plugin_notify_message(struct command *cmd,
			   enum log_level level,
			   const char *fmt, ...)
{
	va_list ap;
	struct json_stream *js;
	const char *msg;

	va_start(ap, fmt);
	msg = tal_vfmt(tmpctx, fmt, ap);
	va_end(ap);

	/* Also log, debug level */
	plugin_log(cmd->plugin, LOG_DBG, "notify msg %s: %s",
		   log_level_name(level), msg);

	js = plugin_notify_start(cmd, "message");
	json_add_string(js, "level", log_level_name(level));

	/* In case we're OOM */
	if (js->jout)
		json_out_addstr(js->jout, "message", msg);

	plugin_notify_end(cmd, js);
}

void plugin_notify_progress(struct command *cmd,
			    u32 num_stages, u32 stage,
			    u32 num_progress, u32 progress)
{
	struct json_stream *js = plugin_notify_start(cmd, "progress");

	assert(progress < num_progress);
	json_add_u32(js, "num", progress);
	json_add_u32(js, "total", num_progress);
	if (num_stages > 0) {
		assert(stage < num_stages);
		json_object_start(js, "stage");
		json_add_u32(js, "num", stage);
		json_add_u32(js, "total", num_stages);
		json_object_end(js);
	}
	plugin_notify_end(cmd, js);
}

void NORETURN plugin_exit(struct plugin *p, int exitcode)
{
	p->exiting = true;
	io_conn_out_exclusive(p->stdout_conn, true);
	io_wake(p);
	io_loop(NULL, NULL);
	exit(exitcode);
}

void NORETURN plugin_errv(struct plugin *p, const char *fmt, va_list ap)
{
	va_list ap2;

	/* In case it gets consumed, make a copy. */
	va_copy(ap2, ap);

	plugin_logv(p, LOG_BROKEN, fmt, ap);
	vfprintf(stderr, fmt, ap2);
	fprintf(stderr, "\n");
	plugin_exit(p, 1);
	va_end(ap2);
}

void NORETURN plugin_err(struct plugin *p, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	plugin_errv(p, fmt, ap);
	va_end(ap);
}

void plugin_log(struct plugin *p, enum log_level l, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	plugin_logv(p, l, fmt, ap);
	va_end(ap);
}

static void PRINTF_FMT(2,3) log_memleak(struct plugin *plugin, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	plugin_logv(plugin, LOG_BROKEN, fmt, ap);
	va_end(ap);
}

static void memleak_check(struct plugin *plugin, struct command *cmd)
{
	struct htable *memtable;

	memtable = memleak_start(tmpctx);

	/* cmd in use right now */
	memleak_ptr(memtable, cmd);
	memleak_ignore_children(memtable, cmd);

	/* Now delete plugin and anything it has pointers to. */
	memleak_scan_obj(memtable, plugin);

	/* Memleak needs some help to see into strmaps */
	memleak_scan_strmap(memtable, &plugin->out_reqs);

	/* We know usage strings are referred to. */
	memleak_scan_strmap(memtable, &cmd->plugin->usagemap);

	if (plugin->mark_mem)
		plugin->mark_mem(plugin, memtable);

	dump_memleak(memtable, log_memleak, plugin);
}

void plugin_set_memleak_handler(struct plugin *plugin,
				void (*mark_mem)(struct plugin *plugin,
						 struct htable *memtable))
{
	if (plugin->developer)
		plugin->mark_mem = mark_mem;
}

bool command_deprecated_ok_flag(const struct command *cmd)
{
	if (cmd->plugin->deprecated_ok_override)
		return *cmd->plugin->deprecated_ok_override;
	return cmd->plugin->deprecated_ok;
}

static struct command_result *param_tok(struct command *cmd, const char *name,
					const char *buffer, const jsmntok_t * tok,
					const jsmntok_t **out)
{
	*out = tok;
	return NULL;
}

static void ld_command_handle(struct plugin *plugin,
			      const char *buffer,
			      const jsmntok_t *toks)
{
	const jsmntok_t *methtok, *paramstok, *filtertok;
	const char *methodname;
	struct command *cmd;
	const char *id;
	enum command_type type;

	methtok = json_get_member(buffer, toks, "method");
	paramstok = json_get_member(buffer, toks, "params");
	filtertok = json_get_member(buffer, toks, "filter");

	if (!methtok || !paramstok)
		plugin_err(plugin, "Malformed JSON-RPC notification missing "
			   "\"method\" or \"params\": %.*s",
			   json_tok_full_len(toks),
			   json_tok_full(buffer, toks));

	methodname = json_strdup(NULL, buffer, methtok);
	id = json_get_id(tmpctx, buffer, toks);

	if (!id)
		type = COMMAND_TYPE_NOTIFICATION;
	else if (streq(methodname, "check"))
		type = COMMAND_TYPE_CHECK;
	else
		type = COMMAND_TYPE_NORMAL;

	cmd = new_command(plugin, plugin,
			  id ? id : tal_fmt(tmpctx, "notification-%s", methodname),
			  take(methodname),
			  type);

	if (!plugin->manifested) {
		if (streq(cmd->methodname, "getmanifest")) {
			handle_getmanifest(cmd, buffer, paramstok);
			plugin->manifested = true;
			return;
		}
		plugin_err(plugin, "Did not receive 'getmanifest' yet, but got '%s'"
			   " instead", cmd->methodname);
	}

	if (!plugin->initialized) {
		if (streq(cmd->methodname, "init")) {
			handle_init(cmd, buffer, paramstok);
			plugin->initialized = true;
			return;
		}
		plugin_err(plugin, "Did not receive 'init' yet, but got '%s'"
			   " instead", cmd->methodname);
	}

	/* If that's a notification. */
	if (cmd->type == COMMAND_TYPE_NOTIFICATION) {
		bool is_shutdown = streq(cmd->methodname, "shutdown");
		if (is_shutdown && plugin->developer)
			memleak_check(plugin, cmd);

		if (streq(cmd->methodname, "deprecated_oneshot")) {
			const char *err;

			plugin->deprecated_ok_override = tal(plugin, bool);
			err = json_scan(tmpctx, buffer, paramstok,
					"{deprecated_oneshot:{deprecated_ok:%}}",
					JSON_SCAN(json_to_bool,
						  plugin->deprecated_ok_override));
			if (err)
				plugin_err(plugin, "Parsing deprecated_oneshot notification: %s", err);
			return;
		}
		for (size_t i = 0; i < plugin->num_notif_subs; i++) {
			if (streq(cmd->methodname,
				  plugin->notif_subs[i].name)
			    || is_asterix_notification(cmd->methodname,
						       plugin->notif_subs[i].name)) {
				plugin->notif_subs[i].handle(cmd,
							     buffer,
							     paramstok);
				return;
			}
		}

		/* We subscribe them to this always */
		if (is_shutdown && plugin->developer)
			plugin_exit(plugin, 0);

		plugin_err(plugin, "Unregistered notification %.*s",
			   json_tok_full_len(methtok),
			   json_tok_full(buffer, methtok));
	}

	for (size_t i = 0; i < plugin->num_hook_subs; i++) {
		if (streq(cmd->methodname, plugin->hook_subs[i].name)) {
			cmd->type = COMMAND_TYPE_HOOK;
			plugin->hook_subs[i].handle(cmd,
						    buffer,
						    paramstok);
			return;
		}
	}

	if (filtertok) {
		/* On error, this fails cmd */
		if (parse_filter(cmd, "filter", buffer, filtertok)
		    != NULL)
			return;
	}

	/* Is this actually a check command? */
	if (cmd->type == COMMAND_TYPE_CHECK) {
		const jsmntok_t *method;
		jsmntok_t *mod_params;

		/* We're going to mangle it, so make a copy */
		mod_params = json_tok_copy(cmd, paramstok);
		if (!param_check(cmd, buffer, mod_params,
				 p_req("command_to_check", param_tok, &method),
				 p_opt_any(),
				 NULL)) {
			plugin_err(plugin,
				   "lightningd check without command_to_check: %.*s",
				   json_tok_full_len(toks),
				   json_tok_full(buffer, toks));
		}
		tal_free(cmd->methodname);
		cmd->methodname = json_strdup(cmd, buffer, method);

		/* Point method to the name, not the value */
		if (mod_params->type == JSMN_OBJECT)
			method--;

		json_tok_remove(&mod_params, mod_params, method, 1);
		paramstok = mod_params;
	}

	for (size_t i = 0; i < plugin->num_commands; i++) {
		if (streq(cmd->methodname, plugin->commands[i].name)) {
			plugin->commands[i].handle(cmd,
						   buffer,
						   paramstok);
			/* Reset this */
			plugin->deprecated_ok_override
				= tal_free(plugin->deprecated_ok_override);
			return;
		}
	}

	/* Dynamic parameters */
	if (streq(cmd->methodname, "setconfig")) {
		const jsmntok_t *valtok;
		const char *config, *val, *problem;
		struct plugin_option *popt;
		struct command_result *ret;
		bool check_only;

		config = json_strdup(tmpctx, buffer,
				     json_get_member(buffer, paramstok, "config"));
		popt = find_opt(plugin, config);
		if (!popt) {
			plugin_err(plugin,
				   "lightningd setconfig unknown option '%s'?",
				   config);
		}
		if (!popt->dynamic) {
			plugin_err(plugin,
				   "lightningd setconfig non-dynamic option '%s'?",
				   config);
		}

		check_only = command_check_only(cmd);
		plugin_log(plugin, LOG_DBG, "setconfig %s check_only=%i", config, check_only);

		valtok = json_get_member(buffer, paramstok, "val");
		if (valtok)
			val = json_strdup(tmpctx, buffer, valtok);
		else
			val = "true";

		problem = popt->handle(cmd, val, check_only, popt->arg);
		if (problem)
			ret = command_fail(cmd, JSONRPC2_INVALID_PARAMS,
					   "%s", problem);
		else {
			if (check_only)
				ret = command_check_done(cmd);
			else
				ret = command_finished(cmd, jsonrpc_stream_success(cmd));
		}
		assert(ret == &complete);
		return;
	}

	plugin_err(plugin, "Unknown command '%s'", cmd->methodname);
}

static struct io_plan *ld_read_json(struct io_conn *conn,
				    struct plugin *plugin)
{
	/* Gather an parse any new bytes */
	for (;;) {
		const jsmntok_t *toks;
		const char *buf;
		const char *err;

		err = jsonrpc_io_parse(tmpctx,
				       plugin->lightningd_in,
				       &toks, &buf);
		if (err)
			plugin_err(plugin, "%s", err);

		if (!toks)
			break;

		ld_command_handle(plugin, buf, toks);
		jsonrpc_io_parse_done(plugin->lightningd_in);
	}

	/* Read more */
	return jsonrpc_io_read(conn, plugin->lightningd_in,
				  ld_read_json, plugin);
}

static struct io_plan *ld_write_json(struct io_conn *conn,
				     struct plugin *plugin);

static struct io_plan *
ld_stream_complete(struct io_conn *conn, struct json_stream *js,
		   struct plugin *plugin)
{
	struct jstream *jstr = list_pop(&plugin->js_list, struct jstream, list);
	assert(jstr);
	assert(jstr->js == js);
	tal_free(jstr);

	return ld_write_json(conn, plugin);
}

static struct io_plan *ld_write_json(struct io_conn *conn,
				     struct plugin *plugin)
{
	struct jstream *jstr = list_top(&plugin->js_list, struct jstream, list);
	if (jstr)
		return json_stream_output(jstr->js, plugin->stdout_conn,
					  ld_stream_complete, plugin);

	/* If we were simply flushing final output, stop now. */
	if (plugin->exiting)
		io_break(plugin);
	return io_out_wait(conn, plugin, ld_write_json, plugin);
}

static void ld_conn_finish(struct io_conn *conn, struct plugin *plugin)
{
	/* Without one of the conns there is no reason to stay alive. That
	 * certainly means lightningd died, since there is no cleaner way
	 * to stop, return 0. */
	exit(0);
}

/* lightningd writes on our stdin */
static struct io_plan *stdin_conn_init(struct io_conn *conn,
				       struct plugin *plugin)
{
	plugin->stdin_conn = conn;
	io_set_finish(conn, ld_conn_finish, plugin);
	return ld_read_json(conn, plugin);
}

/* lightningd reads from our stdout */
static struct io_plan *stdout_conn_init(struct io_conn *conn,
                                        struct plugin *plugin)
{
	plugin->stdout_conn = conn;
	io_set_finish(conn, ld_conn_finish, plugin);
	return io_wait(plugin->stdout_conn, plugin, ld_write_json, plugin);
}

static struct plugin *new_plugin(const tal_t *ctx,
				 const char *argv0,
				 bool developer,
				 const char *(*init)(struct command *init_cmd,
						     const char *buf,
						     const jsmntok_t *),
				 const enum plugin_restartability restartability,
				 bool init_rpc,
				 struct feature_set *features STEALS,
				 const struct plugin_command *commands TAKES,
				 size_t num_commands,
				 const struct plugin_notification *notif_subs TAKES,
				 size_t num_notif_subs,
				 const struct plugin_hook *hook_subs TAKES,
				 size_t num_hook_subs,
				 const char **notif_topics TAKES,
				 size_t num_notif_topics,
				 va_list ap)
{
	const char *optname;
	struct plugin *p = tal(ctx, struct plugin);
	char *name;

	/* id is our name, without extension (not that we expect any, in C!) */
	name = path_basename(p, argv0);
	name[path_ext_off(name)] = '\0';
	p->id = name;
	p->developer = developer;
	p->deprecated_ok_override = NULL;
	p->lightningd_in = jsonrpc_io_new(p);
	list_head_init(&p->js_list);
	/* Async RPC */
	p->jsonrpc_in = jsonrpc_io_new(p);
	list_head_init(&p->rpc_js_list);
	p->io_rpc_conn = NULL;
	p->next_outreq_id = 0;
	strmap_init(&p->out_reqs);
	p->beglist = NULL;

	p->desired_features = tal_steal(p, features);
	if (init_rpc)
		p->sync_io = jsonrpc_io_new(p);
	else
		p->sync_io = NULL;
	p->init = init;
	p->manifested = p->initialized = p->exiting = false;
	p->restartability = restartability;
	strmap_init(&p->usagemap);

	p->commands = commands;
	if (taken(commands))
		tal_steal(p, commands);
	p->num_commands = num_commands;
	p->notif_topics = notif_topics;
	if (taken(notif_topics))
		tal_steal(p, notif_topics);
	p->num_notif_topics = num_notif_topics;
	p->notif_subs = notif_subs;
	if (taken(notif_subs))
		tal_steal(p, notif_subs);
	p->num_notif_subs = num_notif_subs;
	p->hook_subs = hook_subs;
	if (taken(hook_subs))
		tal_steal(p, hook_subs);
	p->num_hook_subs = num_hook_subs;
	p->opts = tal_arr(p, struct plugin_option, 0);

	while ((optname = va_arg(ap, const char *)) != NULL) {
		struct plugin_option o;
		o.name = optname;
		o.type = va_arg(ap, const char *);
		o.description = va_arg(ap, const char *);
		o.handle = va_arg(ap, char *(*)(struct command *, const char *str, bool check_only, void *arg));
		o.jsonfmt = va_arg(ap, bool (*)(struct command *, struct json_stream *, const char *, void *arg));
		o.arg = va_arg(ap, void *);
		o.dev_only = va_arg(ap, int); /* bool gets promoted! */
		o.depr_start = va_arg(ap, const char *);
		o.depr_end = va_arg(ap, const char *);
		o.dynamic = va_arg(ap, int); /* bool gets promoted! */
		o.multi = va_arg(ap, int); /* bool gets promoted! */
		tal_arr_expand(&p->opts, o);
	}

	p->mark_mem = NULL;
	return p;
}

void plugin_main(char *argv[],
		 const char *(*init)(struct command *init_cmd,
				     const char *buf, const jsmntok_t *),
		 void *data,
		 const enum plugin_restartability restartability,
		 bool init_rpc,
		 struct feature_set *features STEALS,
		 const struct plugin_command *commands TAKES,
		 size_t num_commands,
		 const struct plugin_notification *notif_subs TAKES,
		 size_t num_notif_subs,
		 const struct plugin_hook *hook_subs TAKES,
		 size_t num_hook_subs,
		 const char **notif_topics TAKES,
		 size_t num_notif_topics,
		 ...)
{
	struct plugin *plugin;
	va_list ap;
	bool developer;

	setup_locale();

	developer = daemon_developer_mode(argv);

	/* Note this already prints to stderr, which is enough for now */
	daemon_setup(argv[0], NULL, NULL);

	va_start(ap, num_notif_topics);
	plugin = new_plugin(NULL, argv[0], developer,
			    init, restartability, init_rpc, features, commands,
			    num_commands, notif_subs, num_notif_subs, hook_subs,
			    num_hook_subs, notif_topics, num_notif_topics, ap);
	plugin_set_data(plugin, data);
	va_end(ap);
	setup_command_usage(plugin);

	timers_init(&plugin->timers, time_mono());

	io_new_conn(plugin, STDIN_FILENO, stdin_conn_init, plugin);
	io_new_conn(plugin, STDOUT_FILENO, stdout_conn_init, plugin);

	for (;;) {
		struct timer *expired = NULL;

		clean_tmpctx();

		/* Will only exit if a timer has expired. */
		io_loop(&plugin->timers, &expired);
		call_plugin_timer(plugin, expired);
	}

	tal_free(plugin);
}

static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx,
							   const char *buffer,
							   const jsmntok_t *tok)
{
	struct listpeers_channel *chan;
	const jsmntok_t *privtok = json_get_member(buffer, tok, "private"),
			*statetok = json_get_member(buffer, tok, "state"),
			*ftxidtok =
			    json_get_member(buffer, tok, "funding_txid"),
			*scidtok =
			    json_get_member(buffer, tok, "short_channel_id"),
			*dirtok = json_get_member(buffer, tok, "direction"),
			*tmsattok = json_get_member(buffer, tok, "total_msat"),
			*smsattok =
			    json_get_member(buffer, tok, "spendable_msat"),
			*aliastok = json_get_member(buffer, tok, "alias"),
			*max_htlcs = json_get_member(buffer, tok, "max_accepted_htlcs"),
			*htlcstok = json_get_member(buffer, tok, "htlcs"),
			*idtok = json_get_member(buffer, tok, "peer_id"),
			*conntok = json_get_member(buffer, tok, "peer_connected");

	chan = tal(ctx, struct listpeers_channel);

	if (scidtok != NULL) {
		assert(dirtok != NULL);
		chan->scid = tal(chan, struct short_channel_id);
		json_to_short_channel_id(buffer, scidtok, chan->scid);
	} else {
		chan->scid = NULL;
	}

	if (aliastok != NULL) {
		const jsmntok_t *loctok =
				    json_get_member(buffer, aliastok, "local"),
				*remtok =
				    json_get_member(buffer, aliastok, "remote");
		if (loctok) {
			chan->alias[LOCAL] = tal(chan, struct short_channel_id);
			json_to_short_channel_id(buffer, loctok,
						 chan->alias[LOCAL]);
		} else
			chan->alias[LOCAL] = NULL;

		if (remtok) {
			chan->alias[REMOTE] = tal(chan, struct short_channel_id);
			json_to_short_channel_id(buffer, loctok,
						 chan->alias[REMOTE]);
		} else
			chan->alias[REMOTE] = NULL;
	} else {
		chan->alias[LOCAL] = NULL;
		chan->alias[REMOTE] = NULL;
	}

	/* If we catch a channel during opening, these might not be set.
	 * It's not a real channel (yet), so ignore it! */
	if (!chan->scid && !chan->alias[LOCAL])
		return tal_free(chan);

	json_to_node_id(buffer, idtok, &chan->id);
	json_to_bool(buffer, conntok, &chan->connected);
	json_to_bool(buffer, privtok, &chan->private);
	chan->state = json_strdup(chan, buffer, statetok);
	json_to_txid(buffer, ftxidtok, &chan->funding_txid);

	json_to_int(buffer, dirtok, &chan->direction);
	json_to_msat(buffer, tmsattok, &chan->total_msat);
	json_to_msat(buffer, smsattok, &chan->spendable_msat);
	json_to_u16(buffer, max_htlcs, &chan->max_accepted_htlcs);
	chan->num_htlcs = htlcstok->size;

	return chan;
}

struct listpeers_channel **json_to_listpeers_channels(const tal_t *ctx,
						      const char *buffer,
						      const jsmntok_t *tok)
{
	size_t i;
	const jsmntok_t *iter;
	const jsmntok_t *channelstok = json_get_member(buffer, tok, "channels");
	struct listpeers_channel **chans;

	chans = tal_arr(ctx, struct listpeers_channel *, 0);
	json_for_each_arr(i, iter, channelstok) {
		struct listpeers_channel *chan;

		chan = json_to_listpeers_channel(chans, buffer, iter);
		if (!chan)
			continue;
		tal_arr_expand(&chans, chan);
	}
	return chans;
}

struct createonion_response *json_to_createonion_response(const tal_t *ctx,
							  const char *buffer,
							  const jsmntok_t *toks)
{
	size_t i;
	struct createonion_response *resp;
	const jsmntok_t *oniontok = json_get_member(buffer, toks, "onion");
	const jsmntok_t *secretstok = json_get_member(buffer, toks, "shared_secrets");
	const jsmntok_t *cursectok;

	if (oniontok == NULL || secretstok == NULL)
		return NULL;

	resp = tal(ctx, struct createonion_response);

	if (oniontok->type != JSMN_STRING)
		goto fail;

	resp->onion = json_tok_bin_from_hex(resp, buffer, oniontok);
	resp->shared_secrets = tal_arr(resp, struct secret, secretstok->size);

	json_for_each_arr(i, cursectok, secretstok) {
		if (cursectok->type != JSMN_STRING)
			goto fail;
		json_to_secret(buffer, cursectok, &resp->shared_secrets[i]);
	}
	return resp;

fail:
	return tal_free(resp);
}

static bool json_to_route_hop_inplace(struct route_hop *dst, const char *buffer,
				      const jsmntok_t *toks)
{
	const jsmntok_t *idtok = json_get_member(buffer, toks, "id");
	const jsmntok_t *channeltok = json_get_member(buffer, toks, "channel");
	const jsmntok_t *directiontok = json_get_member(buffer, toks, "direction");
	const jsmntok_t *amounttok = json_get_member(buffer, toks, "amount_msat");
	const jsmntok_t *delaytok = json_get_member(buffer, toks, "delay");
	const jsmntok_t *styletok = json_get_member(buffer, toks, "style");

	if (idtok == NULL || channeltok == NULL || directiontok == NULL ||
	    amounttok == NULL || delaytok == NULL || styletok == NULL)
		return false;

	json_to_node_id(buffer, idtok, &dst->node_id);
	json_to_short_channel_id(buffer, channeltok, &dst->scid);
	json_to_int(buffer, directiontok, &dst->direction);
	json_to_msat(buffer, amounttok, &dst->amount);
	json_to_number(buffer, delaytok, &dst->delay);
	return true;
}

struct route_hop *json_to_route(const tal_t *ctx, const char *buffer,
				const jsmntok_t *toks)
{
	size_t num = toks->size, i;
	struct route_hop *hops;
	const jsmntok_t *rtok;
	if (toks->type != JSMN_ARRAY)
		return NULL;

	hops = tal_arr(ctx, struct route_hop, num);
	json_for_each_arr(i, rtok, toks) {
		if (!json_to_route_hop_inplace(&hops[i], buffer, rtok))
			return tal_free(hops);
	}
	return hops;
}

struct command_result *WARN_UNUSED_RESULT
command_hook_success(struct command *cmd)
{
	struct json_stream *response = jsonrpc_stream_success(cmd);
	assert(cmd->type == COMMAND_TYPE_HOOK);
	json_add_string(response, "result", "continue");
	return command_finished(cmd, response);
}

struct command *aux_command(const struct command *cmd)
{
	return new_command(cmd->plugin, cmd->plugin, cmd->id,
			   cmd->methodname, COMMAND_TYPE_AUX);
}

struct command_result *WARN_UNUSED_RESULT
aux_command_done(struct command *cmd)
{
	assert(cmd->type == COMMAND_TYPE_AUX);
	tal_free(cmd);
	return &complete;
}

struct command_result *WARN_UNUSED_RESULT
notification_handled(struct command *cmd)
{
	assert(cmd->type == COMMAND_TYPE_NOTIFICATION);
	tal_free(cmd);
	return &complete;
}

bool plugin_developer_mode(const struct plugin *plugin)
{
	return plugin->developer;
}

void plugin_set_data(struct plugin *plugin, void *data TAKES)
{
	if (taken(data))
		tal_steal(plugin, data);
	plugin->data = data;
}

void *plugin_get_data_(struct plugin *plugin)
{
	return plugin->data;
}
