From 53653f3cd612cc199f2a9cb4c61151e65484c10f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 10:59:56 +1030 Subject: [PATCH 01/13] plugin: simplify plugin dir test a little. Check if it's a normal file first, then check permissions. Signed-off-by: Rusty Russell --- lightningd/plugin.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 57bd6079226a..b37fb932f84e 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -725,11 +725,11 @@ static const char *plugin_fullpath(const tal_t *ctx, const char *dir, fullname = path_join(ctx, dir, basename); if (stat(fullname, &st) != 0) return tal_free(fullname); - if (!(st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) || st.st_mode & S_IFDIR) + /* Only regular files please (or symlinks to such: stat not lstat!) */ + if ((st.st_mode & S_IFMT) != S_IFREG) return tal_free(fullname); - - /* Ignore directories, they have exec mode, but aren't executable. */ - if (st.st_mode & S_IFDIR) + /* Must be executable by someone. */ + if (!(st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) return tal_free(fullname); return fullname; } From 9e35c027a82f162366c6ec8e8105f308efd0bcd5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:00:56 +1030 Subject: [PATCH 02/13] plugins: allow --dev-debugger=. This currently just invokes GDB, but we could generalize it (though pdb doesn't allow attaching to a running process, other python debuggers seem to). Signed-off-by: Rusty Russell --- common/daemon.c | 21 +++++++++++++++++++++ common/daemon.h | 3 +++ common/subdaemon.c | 13 ++----------- lightningd/lightningd.c | 4 ++-- lightningd/lightningd.h | 6 +++--- lightningd/options.c | 10 ++++++++-- lightningd/plugin.c | 22 ++++++++++++++++------ lightningd/plugin.h | 4 +++- lightningd/subd.c | 8 +------- lightningd/subd.h | 1 - lightningd/test/run-find_my_abspath.c | 2 +- 11 files changed, 60 insertions(+), 34 deletions(-) diff --git a/common/daemon.c b/common/daemon.c index 276bb4838ef8..67fc16e39638 100644 --- a/common/daemon.c +++ b/common/daemon.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -150,3 +151,23 @@ void daemon_shutdown(void) tal_free(tmpctx); wally_cleanup(0); } + +void daemon_maybe_debug(int argc, char *argv[]) +{ +#if DEVELOPER + for (int i = 1; i < argc; i++) { + if (!streq(argv[i], "--debugger")) + continue; + + /* Don't let this mess up stdout, so redir to /dev/null */ + char *cmd = tal_fmt(NULL, "${DEBUG_TERM:-gnome-terminal --} gdb -ex 'attach %u' %s >/dev/null &", getpid(), argv[0]); + fprintf(stderr, "Running %s\n", cmd); + /* warn_unused_result is fascist bullshit. + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 */ + if (system(cmd)) + ; + /* Continue in the debugger. */ + kill(getpid(), SIGSTOP); + } +#endif /* DEVELOPER */ +} diff --git a/common/daemon.h b/common/daemon.h index b211e37772db..d252dff3a5de 100644 --- a/common/daemon.h +++ b/common/daemon.h @@ -14,6 +14,9 @@ int daemon_poll(struct pollfd *fds, nfds_t nfds, int timeout); /* Shutdown for a valgrind-clean exit (frees everything) */ void daemon_shutdown(void); +/* Kick in a debugger if they set --debugger */ +void daemon_maybe_debug(int argc, char *argv[]); + struct backtrace_state *backtrace_state; #endif /* LIGHTNING_COMMON_DAEMON_H */ diff --git a/common/subdaemon.c b/common/subdaemon.c index ed3b8bd0bd80..58349f343b80 100644 --- a/common/subdaemon.c +++ b/common/subdaemon.c @@ -37,19 +37,10 @@ void subdaemon_setup(int argc, char *argv[]) logging_io = true; } + daemon_maybe_debug(argc, argv); + #if DEVELOPER - /* From debugger, set debugger_spin to 0. */ for (int i = 1; i < argc; i++) { - if (streq(argv[i], "--debugger")) { - char *cmd = tal_fmt(NULL, "${DEBUG_TERM:-gnome-terminal --} gdb -ex 'attach %u' %s &", getpid(), argv[0]); - fprintf(stderr, "Running %s\n", cmd); - /* warn_unused_result is fascist bullshit. - * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 */ - if (system(cmd)) - ; - /* Continue in the debugger. */ - kill(getpid(), SIGSTOP); - } if (strstarts(argv[i], "--dev-disconnect=")) { dev_disconnect_init(atoi(argv[i] + strlen("--dev-disconnect="))); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index ebcac22dcbfc..1b897211ad3f 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -110,8 +110,8 @@ static struct lightningd *new_lightningd(const tal_t *ctx) * is a nod to keeping it minimal and explicit: we need this code for * testing, but its existence means we're not actually testing the * same exact code users will be running. */ + ld->dev_debug_subprocess = NULL; #if DEVELOPER - ld->dev_debug_subdaemon = NULL; ld->dev_disconnect_fd = -1; ld->dev_subdaemon_fail = false; ld->dev_allow_localhost = false; @@ -611,7 +611,7 @@ int main(int argc, char *argv[]) /*~ Initialize all the plugins we just registered, so they can * do their thing and tell us about themselves (including * options registration). */ - plugins_init(ld->plugins); + plugins_init(ld->plugins, ld->dev_debug_subprocess); /*~ Handle options and config; move to .lightningd (--lightning-dir) */ handle_opts(ld, argc, argv); diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index d508c02eb9e8..4c1ebb28639e 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -183,10 +183,10 @@ struct lightningd { * if we are the fundee. */ u32 max_funding_unconfirmed; -#if DEVELOPER - /* If we want to debug a subdaemon. */ - const char *dev_debug_subdaemon; + /* If we want to debug a subdaemon/plugin. */ + const char *dev_debug_subprocess; +#if DEVELOPER /* If we have a --dev-disconnect file */ int dev_disconnect_fd; diff --git a/lightningd/options.c b/lightningd/options.c index 6569157caeec..7307ab73e96b 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -435,6 +435,12 @@ static void config_register_opts(struct lightningd *ld) } #if DEVELOPER +static char *opt_subprocess_debug(const char *optarg, struct lightningd *ld) +{ + ld->dev_debug_subprocess = optarg; + return NULL; +} + static void dev_register_opts(struct lightningd *ld) { opt_register_noarg("--dev-no-reconnect", opt_set_invbool, @@ -442,8 +448,8 @@ static void dev_register_opts(struct lightningd *ld) "Disable automatic reconnect attempts"); opt_register_noarg("--dev-fail-on-subdaemon-fail", opt_set_bool, &ld->dev_subdaemon_fail, opt_hidden); - opt_register_arg("--dev-debugger=", opt_subd_debug, NULL, - ld, "Invoke gdb at start of "); + opt_register_early_arg("--dev-debugger=", opt_subprocess_debug, NULL, + ld, "Invoke gdb at start of "); opt_register_arg("--dev-broadcast-interval=", opt_set_uintval, opt_show_uintval, &ld->config.broadcast_interval_msec, "Time between gossip broadcasts in milliseconds"); diff --git a/lightningd/plugin.c b/lightningd/plugin.c index b37fb932f84e..46d72dc6b17c 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -766,7 +766,7 @@ void clear_plugins(struct plugins *plugins) tal_free(p); } -void plugins_init(struct plugins *plugins) +void plugins_init(struct plugins *plugins, const char *dev_plugin_debug) { struct plugin *p; char **cmd; @@ -778,9 +778,13 @@ void plugins_init(struct plugins *plugins) /* Spawn the plugin processes before entering the io_loop */ list_for_each(&plugins->plugins, p, list) { - cmd = tal_arr(p, char *, 2); + bool debug; + + debug = dev_plugin_debug && strends(p->cmd, dev_plugin_debug); + cmd = tal_arrz(p, char *, 2 + debug); cmd[0] = p->cmd; - cmd[1] = NULL; + if (debug) + cmd[1] = "--debugger"; p->pid = pipecmdarr(&stdout, &stdin, NULL, cmd); if (p->pid == -1) @@ -798,9 +802,15 @@ void plugins_init(struct plugins *plugins) json_array_end(req->stream); plugin_request_queue(req); plugins->pending_manifests++; - p->timeout_timer = new_reltimer( - &plugins->timers, p, time_from_sec(PLUGIN_MANIFEST_TIMEOUT), - plugin_manifest_timeout, p); + /* Don't timeout if they're running a debugger. */ + if (debug) + p->timeout_timer = NULL; + else { + p->timeout_timer + = new_reltimer(&plugins->timers, p, + time_from_sec(PLUGIN_MANIFEST_TIMEOUT), + plugin_manifest_timeout, p); + } tal_free(cmd); } diff --git a/lightningd/plugin.h b/lightningd/plugin.h index 2e9582e7b911..40d64caf428d 100644 --- a/lightningd/plugin.h +++ b/lightningd/plugin.h @@ -27,8 +27,10 @@ struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book, * 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. + * + * The dev_plugin_debug arg comes from --dev-debugger if DEVELOPER. */ -void plugins_init(struct plugins *plugins); +void plugins_init(struct plugins *plugins, const char *dev_plugin_debug); /** * Register a plugin for initialization and execution. diff --git a/lightningd/subd.c b/lightningd/subd.c index 41bb0dd17865..62c9361be098 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -626,7 +626,7 @@ static struct subd *new_subd(struct lightningd *ld, assert(name != NULL); #if DEVELOPER - debug_subd = ld->dev_debug_subdaemon; + debug_subd = ld->dev_debug_subprocess; disconnect_fd = ld->dev_disconnect_fd; #endif /* DEVELOPER */ @@ -784,12 +784,6 @@ void subd_release_channel(struct subd *owner, void *channel) } #if DEVELOPER -char *opt_subd_debug(const char *optarg, struct lightningd *ld) -{ - ld->dev_debug_subdaemon = optarg; - return NULL; -} - char *opt_subd_dev_disconnect(const char *optarg, struct lightningd *ld) { ld->dev_disconnect_fd = open(optarg, O_RDONLY); diff --git a/lightningd/subd.h b/lightningd/subd.h index 81730ef813f0..2cfaf395752b 100644 --- a/lightningd/subd.h +++ b/lightningd/subd.h @@ -206,7 +206,6 @@ void subd_shutdown(struct subd *subd, unsigned int seconds); const char *find_my_abspath(const tal_t *ctx, const char *argv0); #if DEVELOPER -char *opt_subd_debug(const char *optarg, struct lightningd *ld); char *opt_subd_dev_disconnect(const char *optarg, struct lightningd *ld); bool dev_disconnect_permanent(struct lightningd *ld); diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 143f211cf511..9044ea432bba 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -136,7 +136,7 @@ void onchaind_replay_channels(struct lightningd *ld UNNEEDED) void plugins_config(struct plugins *plugins UNNEEDED) { fprintf(stderr, "plugins_config called!\n"); abort(); } /* Generated stub for plugins_init */ -void plugins_init(struct plugins *plugins UNNEEDED) +void plugins_init(struct plugins *plugins UNNEEDED, const char *dev_plugin_debug UNNEEDED) { fprintf(stderr, "plugins_init called!\n"); abort(); } /* Generated stub for plugins_new */ struct plugins *plugins_new(const tal_t *ctx UNNEEDED, struct log_book *log_book UNNEEDED, From b721297fe6ba5cab7a4bad1d3bcc0c4fff4d754a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:01:56 +1030 Subject: [PATCH 03/13] ccan: update to get updated pipecmd. Note that this changes the order of arguments to pipecmd to match the documentation, so we fix all the callers! Also make configure re-run when configurator changes. Signed-off-by: Rusty Russell --- Makefile | 2 +- ccan/README | 2 +- ccan/ccan/compiler/compiler.h | 58 ++++++++++++++++++++++++++ ccan/ccan/pipecmd/_info | 2 +- ccan/ccan/pipecmd/pipecmd.c | 29 +++++++++---- ccan/ccan/pipecmd/pipecmd.h | 7 ++++ ccan/ccan/pipecmd/test/run-fdleak.c | 2 +- ccan/ccan/pipecmd/test/run.c | 10 ++--- ccan/ccan/take/take.c | 17 ++++++-- ccan/tools/configurator/configurator.c | 9 ++++ lightningd/bitcoind.c | 4 +- lightningd/lightningd.c | 4 +- lightningd/plugin.c | 2 +- 13 files changed, 122 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 3e74d4bc749c..8a6ff9e54951 100644 --- a/Makefile +++ b/Makefile @@ -184,7 +184,7 @@ LDLIBS = -L/usr/local/lib -lm -lgmp -lsqlite3 -lz $(COVFLAGS) default: all-programs all-test-programs -config.vars ccan/config.h: configure +config.vars ccan/config.h: configure ccan/tools/configurator/configurator.c @if [ ! -f config.vars ]; then echo 'The 1990s are calling: use ./configure!' >&2; exit 1; fi ./configure --reconfigure diff --git a/ccan/README b/ccan/README index de2927b94c82..e08a994a973b 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2454-gc656dceb +CCAN version: init-2455-gd3d2242b diff --git a/ccan/ccan/compiler/compiler.h b/ccan/ccan/compiler/compiler.h index bce4f25a1226..caa89edc5968 100644 --- a/ccan/ccan/compiler/compiler.h +++ b/ccan/ccan/compiler/compiler.h @@ -228,4 +228,62 @@ #define WARN_UNUSED_RESULT #endif #endif + + +#if HAVE_ATTRIBUTE_DEPRECATED +/** + * WARN_DEPRECATED - warn that a function/type/variable is deprecated when used. + * + * Used to mark a function, type or variable should not be used. + * + * Example: + * WARN_DEPRECATED char *oldfunc(char *buf); + */ +#define WARN_DEPRECATED __attribute__((__deprecated__)) +#else +#define WARN_DEPRECATED +#endif + + +#if HAVE_ATTRIBUTE_NONNULL +/** + * NO_NULL_ARGS - specify that no arguments to this function can be NULL. + * + * The compiler will warn if any pointer args are NULL. + * + * Example: + * NO_NULL_ARGS char *my_copy(char *buf); + */ +#define NO_NULL_ARGS __attribute__((__nonnull__)) + +/** + * NON_NULL_ARGS - specify that some arguments to this function can't be NULL. + * @...: 1-based argument numbers for which args can't be NULL. + * + * The compiler will warn if any of the specified pointer args are NULL. + * + * Example: + * char *my_copy2(char *buf, char *maybenull) NON_NULL_ARGS(1, 2); + */ +#define NON_NULL_ARGS(index, ...) __attribute__((__nonnull__(index, __VA_ARGS__))) +#else +#define NO_NULL_ARGS +#define NON_NULL_ARGS(index, ...) +#endif + + +#if HAVE_ATTRIBUTE_SENTINEL +/** + * LAST_ARG_NULL - specify the last argument of a variadic function must be NULL. + * + * The compiler will warn if the last argument isn't NULL. + * + * Example: + * char *join_string(char *buf, ...) LAST_ARG_NULL; + */ +#define LAST_ARG_NULL __attribute__((__sentinel__)) +#else +#define LAST_ARG_NULL +#endif + #endif /* CCAN_COMPILER_H */ diff --git a/ccan/ccan/pipecmd/_info b/ccan/ccan/pipecmd/_info index a560bfea05cc..8c49511a250c 100644 --- a/ccan/ccan/pipecmd/_info +++ b/ccan/ccan/pipecmd/_info @@ -32,7 +32,7 @@ * exit(1); * exit(0); * } - * child = pipecmd(&outputfd, NULL, NULL, argv[0], "ignoredarg", NULL); + * child = pipecmd(NULL, &outputfd, NULL, argv[0], "ignoredarg", NULL); * if (child < 0) * err(1, "Creating child"); * if (read(outputfd, input, sizeof(input)) != sizeof(input)) diff --git a/ccan/ccan/pipecmd/pipecmd.c b/ccan/ccan/pipecmd/pipecmd.c index 32772a83bc7b..c2b514df5e97 100644 --- a/ccan/ccan/pipecmd/pipecmd.c +++ b/ccan/ccan/pipecmd/pipecmd.c @@ -6,6 +6,8 @@ #include #include +int pipecmd_preserve; + static char **gather_args(const char *arg0, va_list ap) { size_t n = 1; @@ -26,7 +28,7 @@ static char **gather_args(const char *arg0, va_list ap) return arr; } -pid_t pipecmdv(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild, +pid_t pipecmdv(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild, const char *cmd, va_list ap) { char **arr = gather_args(cmd, ap); @@ -36,12 +38,12 @@ pid_t pipecmdv(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild, errno = ENOMEM; return -1; } - ret = pipecmdarr(fd_fromchild, fd_tochild, fd_errfromchild, arr); + ret = pipecmdarr(fd_tochild, fd_fromchild, fd_errfromchild, arr); free_noerr(arr); return ret; } -pid_t pipecmdarr(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild, +pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild, char *const *arr) { int tochild[2], fromchild[2], errfromchild[2], execfail[2]; @@ -49,7 +51,10 @@ pid_t pipecmdarr(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild, int err; if (fd_tochild) { - if (pipe(tochild) != 0) + if (fd_tochild == &pipecmd_preserve) { + tochild[0] = STDIN_FILENO; + fd_tochild = NULL; + } else if (pipe(tochild) != 0) goto fail; } else { tochild[0] = open("/dev/null", O_RDONLY); @@ -57,7 +62,10 @@ pid_t pipecmdarr(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild, goto fail; } if (fd_fromchild) { - if (pipe(fromchild) != 0) + if (fd_fromchild == &pipecmd_preserve) { + fromchild[1] = STDOUT_FILENO; + fd_fromchild = NULL; + } else if (pipe(fromchild) != 0) goto close_tochild_fail; } else { fromchild[1] = open("/dev/null", O_WRONLY); @@ -65,7 +73,10 @@ pid_t pipecmdarr(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild, goto close_tochild_fail; } if (fd_errfromchild) { - if (fd_errfromchild == fd_fromchild) { + if (fd_errfromchild == &pipecmd_preserve) { + errfromchild[1] = STDERR_FILENO; + fd_errfromchild = NULL; + } else if (fd_errfromchild == fd_fromchild) { errfromchild[0] = fromchild[0]; errfromchild[1] = fromchild[1]; } else { @@ -77,7 +88,7 @@ pid_t pipecmdarr(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild, if (errfromchild[1] < 0) goto close_fromchild_fail; } - + if (pipe(execfail) != 0) goto close_errfromchild_fail; @@ -168,14 +179,14 @@ pid_t pipecmdarr(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild, return -1; } -pid_t pipecmd(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild, +pid_t pipecmd(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild, const char *cmd, ...) { pid_t childpid; va_list ap; va_start(ap, cmd); - childpid = pipecmdv(fd_fromchild, fd_tochild, fd_errfromchild, cmd, ap); + childpid = pipecmdv(fd_tochild, fd_fromchild, fd_errfromchild, cmd, ap); va_end(ap); return childpid; diff --git a/ccan/ccan/pipecmd/pipecmd.h b/ccan/ccan/pipecmd/pipecmd.h index 4816950502f2..5bbaefc01234 100644 --- a/ccan/ccan/pipecmd/pipecmd.h +++ b/ccan/ccan/pipecmd/pipecmd.h @@ -18,6 +18,7 @@ * If @errfd is NULL, the child's stderr is (write-only) /dev/null. * * If @errfd == @outfd (and non-NULL) they will be shared. + * If @infd, @outfd or @errfd is &pipecmd_preserve, it is unchanged. * * The return value is the pid of the child, or -1. */ @@ -41,4 +42,10 @@ pid_t pipecmdv(int *infd, int *outfd, int *errfd, const char *cmd, va_list ap); * @arr: NULL-terminated array for arguments (first is program to run). */ pid_t pipecmdarr(int *infd, int *outfd, int *errfd, char *const *arr); + +/** + * pipecmd_preserve - special value for fds to indicate it is unchanged + */ +extern int pipecmd_preserve; + #endif /* CCAN_PIPECMD_H */ diff --git a/ccan/ccan/pipecmd/test/run-fdleak.c b/ccan/ccan/pipecmd/test/run-fdleak.c index 775ef333f8be..6b7729060e04 100644 --- a/ccan/ccan/pipecmd/test/run-fdleak.c +++ b/ccan/ccan/pipecmd/test/run-fdleak.c @@ -21,7 +21,7 @@ int main(int argc, char *argv[]) /* This is how many tests you plan to run */ plan_tests(13); - child = pipecmd(&outfd, NULL, NULL, argv[0], "out", NULL); + child = pipecmd(NULL, &outfd, NULL, argv[0], "out", NULL); if (!ok1(child > 0)) exit(1); ok1(read(outfd, buf, sizeof(buf)) == sizeof(buf)); diff --git a/ccan/ccan/pipecmd/test/run.c b/ccan/ccan/pipecmd/test/run.c index 45b96ab2f03b..e446da6d16b8 100644 --- a/ccan/ccan/pipecmd/test/run.c +++ b/ccan/ccan/pipecmd/test/run.c @@ -48,7 +48,7 @@ int main(int argc, char *argv[]) /* This is how many tests you plan to run */ plan_tests(67); - child = pipecmd(&outfd, &infd, &errfd, argv[0], "inout", NULL); + child = pipecmd(&infd, &outfd, &errfd, argv[0], "inout", NULL); if (!ok1(child > 0)) exit(1); ok1(write(infd, buf, sizeof(buf)) == sizeof(buf)); @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) ok1(WIFEXITED(status)); ok1(WEXITSTATUS(status) == 0); - child = pipecmd(NULL, &infd, NULL, argv[0], "in", NULL); + child = pipecmd(&infd, NULL, NULL, argv[0], "in", NULL); if (!ok1(child > 0)) exit(1); ok1(write(infd, buf, sizeof(buf)) == sizeof(buf)); @@ -72,7 +72,7 @@ int main(int argc, char *argv[]) ok1(WIFEXITED(status)); ok1(WEXITSTATUS(status) == 0); - child = pipecmd(&outfd, NULL, NULL, argv[0], "out", NULL); + child = pipecmd(NULL, &outfd, NULL, argv[0], "out", NULL); if (!ok1(child > 0)) exit(1); ok1(read(outfd, buf, sizeof(buf)) == sizeof(buf)); @@ -94,7 +94,7 @@ int main(int argc, char *argv[]) ok1(WEXITSTATUS(status) == 0); /* errfd == outfd should work with both. */ - child = pipecmd(&errfd, NULL, &errfd, argv[0], "err", NULL); + child = pipecmd(NULL, &errfd, &errfd, argv[0], "err", NULL); if (!ok1(child > 0)) exit(1); ok1(read(errfd, buf, sizeof(buf)) == sizeof(buf)); @@ -104,7 +104,7 @@ int main(int argc, char *argv[]) ok1(WIFEXITED(status)); ok1(WEXITSTATUS(status) == 0); - child = pipecmd(&outfd, NULL, &outfd, argv[0], "out", NULL); + child = pipecmd(NULL, &outfd, &outfd, argv[0], "out", NULL); if (!ok1(child > 0)) exit(1); ok1(read(outfd, buf, sizeof(buf)) == sizeof(buf)); diff --git a/ccan/ccan/take/take.c b/ccan/ccan/take/take.c index c628aac0dc97..4833bf935764 100644 --- a/ccan/ccan/take/take.c +++ b/ccan/ccan/take/take.c @@ -32,9 +32,20 @@ void *take_(const void *p, const char *label) } takenarr = new; /* Once labelarr is set, we maintain it. */ - if (labelarr) - labelarr = realloc(labelarr, - sizeof(*labelarr) * (max_taken+1)); + if (labelarr) { + const char **labelarr_new; + labelarr_new = realloc(labelarr, + sizeof(*labelarr) * (max_taken+1)); + if (labelarr_new) { + labelarr = labelarr_new; + } else { + /* num_taken will be out of sync with the size of + * labelarr after realloc failure. + * Just pretend that we never had labelarr allocated. */ + free(labelarr); + labelarr = NULL; + } + } max_taken++; } if (unlikely(labelarr)) diff --git a/ccan/tools/configurator/configurator.c b/ccan/tools/configurator/configurator.c index 239e91c962b9..1386fc90c8d7 100644 --- a/ccan/tools/configurator/configurator.c +++ b/ccan/tools/configurator/configurator.c @@ -136,6 +136,15 @@ static const struct test base_tests[] = { { "HAVE_ATTRIBUTE_CONST", "__attribute__((const)) support", "DEFINES_FUNC", NULL, NULL, "static int __attribute__((const)) func(int x) { return x; }" }, + { "HAVE_ATTRIBUTE_DEPRECATED", "__attribute__((deprecated)) support", + "DEFINES_FUNC", NULL, NULL, + "static int __attribute__((deprecated)) func(int x) { return x; }" }, + { "HAVE_ATTRIBUTE_NONNULL", "__attribute__((nonnull)) support", + "DEFINES_FUNC", NULL, NULL, + "static char *__attribute__((nonnull)) func(char *p) { return p; }" }, + { "HAVE_ATTRIBUTE_SENTINEL", "__attribute__((sentinel)) support", + "DEFINES_FUNC", NULL, NULL, + "static int __attribute__((sentinel)) func(int i, ...) { return i; }" }, { "HAVE_ATTRIBUTE_PURE", "__attribute__((pure)) support", "DEFINES_FUNC", NULL, NULL, "static int __attribute__((pure)) func(int x) { return x; }" }, diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 55b261d2d194..918e891d27eb 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -229,7 +229,7 @@ static void next_bcli(struct bitcoind *bitcoind, enum bitcoind_prio prio) if (!bcli) return; - bcli->pid = pipecmdarr(&bcli->fd, NULL, &bcli->fd, + bcli->pid = pipecmdarr(NULL, &bcli->fd, &bcli->fd, cast_const2(char **, bcli->args)); if (bcli->pid < 0) fatal("%s exec failed: %s", bcli->args[0], strerror(errno)); @@ -806,7 +806,7 @@ void wait_for_bitcoind(struct bitcoind *bitcoind) bool printed = false; for (;;) { - child = pipecmdarr(&from, NULL, &from, cast_const2(char **,cmd)); + child = pipecmdarr(NULL, &from, &from, cast_const2(char **,cmd)); if (child < 0) { if (errno == ENOENT) { fatal_bitcoind_failure(bitcoind, "bitcoin-cli not found. Is bitcoin-cli (part of Bitcoin Core) available in your PATH?"); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 1b897211ad3f..3708fa223179 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -258,10 +258,10 @@ void test_subdaemons(const struct lightningd *ld) const char *dpath = path_join(tmpctx, ld->daemon_dir, subdaemons[i]); const char *verstring; /*~ CCAN's pipecmd module is like popen for grownups: it - * takes pointers to fill in stdout, stdin and stderr file + * takes pointers to fill in stdin, stdout and stderr file * descriptors if desired, and the remainder of arguments * are the command and its argument. */ - pid_t pid = pipecmd(&outfd, NULL, &outfd, + pid_t pid = pipecmd(NULL, &outfd, &outfd, dpath, "--version", NULL); /*~ Our logging system: spam goes in at log_debug level, but diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 46d72dc6b17c..72457a46f01c 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -785,7 +785,7 @@ void plugins_init(struct plugins *plugins, const char *dev_plugin_debug) cmd[0] = p->cmd; if (debug) cmd[1] = "--debugger"; - p->pid = pipecmdarr(&stdout, &stdin, NULL, cmd); + p->pid = pipecmdarr(&stdin, &stdout, NULL, cmd); if (p->pid == -1) fatal("error starting plugin '%s': %s", p->cmd, From c42406a5e4df2c832eff6048389301a11e7a2dba Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:02:56 +1030 Subject: [PATCH 04/13] plugin: preserve stderr for plugins. Now we've updated ccan/pipecmd, we can use pipecmd_preserve to preserve stderr for plugins so we see their error spew. Signed-off-by: Rusty Russell --- lightningd/plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 72457a46f01c..0cf31a15e546 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -785,7 +785,7 @@ void plugins_init(struct plugins *plugins, const char *dev_plugin_debug) cmd[0] = p->cmd; if (debug) cmd[1] = "--debugger"; - p->pid = pipecmdarr(&stdin, &stdout, NULL, cmd); + p->pid = pipecmdarr(&stdin, &stdout, &pipecmd_preserve, cmd); if (p->pid == -1) fatal("error starting plugin '%s': %s", p->cmd, From d9eb33c1354fad38b43ce048c51041b1d2c5f1a7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:03:56 +1030 Subject: [PATCH 05/13] plugin: log response given by plugin if it's invalid JSON. Signed-off-by: Rusty Russell --- lightningd/plugin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 0cf31a15e546..f75344fcbbb0 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -276,7 +276,8 @@ static bool plugin_read_json_one(struct plugin *plugin) toks = json_parse_input(plugin->buffer, plugin->used, &valid); if (!toks) { if (!valid) { - plugin_kill(plugin, "Failed to parse JSON response"); + plugin_kill(plugin, "Failed to parse JSON response '%.*s'", + (int)plugin->used, plugin->buffer); return false; } /* We need more. */ From 706ba9ce9015821293d813934b20fd2d4e8bdf6f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:04:56 +1030 Subject: [PATCH 06/13] jsonrpc: add the obj token to the callback. This (will) avoid the plugin having to walk back from the params object as it currently does. No code changes; I removed UNUSED and UNNEEDED labels from the other parameters though (as *every* json_rpc callback needs to call param() these days, they're *always* used). Signed-off-by: Rusty Russell --- lightningd/chaintopology.c | 4 +++- lightningd/connect_control.c | 4 +++- lightningd/gossip_control.c | 25 +++++++++++++++++++------ lightningd/invoice.c | 29 ++++++++++++++++++++++------- lightningd/jsonrpc.c | 34 ++++++++++++++++++++++++---------- lightningd/jsonrpc.h | 4 +++- lightningd/log.c | 4 +++- lightningd/memdump.c | 10 ++++++---- lightningd/opening_control.c | 4 +++- lightningd/options.c | 4 +++- lightningd/pay.c | 14 ++++++++++---- lightningd/payalgo.c | 4 +++- lightningd/peer_control.c | 32 ++++++++++++++++++++++++-------- lightningd/peer_htlcs.c | 10 +++++++--- lightningd/ping.c | 4 +++- lightningd/plugin.c | 1 + lightningd/test/run-param.c | 6 ++++-- wallet/walletrpc.c | 25 +++++++++++++++++-------- 18 files changed, 158 insertions(+), 60 deletions(-) diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 8a31e93267b2..9fe3db2279aa 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -463,7 +463,9 @@ u32 feerate_to_style(u32 feerate_perkw, enum feerate_style style) } static void json_feerates(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct chain_topology *topo = cmd->ld->topology; struct json_stream *response; diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 46a75debe384..68cc51d90ec4 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -74,7 +74,9 @@ static void connect_cmd_succeed(struct command *cmd, const struct pubkey *id) } static void json_connect(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { u32 *port; jsmntok_t *idtok; diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 7ca3e54982d9..a0230437f51f 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -243,8 +243,10 @@ static void json_getnodes_reply(struct subd *gossip UNUSED, const u8 *reply, command_success(cmd, response); } -static void json_listnodes(struct command *cmd, const char *buffer, - const jsmntok_t *params) +static void json_listnodes(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { u8 *req; struct pubkey *id; @@ -286,7 +288,10 @@ static void json_getroute_reply(struct subd *gossip UNUSED, const u8 *reply, con command_success(cmd, response); } -static void json_getroute(struct command *cmd, const char *buffer, const jsmntok_t *params) +static void json_getroute(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct lightningd *ld = cmd->ld; struct pubkey *destination; @@ -393,8 +398,10 @@ static void json_listchannels_reply(struct subd *gossip UNUSED, const u8 *reply, command_success(cmd, response); } -static void json_listchannels(struct command *cmd, const char *buffer, - const jsmntok_t *params) +static void json_listchannels(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { u8 *req; struct short_channel_id *id; @@ -443,7 +450,9 @@ static void json_scids_reply(struct subd *gossip UNUSED, const u8 *reply, } static void json_dev_query_scids(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { u8 *msg; const jsmntok_t *scidstok; @@ -486,6 +495,7 @@ AUTODATA(json_command, &dev_query_scids_command); static void json_dev_send_timestamp_filter(struct command *cmd, const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { u8 *msg; @@ -555,6 +565,7 @@ static void json_channel_range_reply(struct subd *gossip UNUSED, const u8 *reply static void json_dev_query_channel_range(struct command *cmd, const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { u8 *msg; @@ -584,6 +595,7 @@ AUTODATA(json_command, &dev_query_channel_range_command); static void json_dev_set_max_scids_encode_size(struct command *cmd, const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { u8 *msg; @@ -609,6 +621,7 @@ AUTODATA(json_command, &dev_set_max_scids_encode_size); static void json_dev_suppress_gossip(struct command *cmd, const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { if (!param(cmd, buffer, params, NULL)) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 8e22b7bb8bb2..bade4f9304c8 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -292,7 +292,9 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, } static void json_invoice(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { const jsmntok_t *fallbacks; const jsmntok_t *preimagetok; @@ -415,7 +417,9 @@ static void json_add_invoices(struct json_stream *response, } static void json_listinvoices(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct json_escaped *label; struct json_stream *response; @@ -441,7 +445,9 @@ static const struct json_command listinvoices_command = { AUTODATA(json_command, &listinvoices_command); static void json_delinvoice(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct invoice i; const struct invoice_details *details; @@ -492,7 +498,9 @@ static const struct json_command delinvoice_command = { }; AUTODATA(json_command, &delinvoice_command); -static void json_delexpiredinvoice(struct command *cmd, const char *buffer, +static void json_delexpiredinvoice(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { u64 *maxexpirytime; @@ -516,6 +524,7 @@ AUTODATA(json_command, &delexpiredinvoice_command); static void json_autocleaninvoice(struct command *cmd, const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { u64 *cycle; @@ -541,7 +550,9 @@ static const struct json_command autocleaninvoice_command = { AUTODATA(json_command, &autocleaninvoice_command); static void json_waitanyinvoice(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { u64 *pay_index; struct wallet *wallet = cmd->ld->wallet; @@ -575,7 +586,9 @@ AUTODATA(json_command, &waitanyinvoice_command); * waiters, if the payment is still pending. */ static void json_waitinvoice(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct invoice i; const struct invoice_details *details; @@ -648,7 +661,9 @@ static void json_add_fallback(struct json_stream *response, } static void json_decodepay(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct bolt11 *b11; struct json_stream *response; diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 0c164e147434..857ac92ac952 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -134,7 +134,9 @@ static void destroy_jcon(struct json_connection *jcon) } static void json_help(struct command *cmd, - const char *buffer, const jsmntok_t *params); + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params); static const struct json_command help_command = { "help", @@ -151,7 +153,9 @@ static const struct json_command help_command = { AUTODATA(json_command, &help_command); static void json_stop(struct command *cmd, - const char *buffer UNUSED, const jsmntok_t *params UNUSED) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct json_stream *response; @@ -174,7 +178,9 @@ AUTODATA(json_command, &stop_command); #if DEVELOPER static void json_rhash(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNUSED, + const jsmntok_t *params) { struct json_stream *response; struct sha256 *secret; @@ -222,7 +228,9 @@ static void slowcmd_start(struct slowcmd *sc) } static void json_slowcmd(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNUSED, + const jsmntok_t *params) { struct slowcmd *sc = tal(cmd, struct slowcmd); @@ -244,7 +252,9 @@ static const struct json_command dev_slowcmd_command = { AUTODATA(json_command, &dev_slowcmd_command); static void json_crash(struct command *cmd UNUSED, - const char *buffer UNUSED, const jsmntok_t *params UNUSED) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { if (!param(cmd, buffer, params, NULL)) return; @@ -277,7 +287,7 @@ static void json_add_help_command(struct command *cmd, { char *usage; cmd->mode = CMD_USAGE; - json_command->dispatch(cmd, NULL, NULL); + json_command->dispatch(cmd, NULL, NULL, NULL); usage = tal_fmt(cmd, "%s %s", json_command->name, cmd->usage); json_object_start(response, NULL); @@ -302,7 +312,9 @@ static void json_add_help_command(struct command *cmd, } static void json_help(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct json_stream *response; const jsmntok_t *cmdtok; @@ -570,7 +582,7 @@ static void parse_request(struct json_connection *jcon, const jsmntok_t tok[]) } db_begin_transaction(jcon->ld->wallet->db); - c->json_cmd->dispatch(c, jcon->buffer, params); + c->json_cmd->dispatch(c, jcon->buffer, tok, params); db_commit_transaction(jcon->ld->wallet->db); /* If they didn't complete it, they must call command_still_pending. @@ -953,7 +965,9 @@ static bool json_tok_command(struct command *cmd, const char *name, } static void json_check(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { jsmntok_t *mod_params; const jsmntok_t *name_tok; @@ -984,7 +998,7 @@ static void json_check(struct command *cmd, * we're after and would be more clear. */ ok = true; cmd->ok = &ok; - cmd->json_cmd->dispatch(cmd, buffer, mod_params); + cmd->json_cmd->dispatch(cmd, buffer, mod_params, mod_params); if (!ok) return; diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index da21d36efa6b..bc37358874e5 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -47,7 +47,9 @@ struct command { struct json_command { const char *name; void (*dispatch)(struct command *, - const char *buffer, const jsmntok_t *params); + const char *buffer, + const jsmntok_t *obj, + const jsmntok_t *params); const char *description; bool deprecated; const char *verbose; diff --git a/lightningd/log.c b/lightningd/log.c index b09d96d62987..3dfd18f2c7f1 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -716,7 +716,9 @@ bool json_tok_loglevel(struct command *cmd, const char *name, } static void json_getlog(struct command *cmd, - const char *buffer, const jsmntok_t * params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t * params) { struct json_stream *response; enum log_level *minlevel; diff --git a/lightningd/memdump.c b/lightningd/memdump.c index eb940d7ed82c..25797b7d3097 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -62,8 +62,9 @@ static void add_memdump(struct json_stream *response, } static void json_memdump(struct command *cmd, - const char *buffer UNNEEDED, - const jsmntok_t *params UNNEEDED) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct json_stream *response; @@ -283,8 +284,9 @@ void opening_memleak_done(struct command *cmd, struct subd *leaker) } static void json_memleak(struct command *cmd, - const char *buffer UNNEEDED, - const jsmntok_t *params UNNEEDED) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { if (!param(cmd, buffer, params, NULL)) return; diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index a11215a2298d..c08c7f16fe4b 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -762,7 +762,9 @@ void opening_peer_no_active_channels(struct peer *peer) * json_fund_channel - Entrypoint for funding a channel */ static void json_fund_channel(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { const jsmntok_t *sattok; struct funding_channel * fc = tal(cmd, struct funding_channel); diff --git a/lightningd/options.c b/lightningd/options.c index 7307ab73e96b..9d3881cb92b5 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1046,7 +1046,9 @@ static void add_config(struct lightningd *ld, } static void json_listconfigs(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { size_t i; struct json_stream *response = NULL; diff --git a/lightningd/pay.c b/lightningd/pay.c index 15aff6d03618..61b4a7ce4c2f 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -941,7 +941,9 @@ static void json_sendpay_on_resolve(const struct sendpay_result* r, } static void json_sendpay(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { const jsmntok_t *routetok; const jsmntok_t *t, *end; @@ -1025,7 +1027,9 @@ static void waitsendpay_timeout(struct command *cmd) command_fail(cmd, PAY_IN_PROGRESS, "Timed out while waiting"); } -static void json_waitsendpay(struct command *cmd, const char *buffer, +static void json_waitsendpay(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { struct sha256 *rhash; @@ -1054,8 +1058,10 @@ static const struct json_command waitsendpay_command = { }; AUTODATA(json_command, &waitsendpay_command); -static void json_listpayments(struct command *cmd, const char *buffer, - const jsmntok_t *params) +static void json_listpayments(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { const struct wallet_payment **payments; struct json_stream *response; diff --git a/lightningd/payalgo.c b/lightningd/payalgo.c index fe78a6942515..ef525db957dc 100644 --- a/lightningd/payalgo.c +++ b/lightningd/payalgo.c @@ -592,7 +592,9 @@ static void json_pay_stop_retrying(struct pay *pay) } static void json_pay(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { double *riskfactor; double *maxfeepercent; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 0607b5c8ac41..51e026e19973 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -803,7 +803,9 @@ static void json_add_peer(struct lightningd *ld, } static void json_listpeers(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { enum log_level *ll; struct pubkey *specific_id; @@ -890,7 +892,9 @@ command_find_channel(struct command *cmd, } static void json_close(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { const jsmntok_t *idtok; struct peer *peer; @@ -1032,7 +1036,9 @@ void load_channels_from_wallet(struct lightningd *ld) } static void json_disconnect(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct pubkey *id; struct peer *peer; @@ -1071,7 +1077,9 @@ static const struct json_command disconnect_command = { AUTODATA(json_command, &disconnect_command); static void json_getinfo(struct command *cmd, - const char *buffer UNUSED, const jsmntok_t *params UNUSED) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct json_stream *response; struct peer *peer; @@ -1144,7 +1152,9 @@ AUTODATA(json_command, &getinfo_command); #if DEVELOPER static void json_sign_last_tx(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct pubkey *peerid; struct peer *peer; @@ -1191,7 +1201,9 @@ static const struct json_command dev_sign_last_tx = { AUTODATA(json_command, &dev_sign_last_tx); static void json_dev_fail(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct pubkey *peerid; struct peer *peer; @@ -1236,7 +1248,9 @@ static void dev_reenable_commit_finished(struct subd *channeld UNUSED, } static void json_dev_reenable_commit(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct pubkey *peerid; struct peer *peer; @@ -1325,7 +1339,9 @@ static void process_dev_forget_channel(struct bitcoind *bitcoind UNUSED, command_success(forget->cmd, response); } -static void json_dev_forget_channel(struct command *cmd, const char *buffer, +static void json_dev_forget_channel(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { struct pubkey *peerid; diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 3eccba420ee8..d7f620260683 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1791,7 +1791,9 @@ void htlcs_reconnect(struct lightningd *ld, #if DEVELOPER -static void json_dev_ignore_htlcs(struct command *cmd, const char *buffer, +static void json_dev_ignore_htlcs(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { struct pubkey *peerid; @@ -1846,8 +1848,10 @@ static void listforwardings_add_forwardings(struct json_stream *response, struct tal_free(forwardings); } -static void json_listforwards(struct command *cmd, const char *buffer, - const jsmntok_t *params) +static void json_listforwards(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct json_stream *response; diff --git a/lightningd/ping.c b/lightningd/ping.c index b7471aa5c865..115853f69953 100644 --- a/lightningd/ping.c +++ b/lightningd/ping.c @@ -79,7 +79,9 @@ void ping_reply(struct subd *subd, const u8 *msg) } static void json_ping(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { u8 *msg; unsigned int *len, *pongbytes; diff --git a/lightningd/plugin.c b/lightningd/plugin.c index f75344fcbbb0..3e2d4cd5f2eb 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -534,6 +534,7 @@ static void plugin_rpcmethod_cb(const struct plugin_request *req, } static void plugin_rpcmethod_dispatch(struct command *cmd, const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { const jsmntok_t *toks = params, *methtok, *idtok; diff --git a/lightningd/test/run-param.c b/lightningd/test/run-param.c index c46c64c68257..e5b090eded5b 100644 --- a/lightningd/test/run-param.c +++ b/lightningd/test/run-param.c @@ -507,7 +507,9 @@ static void json_tok_tests(void) test_cb(json_tok_percent, double, "[ 'wow' ]", 0, false); } -static void test_invoice(struct command *cmd, const char *buffer, +static void test_invoice(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { u64 *msatoshi_val; @@ -546,7 +548,7 @@ static void usage(void) cmd->mode = CMD_USAGE; cmd->json_cmd = &invoice_command; - cmd->json_cmd->dispatch(cmd, NULL, NULL); + cmd->json_cmd->dispatch(cmd, NULL, NULL, NULL); assert(streq(cmd->usage, "msatoshi label description " diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 5f4a89838aaa..a3771d43e098 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -85,7 +85,9 @@ static void wallet_withdrawal_broadcast(struct bitcoind *bitcoind UNUSED, * the HSM to generate the signatures. */ static void json_withdraw(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { const jsmntok_t *desttok, *sattok; struct withdrawal *withdraw = tal(cmd, struct withdrawal); @@ -239,8 +241,10 @@ static bool json_tok_newaddr(struct command *cmd, const char *name, return false; } -static void json_newaddr(struct command *cmd, const char *buffer UNUSED, - const jsmntok_t *params UNUSED) +static void json_newaddr(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct json_stream *response; struct ext_key ext; @@ -299,7 +303,9 @@ static const struct json_command newaddr_command = { AUTODATA(json_command, &newaddr_command); static void json_listaddrs(struct command *cmd, - const char *buffer, const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct json_stream *response; struct ext_key ext; @@ -376,8 +382,10 @@ static const struct json_command listaddrs_command = { }; AUTODATA(json_command, &listaddrs_command); -static void json_listfunds(struct command *cmd, const char *buffer UNUSED, - const jsmntok_t *params UNUSED) +static void json_listfunds(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct json_stream *response; struct peer *p; @@ -503,8 +511,9 @@ static void process_utxo_result(struct bitcoind *bitcoind, } static void json_dev_rescan_outputs(struct command *cmd, - const char *buffer UNUSED, - const jsmntok_t *params UNUSED) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct txo_rescan *rescan = tal(cmd, struct txo_rescan); From c1f7bbc638eec01da65266ad4a7b8c5b6e5be7ff Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:05:56 +1030 Subject: [PATCH 07/13] lightningd: expose lower-level APIs. We need these for literal copying of requests between plugin and client. Signed-off-by: Rusty Russell --- lightningd/json_stream.c | 9 ++++++--- lightningd/json_stream.h | 9 +++++++++ lightningd/jsonrpc.c | 28 +++++++++++++++------------- lightningd/jsonrpc.h | 4 ++++ 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c index 11ab28150142..3ddf00b6796b 100644 --- a/lightningd/json_stream.c +++ b/lightningd/json_stream.c @@ -95,15 +95,18 @@ static void js_written_some(struct json_stream *js) io_wake(js); } -void json_stream_append(struct json_stream *js, const char *str) +void json_stream_append_part(struct json_stream *js, const char *str, size_t len) { - size_t len = strlen(str); - mkroom(js, len); memcpy(membuf_add(&js->outbuf, len), str, len); js_written_some(js); } +void json_stream_append(struct json_stream *js, const char *str) +{ + json_stream_append_part(js, str, strlen(str)); +} + static void json_stream_append_vfmt(struct json_stream *js, const char *fmt, va_list ap) { diff --git a/lightningd/json_stream.h b/lightningd/json_stream.h index 29c05fce3623..64632da39f87 100644 --- a/lightningd/json_stream.h +++ b/lightningd/json_stream.h @@ -55,6 +55,15 @@ void json_object_end(struct json_stream *js); */ void json_stream_append(struct json_stream *js, const char *str); +/** + * json_stream_append_part - literally insert part of string into json_stream. + * @js: the json_stream. + * @str: the string. + * @len: the length to append (<= strlen(str)). + */ +void json_stream_append_part(struct json_stream *js, const char *str, + size_t len); + /** * json_stream_append_fmt - insert formatted string into the json_stream. * @js: the json_stream. diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 857ac92ac952..e969e64a95bc 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -386,14 +386,9 @@ static void destroy_command(struct command *cmd) list_del_from(&cmd->jcon->commands, &cmd->list); } -void command_success(struct command *cmd, struct json_stream *result) +void command_raw_complete(struct command *cmd, struct json_stream *result) { - assert(cmd); - assert(cmd->have_json_stream); - json_stream_append(result, " }\n\n"); json_stream_close(result, cmd); - if (cmd->ok) - *(cmd->ok) = true; /* If we have a jcon, it will free result for us. */ if (cmd->jcon) @@ -402,19 +397,26 @@ void command_success(struct command *cmd, struct json_stream *result) tal_free(cmd); } +void command_success(struct command *cmd, struct json_stream *result) +{ + assert(cmd); + assert(cmd->have_json_stream); + json_stream_append(result, " }\n\n"); + if (cmd->ok) + *(cmd->ok) = true; + + command_raw_complete(cmd, result); +} + void command_failed(struct command *cmd, struct json_stream *result) { assert(cmd->have_json_stream); /* Have to close error */ json_stream_append(result, " } }\n\n"); - json_stream_close(result, cmd); if (cmd->ok) *(cmd->ok) = false; - /* If we have a jcon, it will free result for us. */ - if (cmd->jcon) - tal_steal(cmd->jcon, result); - tal_free(cmd); + command_raw_complete(cmd, result); } void PRINTF_FMT(3, 4) command_fail(struct command *cmd, int code, @@ -456,7 +458,7 @@ static void json_command_malformed(struct json_connection *jcon, json_stream_close(js, NULL); } -static struct json_stream *attach_json_stream(struct command *cmd) +struct json_stream *json_stream_raw_for_cmd(struct command *cmd) { struct json_stream *js; @@ -473,7 +475,7 @@ static struct json_stream *attach_json_stream(struct command *cmd) static struct json_stream *json_start(struct command *cmd) { - struct json_stream *js = attach_json_stream(cmd); + struct json_stream *js = json_stream_raw_for_cmd(cmd); json_stream_append_fmt(js, "{ \"jsonrpc\": \"2.0\", \"id\" : %s, ", cmd->id); diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index bc37358874e5..4b6785d658a6 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -98,6 +98,10 @@ void PRINTF_FMT(3, 4) command_fail(struct command *cmd, int code, /* Mainly for documentation, that we plan to close this later. */ void command_still_pending(struct command *cmd); +/* For low-level JSON stream access: */ +struct json_stream *json_stream_raw_for_cmd(struct command *cmd); +void command_raw_complete(struct command *cmd, struct json_stream *result); + /** * Create a new jsonrpc to wrap all related information. * From 56b6742652f835765992de00d14734af6189b139 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:06:56 +1030 Subject: [PATCH 08/13] lightningd/plugin: simply patch requests through and don't interpret them (much). We simply look for the id token, and substitute it on the way in/out. We also need to make sure output is '\n\n' terminated. I started this because we weren't forwarding complex errors properly (we treated them as a string), but it's also a huge simplification. `struct plugin_rpc_request` is eliminated entirely: the information we need is actually inside `struct command` already. Signed-off-by: Rusty Russell --- lightningd/plugin.c | 285 ++++++++++++++++++++++---------------------- 1 file changed, 140 insertions(+), 145 deletions(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 3e2d4cd5f2eb..5325b7031635 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1,5 +1,6 @@ #include "lightningd/plugin.h" +#include #include #include #include @@ -61,19 +62,14 @@ struct plugin { struct plugin_request { u64 id; - struct plugin *plugin; - - /* Method to be called */ - const char *method; - - /* JSON encoded params, either a dict or an array */ - const char *json_params; - const char *response; - const jsmntok_t *resulttok, *errortok, *toks; struct json_stream *stream; - /* The response handler to be called on success or error */ - void (*cb)(const struct plugin_request *, void *); + /* The response handler to be called when plugin gives us an object. */ + void (*cb)(const struct plugin_request *, + const char *buffer, + const jsmntok_t *toks, + const jsmntok_t *idtok, + void *); void *arg; }; @@ -93,20 +89,6 @@ struct plugins { struct lightningd *ld; }; -/* Represents a pending JSON-RPC request that was forwarded to a - * plugin and is currently waiting for it to return the result. */ -struct plugin_rpc_request { - /* The json-serialized ID as it was passed to us by the - * client, will be used to return the result */ - const char *id; - - const char *method; - const char *params; - - struct plugin *plugin; - struct command *cmd; -}; - /* Simple storage for plugin options inbetween registering them on the * command line and passing them off to the plugin */ struct plugin_opt { @@ -203,25 +185,23 @@ static void PRINTF_FMT(2,3) plugin_kill(struct plugin *plugin, char *fmt, ...) /** * Create the header of a JSON-RPC request and return open stream. * - * This is a partial request, missing the params element, which the - * caller needs to add. We can't open it yet since we don't know - * whether it is supposed to be an object (name-value pairs) or an - * array. + * The caller needs to add the request to req->stream. */ static struct plugin_request * -plugin_request_new_(struct plugin *plugin, const char *method, - void (*cb)(const struct plugin_request *, void *), +plugin_request_new_(struct plugin *plugin, + void (*cb)(const struct plugin_request *, + const char *buffer, + const jsmntok_t *toks, + const jsmntok_t *idtok, + void *), void *arg) { static u64 next_request_id = 0; struct plugin_request *req = tal(plugin, struct plugin_request); - u64 request_id = next_request_id++; - req->id = request_id; - req->method = tal_strdup(req, method); + req->id = next_request_id++; req->cb = cb; req->arg = arg; - req->plugin = plugin; /* We will not concurrently drain, if we do we must set the * writer to non-NULL */ @@ -229,31 +209,27 @@ plugin_request_new_(struct plugin *plugin, const char *method, /* Add to map so we can find it later when routing the response */ uintmap_add(&plugin->plugins->pending_requests, req->id, req); - - json_object_start(req->stream, NULL); - json_add_string(req->stream, "jsonrpc", "2.0"); - json_add_string(req->stream, "method", method); - json_add_u64(req->stream, "id", request_id); return req; } -#define plugin_request_new(plugin, method, cb, arg) \ - plugin_request_new_( \ - (plugin), (method), \ - typesafe_cb_preargs(void, void *, (cb), (arg), \ - const struct plugin_request *), \ +#define plugin_request_new(plugin, cb, arg) \ + plugin_request_new_( \ + (plugin), \ + typesafe_cb_preargs(void, void *, (cb), (arg), \ + const struct plugin_request *, \ + const char *buffer, \ + const jsmntok_t *toks, \ + const jsmntok_t *idtok), \ (arg)) /** * Given a request, send it to the plugin. */ -static void plugin_request_queue(struct plugin_request *req) +static void plugin_request_queue(struct plugin *plugin, + struct plugin_request *req) { - /* Finish the `params` object and submit the request */ - json_object_end(req->stream); /* root element */ - json_stream_append(req->stream, "\n\n"); - *tal_arr_expand(&req->plugin->js_arr) = req->stream; - io_wake(req->plugin); + *tal_arr_expand(&plugin->js_arr) = req->stream; + io_wake(plugin); } /** @@ -267,7 +243,7 @@ static bool plugin_read_json_one(struct plugin *plugin) jsmntok_t *toks; bool valid; u64 id; - const jsmntok_t *idtok, *resulttok, *errortok; + const jsmntok_t *idtok; struct plugin_request *request; /* FIXME: This could be done more efficiently by storing the @@ -290,19 +266,15 @@ static bool plugin_read_json_one(struct plugin *plugin) return false; } - resulttok = json_get_member(plugin->buffer, toks, "result"); - errortok = json_get_member(plugin->buffer, toks, "error"); idtok = json_get_member(plugin->buffer, toks, "id"); if (!idtok) { plugin_kill(plugin, "JSON-RPC response does not contain an \"id\"-field"); return false; - } else if (!resulttok && !errortok) { - plugin_kill(plugin, "JSON-RPC response does not contain a \"result\" or \"error\" field"); - return false; } - /* We only send u64 ids, so if this fails it's a critical error */ + /* We only send u64 ids, so if this fails it's a critical error (note + * that this also works if id is inside a JSON string!). */ if (!json_to_u64(plugin->buffer, idtok, &id)) { plugin_kill(plugin, "JSON-RPC response \"id\"-field is not a u64"); return false; @@ -316,12 +288,7 @@ static bool plugin_read_json_one(struct plugin *plugin) } /* We expect the request->cb to copy if needed */ - request->response = plugin->buffer; - request->errortok = errortok; - request->resulttok = resulttok; - request->toks = toks; - request->cb(request, request->arg); - + request->cb(request, plugin->buffer, toks, idtok, request->arg); tal_free(request); uintmap_del(&plugin->plugins->pending_requests, id); @@ -478,25 +445,25 @@ static bool plugin_opt_add(struct plugin *plugin, const char *buffer, /* Iterate through the options in the manifest response, and add them * to the plugin and the command line options */ -static bool plugin_opts_add(const struct plugin_request *req) +static bool plugin_opts_add(struct plugin *plugin, + const char *buffer, + const jsmntok_t *resulttok) { - const char *buffer = req->plugin->buffer; - const jsmntok_t *options = - json_get_member(req->plugin->buffer, req->resulttok, "options"); + const jsmntok_t *options = json_get_member(buffer, resulttok, "options"); if (!options) { - plugin_kill(req->plugin, + plugin_kill(plugin, "\"result.options\" was not found in the manifest"); return false; } if (options->type != JSMN_ARRAY) { - plugin_kill(req->plugin, "\"result.options\" is not an array"); + plugin_kill(plugin, "\"result.options\" is not an array"); return false; } for (size_t i = 0; i < options->size; i++) - if (!plugin_opt_add(req->plugin, buffer, json_get_arr(options, i))) + if (!plugin_opt_add(plugin, buffer, json_get_arr(options, i))) return false; return true; @@ -508,92 +475,91 @@ static void plugin_rpcmethod_destroy(struct json_command *cmd, jsonrpc_command_remove(rpc, cmd->name); } +static void json_stream_forward_change_id(struct json_stream *stream, + const char *buffer, + const jsmntok_t *toks, + const jsmntok_t *idtok, + const char *new_id) +{ + /* We copy everything, but replace the id (maybe inside a string, we + * don't care) */ + json_stream_append_part(stream, buffer + toks->start, + idtok->start - toks->start); + json_stream_append(stream, new_id); + json_stream_append_part(stream, buffer + idtok->end, + toks->end - idtok->end); + + /* We promise it will end in '\n\n' */ + /* It's an object (with an id!): definitely can't be less that "{}" */ + assert(toks->end - toks->start >= 2); + if (buffer[toks->end-1] != '\n') + json_stream_append(stream, "\n\n"); + else if (buffer[toks->end-2] != '\n') + json_stream_append(stream, "\n"); +} + static void plugin_rpcmethod_cb(const struct plugin_request *req, - struct plugin_rpc_request *rpc_req) + const char *buffer, + const jsmntok_t *toks, + const jsmntok_t *idtok, + struct command *cmd) { struct json_stream *response; - const jsmntok_t *res; - assert(req->resulttok || req->errortok); - - if (req->errortok) { - res = req->errortok; - command_fail(rpc_req->cmd, PLUGIN_ERROR, "%.*s", - res->end - res->start, req->response + res->start); - tal_free(rpc_req); - return; - } - res = req->resulttok; - response = json_stream_success(rpc_req->cmd); + response = json_stream_raw_for_cmd(cmd); + json_stream_forward_change_id(response, buffer, toks, idtok, cmd->id); + command_raw_complete(cmd, response); +} - json_add_member(response, NULL, "%.*s", json_tok_len(res), - json_tok_contents(req->response, res)); +static struct plugin *find_plugin_for_command(struct command *cmd) +{ + struct plugins *plugins = cmd->ld->plugins; + struct plugin *plugin; - command_success(rpc_req->cmd, response); - tal_free(rpc_req); + /* Find the plugin that registered this RPC call */ + list_for_each(&plugins->plugins, plugin, list) { + for (size_t i=0; imethods); i++) { + if (streq(cmd->json_cmd->name, plugin->methods[i])) + return plugin; + } + } + /* This should never happen, it'd mean that a plugin didn't + * cleanup after dying */ + abort(); } static void plugin_rpcmethod_dispatch(struct command *cmd, const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) + const jsmntok_t *toks, + const jsmntok_t *params UNNEEDED) { - const jsmntok_t *toks = params, *methtok, *idtok; - struct plugin_rpc_request *request; - struct plugins *plugins = cmd->ld->plugins; + const jsmntok_t *idtok; struct plugin *plugin; struct plugin_request *req; + char id[STR_MAX_CHARS(u64)]; if (cmd->mode == CMD_USAGE) { + /* FIXME! */ cmd->usage = "[params]"; return; } - /* We're given the params, but we need to walk back to the - * root object, so just walk backwards until the current - * element has no parents, that's going to be the root - * element. */ - while (toks->parent != -1) - toks--; + plugin = find_plugin_for_command(cmd); - methtok = json_get_member(buffer, toks, "method"); + /* Find ID again (We've parsed them before, this should not fail!) */ idtok = json_get_member(buffer, toks, "id"); - /* We've parsed them before, these should not fail! */ - assert(idtok != NULL && methtok != NULL); - - request = tal(NULL, struct plugin_rpc_request); - request->method = tal_strndup(request, buffer + methtok->start, - methtok->end - methtok->start); - request->id = tal_strndup(request, buffer + idtok->start, - idtok->end - idtok->start); - request->params = tal_strndup(request, buffer + params->start, - params->end - params->start); - request->plugin = NULL; - request->cmd = cmd; - - /* Find the plugin that registered this RPC call */ - list_for_each(&plugins->plugins, plugin, list) { - for (size_t i=0; imethods); i++) { - if (streq(request->method, plugin->methods[i])) { - request->plugin = plugin; - goto found; - } - } - } + assert(idtok != NULL); -found: - /* This should never happen, it'd mean that a plugin didn't - * cleanup after dying */ - assert(request->plugin); + req = plugin_request_new(plugin, plugin_rpcmethod_cb, cmd); + snprintf(id, ARRAY_SIZE(id), "%"PRIu64, req->id); - tal_steal(request->plugin, request); - req = plugin_request_new(request->plugin, request->method, plugin_rpcmethod_cb, request); - json_stream_append_fmt(req->stream, ", \"params\": %s", request->params); - plugin_request_queue(req); + json_stream_forward_change_id(req->stream, buffer, toks, idtok, id); + plugin_request_queue(plugin, req); command_still_pending(cmd); } -static bool plugin_rpcmethod_add(struct plugin *plugin, const char *buffer, +static bool plugin_rpcmethod_add(struct plugin *plugin, + const char *buffer, const jsmntok_t *meth) { const jsmntok_t *nametok, *desctok, *longdesctok; @@ -651,23 +617,24 @@ static bool plugin_rpcmethod_add(struct plugin *plugin, const char *buffer, return true; } -static bool plugin_rpcmethods_add(const struct plugin_request *req) +static bool plugin_rpcmethods_add(struct plugin *plugin, + const char *buffer, + const jsmntok_t *resulttok) { - const char *buffer = req->plugin->buffer; const jsmntok_t *methods = - json_get_member(req->plugin->buffer, req->resulttok, "rpcmethods"); + json_get_member(buffer, resulttok, "rpcmethods"); if (!methods) return false; if (methods->type != JSMN_ARRAY) { - plugin_kill(req->plugin, + plugin_kill(plugin, "\"result.rpcmethods\" is not an array"); return false; } for (size_t i = 0; i < methods->size; i++) - if (!plugin_rpcmethod_add(req->plugin, buffer, + if (!plugin_rpcmethod_add(plugin, buffer, json_get_arr(methods, i))) return false; return true; @@ -682,21 +649,29 @@ static void plugin_manifest_timeout(struct plugin *plugin) /** * Callback for the plugin_manifest request. */ -static void plugin_manifest_cb(const struct plugin_request *req, struct plugin *plugin) +static void plugin_manifest_cb(const struct plugin_request *req, + const char *buffer, + const jsmntok_t *toks, + const jsmntok_t *idtok, + struct plugin *plugin) { + const jsmntok_t *resulttok; + /* Check if all plugins have replied to getmanifest, and break * if they are */ plugin->plugins->pending_manifests--; if (plugin->plugins->pending_manifests == 0) io_break(plugin->plugins); - if (req->resulttok->type != JSMN_OBJECT) { + resulttok = json_get_member(buffer, toks, "result"); + if (!resulttok || resulttok->type != JSMN_OBJECT) { plugin_kill(plugin, - "\"getmanifest\" response is not an object"); + "\"getmanifest\" result is not an object"); return; } - if (!plugin_opts_add(req) || !plugin_rpcmethods_add(req)) + if (!plugin_opts_add(plugin, buffer, resulttok) + || !plugin_rpcmethods_add(plugin, buffer, resulttok)) plugin_kill(plugin, "Failed to register options or methods"); /* Reset timer, it'd kill us otherwise. */ tal_free(plugin->timeout_timer); @@ -768,6 +743,22 @@ void clear_plugins(struct plugins *plugins) tal_free(p); } +/* For our own "getmanifest" and "init" requests: starts params[] */ +static void start_simple_request(struct plugin_request *req, const char *reqname) +{ + json_object_start(req->stream, NULL); + json_add_string(req->stream, "jsonrpc", "2.0"); + json_add_string(req->stream, "method", reqname); + json_add_u64(req->stream, "id", req->id); +} + +static void end_simple_request(struct plugin *plugin, struct plugin_request *req) +{ + json_object_end(req->stream); + json_stream_append(req->stream, "\n\n"); + plugin_request_queue(plugin, req); +} + void plugins_init(struct plugins *plugins, const char *dev_plugin_debug) { struct plugin *p; @@ -799,10 +790,11 @@ void plugins_init(struct plugins *plugins, const char *dev_plugin_debug) * write-only on p->stdout */ io_new_conn(p, stdout, plugin_stdout_conn_init, p); io_new_conn(p, stdin, plugin_stdin_conn_init, p); - req = plugin_request_new(p, "getmanifest", plugin_manifest_cb, p); + req = plugin_request_new(p, plugin_manifest_cb, p); + start_simple_request(req, "getmanifest"); json_array_start(req->stream, "params"); json_array_end(req->stream); - plugin_request_queue(req); + end_simple_request(p, req); plugins->pending_manifests++; /* Don't timeout if they're running a debugger. */ if (debug) @@ -826,6 +818,9 @@ void plugins_init(struct plugins *plugins, const char *dev_plugin_debug) } static void plugin_config_cb(const struct plugin_request *req, + const char *buffer, + const jsmntok_t *toks, + const jsmntok_t *idtok, struct plugin *plugin) { /* Nothing to be done here, this is just a report */ @@ -842,7 +837,8 @@ static void plugin_config(struct plugin *plugin) struct lightningd *ld = plugin->plugins->ld; /* No writer since we don't flush concurrently. */ - req = plugin_request_new(plugin, "init", plugin_config_cb, plugin); + req = plugin_request_new(plugin, plugin_config_cb, plugin); + start_simple_request(req, "init"); json_object_start(req->stream, "params"); /* start of .params */ /* Add .params.options */ @@ -861,8 +857,7 @@ static void plugin_config(struct plugin *plugin) json_object_end(req->stream); json_object_end(req->stream); /* end of .params */ - - plugin_request_queue(req); + end_simple_request(plugin, req); } void plugins_config(struct plugins *plugins) From 1be6e7cd7a3c82a05e9eb361e8b6d2358bd9ad4b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:07:56 +1030 Subject: [PATCH 09/13] common/json: add context arg to json_parse_input. All callers currently just hand the same arg twice, but plugins might want this different. Signed-off-by: Rusty Russell --- common/json.c | 5 +++-- common/json.h | 3 ++- common/test/run-json_remove.c | 2 +- lightningd/bitcoind.c | 8 +++++--- lightningd/jsonrpc.c | 2 +- lightningd/plugin.c | 3 ++- lightningd/test/run-jsonrpc.c | 6 +++--- 7 files changed, 17 insertions(+), 12 deletions(-) diff --git a/common/json.c b/common/json.c index 549e5445f7c2..0492e5d38b03 100644 --- a/common/json.c +++ b/common/json.c @@ -172,13 +172,14 @@ const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index) return NULL; } -jsmntok_t *json_parse_input(const char *input, int len, bool *valid) +jsmntok_t *json_parse_input(const tal_t *ctx, + const char *input, int len, bool *valid) { jsmn_parser parser; jsmntok_t *toks; int ret; - toks = tal_arr(input, jsmntok_t, 10); + toks = tal_arr(ctx, jsmntok_t, 10); toks[0].type = JSMN_UNDEFINED; jsmn_init(&parser); diff --git a/common/json.h b/common/json.h index 38120ea15683..9c3328fd5b6e 100644 --- a/common/json.h +++ b/common/json.h @@ -55,7 +55,8 @@ const jsmntok_t *json_get_member(const char *buffer, const jsmntok_t tok[], const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index); /* If input is complete and valid, return tokens. */ -jsmntok_t *json_parse_input(const char *input, int len, bool *valid); +jsmntok_t *json_parse_input(const tal_t *ctx, + const char *input, int len, bool *valid); /* Convert a jsmntype_t enum to a human readable string. */ const char *jsmntype_to_string(jsmntype_t t); diff --git a/common/test/run-json_remove.c b/common/test/run-json_remove.c index 5328e684ac1b..f20df9654e76 100644 --- a/common/test/run-json_remove.c +++ b/common/test/run-json_remove.c @@ -25,7 +25,7 @@ static struct json *json_parse(const tal_t * ctx, const char *str) j->buffer = tal_strdup(j, str); convert_quotes(j->buffer); bool ok; - j->toks = json_parse_input(j->buffer, strlen(j->buffer), &ok); + j->toks = json_parse_input(j, j->buffer, strlen(j->buffer), &ok); assert(ok); j->toks = json_tok_copy(j, j->toks); return j; diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 918e891d27eb..a9693667f211 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -309,7 +309,7 @@ static bool extract_feerate(struct bitcoin_cli *bcli, const jsmntok_t *tokens, *feeratetok; bool valid; - tokens = json_parse_input(output, output_bytes, &valid); + tokens = json_parse_input(output, output, output_bytes, &valid); if (!tokens) fatal("%s: %s response", bcli_args(tmpctx, bcli), @@ -541,7 +541,8 @@ static bool process_gettxout(struct bitcoin_cli *bcli) return true; } - tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid); + tokens = json_parse_input(bcli->output, bcli->output, bcli->output_bytes, + &valid); if (!tokens) fatal("%s: %s response", bcli_args(tmpctx, bcli), valid ? "partial" : "invalid"); @@ -601,7 +602,8 @@ static bool process_getblock(struct bitcoin_cli *bcli) struct bitcoin_txid txid; bool valid; - tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid); + tokens = json_parse_input(bcli->output, bcli->output, bcli->output_bytes, + &valid); if (!tokens) { /* Most likely we are running on a pruned node, call * the callback with NULL to indicate failure */ diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index e969e64a95bc..213d5de9ad84 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -652,7 +652,7 @@ static struct io_plan *read_json(struct io_conn *conn, return io_wait(conn, conn, read_json, jcon); } - toks = json_parse_input(jcon->buffer, jcon->used, &valid); + toks = json_parse_input(jcon->buffer, jcon->buffer, jcon->used, &valid); if (!toks) { if (!valid) { log_unusual(jcon->log, diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 5325b7031635..0d7150bba2d5 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -249,7 +249,8 @@ static bool plugin_read_json_one(struct plugin *plugin) /* FIXME: This could be done more efficiently by storing the * toks and doing an incremental parse, like lightning-cli * does. */ - toks = json_parse_input(plugin->buffer, plugin->used, &valid); + toks = json_parse_input(plugin->buffer, plugin->buffer, plugin->used, + &valid); if (!toks) { if (!valid) { plugin_kill(plugin, "Failed to parse JSON response '%.*s'", diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 146ae5d5f13a..24f5dbee742b 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -76,7 +76,7 @@ static int test_json_filter(void) str = tal_strndup(result, membuf_elems(&result->outbuf), membuf_num_elems(&result->outbuf)); - toks = json_parse_input(str, strlen(str), &valid); + toks = json_parse_input(str, str, strlen(str), &valid); assert(valid); assert(toks); @@ -174,7 +174,7 @@ static void test_json_stream(void) * timeout. */ input = "{\"x\":\"x\"}{\"y\":\"y\"}"; talstr = tal_strndup(NULL, input, strlen(input)); - toks = json_parse_input(talstr, strlen(talstr), &valid); + toks = json_parse_input(talstr, talstr, strlen(talstr), &valid); assert(toks); assert(tal_count(toks) == 4); assert(toks[0].start == 0 && toks[0].end == 9); @@ -185,7 +185,7 @@ static void test_json_stream(void) * accidentally getting the boundaries to match. */ input = "{\"x\":\"x\"}{\"y\":\"y\"}{\"z\":\"z"; talstr = tal_strndup(NULL, input, strlen(input)); - toks = json_parse_input(talstr, strlen(talstr), &valid); + toks = json_parse_input(talstr, talstr, strlen(talstr), &valid); assert(toks); assert(tal_count(toks) == 4); assert(toks[0].start == 0 && toks[0].end == 9); From 63f215ff59ded9d31ae923cfd85eb49176771b76 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:08:56 +1030 Subject: [PATCH 10/13] json: rename json_tok_bitcoin_amount. json_tok* is used with 'struct command', so rename this to match the other low-level json tok helpers. Signed-off-by: Rusty Russell --- common/json.c | 4 ++-- common/json.h | 4 ++-- common/test/run-json.c | 2 +- lightningd/bitcoind.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/json.c b/common/json.c index 0492e5d38b03..3af957d51f8b 100644 --- a/common/json.c +++ b/common/json.c @@ -82,8 +82,8 @@ bool json_to_number(const char *buffer, const jsmntok_t *tok, return true; } -bool json_tok_bitcoin_amount(const char *buffer, const jsmntok_t *tok, - uint64_t *satoshi) +bool json_to_bitcoin_amount(const char *buffer, const jsmntok_t *tok, + uint64_t *satoshi) { char *end; unsigned long btc, sat; diff --git a/common/json.h b/common/json.h index 9c3328fd5b6e..730430389e91 100644 --- a/common/json.h +++ b/common/json.h @@ -35,8 +35,8 @@ bool json_to_u64(const char *buffer, const jsmntok_t *tok, bool json_to_double(const char *buffer, const jsmntok_t *tok, double *num); /* Extract satoshis from this (may be a string, or a decimal number literal) */ -bool json_tok_bitcoin_amount(const char *buffer, const jsmntok_t *tok, - uint64_t *satoshi); +bool json_to_bitcoin_amount(const char *buffer, const jsmntok_t *tok, + uint64_t *satoshi); /* Is this a number? [0..9]+ */ bool json_tok_is_num(const char *buffer, const jsmntok_t *tok); diff --git a/common/test/run-json.c b/common/test/run-json.c index db52d6b01e84..cea278fa5738 100644 --- a/common/test/run-json.c +++ b/common/test/run-json.c @@ -18,7 +18,7 @@ static void do_json_tok_bitcoin_amount(const char* val, uint64_t expected) fprintf(stderr, "do_json_tok_bitcoin_amount(\"%s\", %"PRIu64"): ", val, expected); - assert(json_tok_bitcoin_amount(val, &tok, &amount) == true); + assert(json_to_bitcoin_amount(val, &tok, &amount) == true); assert(amount == expected); fprintf(stderr, "ok\n"); diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index a9693667f211..e3fedec4f797 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -327,7 +327,7 @@ static bool extract_feerate(struct bitcoin_cli *bcli, if (!feeratetok) return false; - return json_tok_bitcoin_amount(output, feeratetok, feerate); + return json_to_bitcoin_amount(output, feeratetok, feerate); } struct estimatefee { @@ -558,7 +558,7 @@ static bool process_gettxout(struct bitcoin_cli *bcli) bcli_args(tmpctx, bcli), (int)bcli->output_bytes, bcli->output); - if (!json_tok_bitcoin_amount(bcli->output, valuetok, &out.amount)) + if (!json_to_bitcoin_amount(bcli->output, valuetok, &out.amount)) fatal("%s: had bad value (%.*s)?", bcli_args(tmpctx, bcli), (int)bcli->output_bytes, bcli->output); From ebb651132662eaccf831acef073563a903513f51 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:09:25 +1030 Subject: [PATCH 11/13] param: abstract 'struct command' so param doesn't need to access it. I want to use param functions in plugins, and they don't have struct command. I had to use a special arg to param() for check to flag it as allowing extra parameters, rather than adding a one-use accessor. Signed-off-by: Rusty Russell --- common/Makefile | 2 +- common/json_command.h | 24 ++++++++++++++++++++++++ lightningd/jsonrpc.c | 20 +++++++++++++++++--- lightningd/jsonrpc.h | 2 -- lightningd/param.c | 36 ++++++++++++++++++++++-------------- lightningd/param.h | 2 ++ lightningd/test/run-param.c | 20 +++++++++++++++++--- 7 files changed, 83 insertions(+), 23 deletions(-) create mode 100644 common/json_command.h diff --git a/common/Makefile b/common/Makefile index e7f098d05c8d..378f1ed9cd76 100644 --- a/common/Makefile +++ b/common/Makefile @@ -52,7 +52,7 @@ COMMON_SRC_NOGEN := \ COMMON_SRC_GEN := common/gen_status_wire.c common/gen_peer_status_wire.c -COMMON_HEADERS_NOGEN := $(COMMON_SRC_NOGEN:.c=.h) common/overflows.h common/htlc.h common/status_levels.h +COMMON_HEADERS_NOGEN := $(COMMON_SRC_NOGEN:.c=.h) common/overflows.h common/htlc.h common/status_levels.h common/json_command.h COMMON_HEADERS_GEN := common/gen_htlc_state_names.h common/gen_status_wire.h common/gen_peer_status_wire.h COMMON_HEADERS := $(COMMON_HEADERS_GEN) $(COMMON_HEADERS_NOGEN) diff --git a/common/json_command.h b/common/json_command.h new file mode 100644 index 000000000000..27140ae71127 --- /dev/null +++ b/common/json_command.h @@ -0,0 +1,24 @@ +/* These functions must be supplied by any binary linking with common/param + * so it can fail commands. */ +#ifndef LIGHTNING_COMMON_JSON_COMMAND_H +#define LIGHTNING_COMMON_JSON_COMMAND_H +#include "config.h" +#include +#include + +struct command; + +/* Caller supplied this: param assumes it can call it. */ +void PRINTF_FMT(3, 4) command_fail(struct command *cmd, int code, + const char *fmt, ...); + +/* Also caller supplied: is this invoked simply to get usage? */ +bool command_usage_only(const struct command *cmd); + +/* If so, this is called. */ +void command_set_usage(struct command *cmd, const char *usage); + +/* Also caller supplied: is this invoked simply to check parameters? */ +bool command_check_only(const struct command *cmd); + +#endif /* LIGHTNING_COMMON_JSON_COMMAND_H */ diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 213d5de9ad84..36d424b4a3a1 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -551,7 +552,6 @@ static void parse_request(struct json_connection *jcon, const jsmntok_t tok[]) json_tok_len(id)); c->mode = CMD_NORMAL; c->ok = NULL; - c->allow_unused = false; list_add_tail(&jcon->commands, &c->list); tal_add_destructor(c, destroy_command); @@ -776,6 +776,21 @@ struct jsonrpc *jsonrpc_new(const tal_t *ctx, struct lightningd *ld) return jsonrpc; } +bool command_usage_only(const struct command *cmd) +{ + return cmd->mode == CMD_USAGE; +} + +void command_set_usage(struct command *cmd, const char *usage) +{ + cmd->usage = usage; +} + +bool command_check_only(const struct command *cmd) +{ + return cmd->mode == CMD_CHECK; +} + void jsonrpc_listen(struct jsonrpc *jsonrpc, struct lightningd *ld) { struct sockaddr_un addr; @@ -980,11 +995,11 @@ static void json_check(struct command *cmd, mod_params = NULL; } else { mod_params = json_tok_copy(cmd, params); - cmd->allow_unused = true; } if (!param(cmd, buffer, mod_params, p_req("command_to_check", json_tok_command, &name_tok), + p_opt_any(), NULL)) return; @@ -995,7 +1010,6 @@ static void json_check(struct command *cmd, json_tok_remove(&mod_params, (jsmntok_t *)name_tok, 1); cmd->mode = CMD_CHECK; - cmd->allow_unused = false; /* FIXME(wythe): Maybe change "ok" to "failed" since that's really what * we're after and would be more clear. */ ok = true; diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 4b6785d658a6..c7c35634e77b 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -38,8 +38,6 @@ struct command { /* This is created if mode is CMD_USAGE */ const char *usage; bool *ok; - /* Do not report unused parameters as errors (default false). */ - bool allow_unused; /* Have we started a json stream already? For debugging. */ bool have_json_stream; }; diff --git a/lightningd/param.c b/lightningd/param.c index bc1397755942..34f0999fad95 100644 --- a/lightningd/param.c +++ b/lightningd/param.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -63,7 +64,8 @@ static bool post_check(struct command *cmd, struct param *params) static bool parse_by_position(struct command *cmd, struct param *params, const char *buffer, - const jsmntok_t tokens[]) + const jsmntok_t tokens[], + bool allow_extra) { const jsmntok_t *tok = tokens + 1; const jsmntok_t *end = json_next(tokens); @@ -79,7 +81,7 @@ static bool parse_by_position(struct command *cmd, } /* check for unexpected trailing params */ - if (!cmd->allow_unused && tok != end) { + if (!allow_extra && tok != end) { command_fail(cmd, JSONRPC2_INVALID_PARAMS, "too many parameters:" " got %u, expected %zu", @@ -108,7 +110,8 @@ static struct param *find_param(struct param *params, const char *start, static bool parse_by_name(struct command *cmd, struct param *params, const char *buffer, - const jsmntok_t tokens[]) + const jsmntok_t tokens[], + bool allow_extra) { const jsmntok_t *first = tokens + 1; const jsmntok_t *last = json_next(tokens); @@ -117,7 +120,7 @@ static bool parse_by_name(struct command *cmd, struct param *p = find_param(params, buffer + first->start, first->end - first->start); if (!p) { - if (!cmd->allow_unused) { + if (!allow_extra) { command_fail(cmd, JSONRPC2_INVALID_PARAMS, "unknown parameter: '%.*s'", first->end - first->start, @@ -239,7 +242,8 @@ static char *param_usage(const tal_t *ctx, static bool param_arr(struct command *cmd, const char *buffer, const jsmntok_t tokens[], - struct param *params) + struct param *params, + bool allow_extra) { #if DEVELOPER if (!check_params(params)) { @@ -248,9 +252,9 @@ static bool param_arr(struct command *cmd, const char *buffer, } #endif if (tokens->type == JSMN_ARRAY) - return parse_by_position(cmd, params, buffer, tokens); + return parse_by_position(cmd, params, buffer, tokens, allow_extra); else if (tokens->type == JSMN_OBJECT) - return parse_by_name(cmd, params, buffer, tokens); + return parse_by_name(cmd, params, buffer, tokens, allow_extra); command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Expected array or object for params"); @@ -263,12 +267,17 @@ bool param(struct command *cmd, const char *buffer, struct param *params = tal_arr(cmd, struct param, 0); const char *name; va_list ap; + bool allow_extra = false; va_start(ap, tokens); while ((name = va_arg(ap, const char *)) != NULL) { bool required = va_arg(ap, int); param_cbx cbx = va_arg(ap, param_cbx); void *arg = va_arg(ap, void *); + if (streq(name, "")) { + allow_extra = true; + continue; + } if (!param_add(¶ms, name, required, cbx, arg)) { command_fail(cmd, PARAM_DEV_ERROR, "developer error: param_add %s", name); @@ -278,14 +287,13 @@ bool param(struct command *cmd, const char *buffer, } va_end(ap); - if (cmd->mode == CMD_USAGE) { - cmd->usage = param_usage(cmd, params); + if (command_usage_only(cmd)) { + command_set_usage(cmd, param_usage(cmd, params)); return false; } - /* Always return false for CMD_USAGE and CMD_CHECK, signaling the caller - * to return immediately. For CMD_NORMAL, return true if all parameters - * are valid. - */ - return param_arr(cmd, buffer, tokens, params) && cmd->mode == CMD_NORMAL; + /* Always return false if we're simply checking command parameters; + * normally this returns true if all parameters are valid. */ + return param_arr(cmd, buffer, tokens, params, allow_extra) + && !command_check_only(cmd); } diff --git a/lightningd/param.h b/lightningd/param.h index 039dde46cf24..0701444c6b72 100644 --- a/lightningd/param.h +++ b/lightningd/param.h @@ -90,4 +90,6 @@ typedef bool(*param_cbx)(struct command *cmd, (const jsmntok_t *)NULL, \ (arg)) == true); }) +/* Special flag for 'check' which allows any parameters. */ +#define p_opt_any() "", false, NULL, NULL #endif /* LIGHTNING_LIGHTNINGD_PARAM_H */ diff --git a/lightningd/test/run-param.c b/lightningd/test/run-param.c index e5b090eded5b..765e4b9ec211 100644 --- a/lightningd/test/run-param.c +++ b/lightningd/test/run-param.c @@ -48,6 +48,21 @@ bool json_feerate_estimate(struct command *cmd UNNEEDED, { fprintf(stderr, "json_feerate_estimate called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ +void command_set_usage(struct command *cmd, const char *usage) +{ + cmd->usage = usage; +} + +bool command_usage_only(const struct command *cmd) +{ + return cmd->mode == CMD_USAGE; +} + +bool command_check_only(const struct command *cmd) +{ + return cmd->mode == CMD_CHECK; +} + struct json { jsmntok_t *toks; char *buffer; @@ -353,7 +368,7 @@ static void five_hundred_params(void) /* first test object version */ struct json *j = json_parse(params, obj); - assert(param_arr(cmd, j->buffer, j->toks, params)); + assert(param_arr(cmd, j->buffer, j->toks, params, false)); for (int i = 0; i < tal_count(ints); ++i) { assert(ints[i]); assert(*ints[i] == i); @@ -362,7 +377,7 @@ static void five_hundred_params(void) /* now test array */ j = json_parse(params, arr); - assert(param_arr(cmd, j->buffer, j->toks, params)); + assert(param_arr(cmd, j->buffer, j->toks, params, false)); for (int i = 0; i < tal_count(ints); ++i) { assert(*ints[i] == i); } @@ -563,7 +578,6 @@ int main(void) setup_tmpctx(); cmd = tal(tmpctx, struct command); cmd->mode = CMD_NORMAL; - cmd->allow_unused = false; fail_msg = tal_arr(cmd, char, 10000); zero_params(); From 92cb984bcc3efede25c7f8a134a47e23989a104d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:09:27 +1030 Subject: [PATCH 12/13] jsonrpc: remove ok pointer. We can use the 'destructor-canary' trick instead. Signed-off-by: Rusty Russell --- lightningd/jsonrpc.c | 21 ++++++++++----------- lightningd/jsonrpc.h | 1 - 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 36d424b4a3a1..f7dff296cf8f 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -403,8 +403,6 @@ void command_success(struct command *cmd, struct json_stream *result) assert(cmd); assert(cmd->have_json_stream); json_stream_append(result, " }\n\n"); - if (cmd->ok) - *(cmd->ok) = true; command_raw_complete(cmd, result); } @@ -414,8 +412,6 @@ void command_failed(struct command *cmd, struct json_stream *result) assert(cmd->have_json_stream); /* Have to close error */ json_stream_append(result, " } }\n\n"); - if (cmd->ok) - *(cmd->ok) = false; command_raw_complete(cmd, result); } @@ -551,7 +547,6 @@ static void parse_request(struct json_connection *jcon, const jsmntok_t tok[]) json_tok_contents(jcon->buffer, id), json_tok_len(id)); c->mode = CMD_NORMAL; - c->ok = NULL; list_add_tail(&jcon->commands, &c->list); tal_add_destructor(c, destroy_command); @@ -981,6 +976,12 @@ static bool json_tok_command(struct command *cmd, const char *name, return false; } +/* We add this destructor as a canary to detect cmd failing. */ +static void destroy_command_canary(struct command *cmd, bool *failed) +{ + *failed = true; +} + static void json_check(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, @@ -988,7 +989,7 @@ static void json_check(struct command *cmd, { jsmntok_t *mod_params; const jsmntok_t *name_tok; - bool ok; + bool failed; struct json_stream *response; if (cmd->mode == CMD_USAGE) { @@ -1010,13 +1011,11 @@ static void json_check(struct command *cmd, json_tok_remove(&mod_params, (jsmntok_t *)name_tok, 1); cmd->mode = CMD_CHECK; - /* FIXME(wythe): Maybe change "ok" to "failed" since that's really what - * we're after and would be more clear. */ - ok = true; - cmd->ok = &ok; + failed = false; + tal_add_destructor2(cmd, destroy_command_canary, &failed); cmd->json_cmd->dispatch(cmd, buffer, mod_params, mod_params); - if (!ok) + if (failed) return; response = json_stream_success(cmd); diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index c7c35634e77b..ce5ae71ba64e 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -37,7 +37,6 @@ struct command { enum command_mode mode; /* This is created if mode is CMD_USAGE */ const char *usage; - bool *ok; /* Have we started a json stream already? For debugging. */ bool have_json_stream; }; From c31e59d178dba6af66b1c663bb387157f0c2d36d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Dec 2018 11:09:28 +1030 Subject: [PATCH 13/13] Move json and param core functionality into common, for plugins. json_escaped.[ch], param.[ch] and jsonrpc_errors.h move from lightningd/ to common/. Tests moved too. We add a new 'common/json_tok.[ch]' for the common parameter parsing routines which a plugin might want, taking them out of lightningd/json.c (which now only contains the lightningd-specific ones). The rest is mainly fixing up includes. Signed-off-by: Rusty Russell --- common/Makefile | 5 +- {lightningd => common}/json_escaped.c | 2 +- {lightningd => common}/json_escaped.h | 6 +- common/json_tok.c | 184 ++++++++++++++++++ common/json_tok.h | 72 +++++++ {lightningd => common}/jsonrpc_errors.h | 8 +- {lightningd => common}/param.c | 7 +- {lightningd => common}/param.h | 12 +- .../test/run-json_escaped.c | 0 {lightningd => common}/test/run-param.c | 41 ++-- common/wallet_tx.c | 3 +- lightningd/Makefile | 8 +- lightningd/chaintopology.c | 5 +- lightningd/connect_control.c | 5 +- lightningd/gossip_control.c | 7 +- lightningd/invoice.c | 8 +- lightningd/json.c | 182 +---------------- lightningd/json.h | 63 ------ lightningd/jsonrpc.c | 6 +- lightningd/jsonrpc.h | 6 +- lightningd/lightningd.c | 2 +- lightningd/log.c | 5 +- lightningd/memdump.c | 5 +- lightningd/opening_control.c | 5 +- lightningd/options.c | 7 +- lightningd/pay.c | 5 +- lightningd/payalgo.c | 5 +- lightningd/peer_control.c | 5 +- lightningd/peer_htlcs.c | 7 +- lightningd/ping.c | 5 +- lightningd/plugin.c | 4 +- lightningd/test/Makefile | 1 + lightningd/test/run-find_my_abspath.c | 3 - lightningd/test/run-invoice-select-inchan.c | 3 - lightningd/test/run-jsonrpc.c | 16 +- wallet/db.c | 2 +- wallet/walletrpc.c | 5 +- 37 files changed, 374 insertions(+), 341 deletions(-) rename {lightningd => common}/json_escaped.c (98%) rename {lightningd => common}/json_escaped.h (88%) create mode 100644 common/json_tok.c create mode 100644 common/json_tok.h rename {lightningd => common}/jsonrpc_errors.h (88%) rename {lightningd => common}/param.c (97%) rename {lightningd => common}/param.h (92%) rename {lightningd => common}/test/run-json_escaped.c (100%) rename {lightningd => common}/test/run-param.c (93%) diff --git a/common/Makefile b/common/Makefile index 378f1ed9cd76..7bd094b3005f 100644 --- a/common/Makefile +++ b/common/Makefile @@ -25,10 +25,13 @@ COMMON_SRC_NOGEN := \ common/initial_commit_tx.c \ common/io_lock.c \ common/json.c \ + common/json_escaped.c \ + common/json_tok.c \ common/key_derive.c \ common/keyset.c \ common/memleak.c \ common/msg_queue.c \ + common/param.c \ common/peer_billboard.c \ common/peer_failed.c \ common/permute_tx.c \ @@ -52,7 +55,7 @@ COMMON_SRC_NOGEN := \ COMMON_SRC_GEN := common/gen_status_wire.c common/gen_peer_status_wire.c -COMMON_HEADERS_NOGEN := $(COMMON_SRC_NOGEN:.c=.h) common/overflows.h common/htlc.h common/status_levels.h common/json_command.h +COMMON_HEADERS_NOGEN := $(COMMON_SRC_NOGEN:.c=.h) common/overflows.h common/htlc.h common/status_levels.h common/json_command.h common/jsonrpc_errors.h COMMON_HEADERS_GEN := common/gen_htlc_state_names.h common/gen_status_wire.h common/gen_peer_status_wire.h COMMON_HEADERS := $(COMMON_HEADERS_GEN) $(COMMON_HEADERS_NOGEN) diff --git a/lightningd/json_escaped.c b/common/json_escaped.c similarity index 98% rename from lightningd/json_escaped.c rename to common/json_escaped.c index 5e62548e50d9..d60654b9277d 100644 --- a/lightningd/json_escaped.c +++ b/common/json_escaped.c @@ -1,4 +1,4 @@ -#include +#include #include struct json_escaped *json_escaped_string_(const tal_t *ctx, diff --git a/lightningd/json_escaped.h b/common/json_escaped.h similarity index 88% rename from lightningd/json_escaped.h rename to common/json_escaped.h index 8ef77123d484..0f742a86f39f 100644 --- a/lightningd/json_escaped.h +++ b/common/json_escaped.h @@ -1,5 +1,5 @@ -#ifndef LIGHTNING_LIGHTNINGD_JSON_ESCAPED_H -#define LIGHTNING_LIGHTNINGD_JSON_ESCAPED_H +#ifndef LIGHTNING_COMMON_JSON_ESCAPED_H +#define LIGHTNING_COMMON_JSON_ESCAPED_H #include "config.h" #include @@ -32,4 +32,4 @@ struct json_escaped *json_escaped_string_(const tal_t *ctx, /* Be very careful here! Can fail! Doesn't handle \u: use UTF-8 please. */ const char *json_escaped_unescape(const tal_t *ctx, const struct json_escaped *esc); -#endif /* LIGHTNING_LIGHTNINGD_JSON_ESCAPED_H */ +#endif /* LIGHTNING_COMMON_JSON_ESCAPED_H */ diff --git a/common/json_tok.c b/common/json_tok.c new file mode 100644 index 000000000000..6e2732216802 --- /dev/null +++ b/common/json_tok.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +bool json_tok_array(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + const jsmntok_t **arr) +{ + if (tok->type == JSMN_ARRAY) + return (*arr = tok); + + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be an array, not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); + return false; +} + +bool json_tok_bool(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + bool **b) +{ + *b = tal(cmd, bool); + if (tok->type == JSMN_PRIMITIVE) { + if (memeqstr(buffer + tok->start, tok->end - tok->start, "true")) { + **b = true; + return true; + } + if (memeqstr(buffer + tok->start, tok->end - tok->start, "false")) { + **b = false; + return true; + } + } + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be 'true' or 'false', not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); + return false; +} + +bool json_tok_double(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + double **num) +{ + *num = tal(cmd, double); + if (json_to_double(buffer, tok, *num)) + return true; + + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be a double, not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); + return false; +} + +bool json_tok_escaped_string(struct command *cmd, const char *name, + const char * buffer, const jsmntok_t *tok, + const char **str) +{ + struct json_escaped *esc = json_to_escaped_string(cmd, buffer, tok); + if (esc) { + *str = json_escaped_unescape(cmd, esc); + if (*str) + return true; + } + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be a string, not '%.*s'" + " (note, we don't allow \\u)", + name, + tok->end - tok->start, buffer + tok->start); + return false; +} + +bool json_tok_string(struct command *cmd, const char *name, + const char * buffer, const jsmntok_t *tok, + const char **str) +{ + *str = tal_strndup(cmd, buffer + tok->start, + tok->end - tok->start); + return true; +} + +bool json_tok_label(struct command *cmd, const char *name, + const char * buffer, const jsmntok_t *tok, + struct json_escaped **label) +{ + /* We accept both strings and number literals here. */ + *label = json_escaped_string_(cmd, buffer + tok->start, tok->end - tok->start); + if (*label && (tok->type == JSMN_STRING || json_tok_is_num(buffer, tok))) + return true; + + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be a string or number, not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); + return false; +} + +bool json_tok_number(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + unsigned int **num) +{ + *num = tal(cmd, unsigned int); + if (json_to_number(buffer, tok, *num)) + return true; + + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be an integer, not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); + return false; +} + +bool json_tok_sha256(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct sha256 **hash) +{ + *hash = tal(cmd, struct sha256); + if (hex_decode(buffer + tok->start, + tok->end - tok->start, + *hash, sizeof(**hash))) + return true; + + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be a 32 byte hex value, not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); + return false; +} + +bool json_tok_msat(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t * tok, + u64 **msatoshi_val) +{ + if (json_tok_streq(buffer, tok, "any")) { + *msatoshi_val = NULL; + return true; + } + *msatoshi_val = tal(cmd, u64); + + if (json_to_u64(buffer, tok, *msatoshi_val) && *msatoshi_val != 0) + return true; + + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be a positive number or 'any', not '%.*s'", + name, + tok->end - tok->start, + buffer + tok->start); + return false; +} + +bool json_tok_percent(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + double **num) +{ + *num = tal(cmd, double); + if (json_to_double(buffer, tok, *num) && **num >= 0.0) + return true; + + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be a positive double, not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); + return false; +} + +bool json_tok_u64(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + uint64_t **num) +{ + *num = tal(cmd, uint64_t); + if (json_to_u64(buffer, tok, *num)) + return true; + + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be an unsigned 64 bit integer, not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); + return false; +} + +bool json_tok_tok(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t * tok, + const jsmntok_t **out) +{ + return (*out = tok); +} diff --git a/common/json_tok.h b/common/json_tok.h new file mode 100644 index 000000000000..b0d6d1898cf9 --- /dev/null +++ b/common/json_tok.h @@ -0,0 +1,72 @@ +/* Helpers for use with param parsing. */ +#ifndef LIGHTNING_COMMON_JSON_TOK_H +#define LIGHTNING_COMMON_JSON_TOK_H +#include "config.h" +#include + +struct command; + +/* Extract json array token */ +bool json_tok_array(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + const jsmntok_t **arr); + +/* Extract boolean this (must be a true or false) */ +bool json_tok_bool(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + bool **b); + +/* Extract double from this (must be a number literal) */ +bool json_tok_double(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + double **num); + +/* Extract an escaped string (and unescape it) */ +bool json_tok_escaped_string(struct command *cmd, const char *name, + const char * buffer, const jsmntok_t *tok, + const char **str); + +/* Extract a string */ +bool json_tok_string(struct command *cmd, const char *name, + const char * buffer, const jsmntok_t *tok, + const char **str); + +/* Extract a label. It is either an escaped string or a number. */ +bool json_tok_label(struct command *cmd, const char *name, + const char * buffer, const jsmntok_t *tok, + struct json_escaped **label); + +/* Extract number from this (may be a string, or a number literal) */ +bool json_tok_number(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + unsigned int **num); + +/* Extract sha256 hash */ +bool json_tok_sha256(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct sha256 **hash); + +/* Extract positive integer, or NULL if tok is 'any'. */ +bool json_tok_msat(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t * tok, + u64 **msatoshi_val); + +/* Extract double in range [0.0, 100.0] */ +bool json_tok_percent(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + double **num); + +/* Extract number from this (may be a string, or a number literal) */ +bool json_tok_u64(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + uint64_t **num); + +/* + * Set the address of @out to @tok. Used as a callback by handlers that + * want to unmarshal @tok themselves. + */ +bool json_tok_tok(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t * tok, + const jsmntok_t **out); + +#endif /* LIGHTNING_COMMON_JSON_TOK_H */ diff --git a/lightningd/jsonrpc_errors.h b/common/jsonrpc_errors.h similarity index 88% rename from lightningd/jsonrpc_errors.h rename to common/jsonrpc_errors.h index 65757e734c6c..5d91eaf660f0 100644 --- a/lightningd/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -1,8 +1,8 @@ -/* lightningd/jsonrpc_errors.h +/* common/jsonrpc_errors.h * Lists error codes for JSON-RPC. */ -#ifndef LIGHTNING_LIGHTNINGD_JSONRPC_ERRORS_H -#define LIGHTNING_LIGHTNINGD_JSONRPC_ERRORS_H +#ifndef LIGHTNING_COMMON_JSONRPC_ERRORS_H +#define LIGHTNING_COMMON_JSONRPC_ERRORS_H #include "config.h" /* Standard errors defined by JSON-RPC 2.0 standard */ @@ -44,4 +44,4 @@ #define INVOICE_LABEL_ALREADY_EXISTS 900 #define INVOICE_PREIMAGE_ALREADY_EXISTS 901 -#endif /* LIGHTNING_LIGHTNINGD_JSONRPC_ERRORS_H */ +#endif /* LIGHTNING_COMMON_JSONRPC_ERRORS_H */ diff --git a/lightningd/param.c b/common/param.c similarity index 97% rename from lightningd/param.c rename to common/param.c index 34f0999fad95..bcdd0773a5fd 100644 --- a/lightningd/param.c +++ b/common/param.c @@ -1,12 +1,9 @@ #include #include #include +#include +#include #include -#include -#include -#include -#include -#include struct param { const char *name; diff --git a/lightningd/param.h b/common/param.h similarity index 92% rename from lightningd/param.h rename to common/param.h index 0701444c6b72..2306114e29df 100644 --- a/lightningd/param.h +++ b/common/param.h @@ -1,6 +1,9 @@ -#ifndef LIGHTNING_LIGHTNINGD_PARAM_H -#define LIGHTNING_LIGHTNINGD_PARAM_H +#ifndef LIGHTNING_COMMON_PARAM_H +#define LIGHTNING_COMMON_PARAM_H #include "config.h" +#include +#include +#include /*~ Greetings adventurer! * @@ -28,8 +31,9 @@ * * All the command handlers throughout the code use this system. * json_invoice() is a great example. The common callbacks can be found in - * lightningd/json.c. Use them directly or feel free to write your own. + * common/json_tok.c. Use them directly or feel free to write your own. */ +struct command; /* * Parse the json tokens. @params can be an array of values or an object @@ -92,4 +96,4 @@ typedef bool(*param_cbx)(struct command *cmd, /* Special flag for 'check' which allows any parameters. */ #define p_opt_any() "", false, NULL, NULL -#endif /* LIGHTNING_LIGHTNINGD_PARAM_H */ +#endif /* LIGHTNING_COMMON_PARAM_H */ diff --git a/lightningd/test/run-json_escaped.c b/common/test/run-json_escaped.c similarity index 100% rename from lightningd/test/run-json_escaped.c rename to common/test/run-json_escaped.c diff --git a/lightningd/test/run-param.c b/common/test/run-param.c similarity index 93% rename from lightningd/test/run-param.c rename to common/test/run-param.c index 765e4b9ec211..cf86444ec875 100644 --- a/lightningd/test/run-param.c +++ b/common/test/run-param.c @@ -1,12 +1,11 @@ #include "config.h" #include "../json.c" #include "../json_escaped.c" -#include "../json_stream.c" +#include "../json_tok.c" #include "../param.c" #include #include #include -#include #include #include #include @@ -33,21 +32,20 @@ void command_fail(struct command *cmd, int code, const char *fmt, ...) } /* AUTOGENERATED MOCKS START */ -/* Generated stub for feerate_from_style */ -u32 feerate_from_style(u32 feerate UNNEEDED, enum feerate_style style UNNEEDED) -{ fprintf(stderr, "feerate_from_style called!\n"); abort(); } -/* Generated stub for feerate_name */ -const char *feerate_name(enum feerate feerate UNNEEDED) -{ fprintf(stderr, "feerate_name called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for json_feerate_estimate */ -bool json_feerate_estimate(struct command *cmd UNNEEDED, - u32 **feerate_per_kw UNNEEDED, enum feerate feerate UNNEEDED) -{ fprintf(stderr, "json_feerate_estimate called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ +/* We do this lightningd-style: */ +enum command_mode { + CMD_NORMAL, + CMD_USAGE, + CMD_CHECK +}; + +struct command { + enum command_mode mode; + const char *usage; +}; + void command_set_usage(struct command *cmd, const char *usage) { cmd->usage = usage; @@ -551,20 +549,9 @@ static void test_invoice(struct command *cmd, static void usage(void) { - /* Do this to simulate a call to our pretend handler (test_invoice) */ - struct json_command invoice_command = { - "invoice", - test_invoice, - "", - false, - "" - }; - cmd->mode = CMD_USAGE; - cmd->json_cmd = &invoice_command; - - cmd->json_cmd->dispatch(cmd, NULL, NULL, NULL); + test_invoice(cmd, NULL, NULL, NULL); assert(streq(cmd->usage, "msatoshi label description " "[expiry] [fallbacks] [preimage]")); diff --git a/common/wallet_tx.c b/common/wallet_tx.c index 88a36460b3e2..e627f0c7bdea 100644 --- a/common/wallet_tx.c +++ b/common/wallet_tx.c @@ -1,6 +1,7 @@ +#include +#include #include #include -#include #include void wtx_init(struct command *cmd, struct wallet_tx * wtx) diff --git a/lightningd/Makefile b/lightningd/Makefile index 62090aa4f252..d516b5e1d457 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -34,8 +34,11 @@ LIGHTNINGD_COMMON_OBJS := \ common/key_derive.o \ common/io_lock.o \ common/json.o \ + common/json_escaped.o \ + common/json_tok.o \ common/memleak.o \ common/msg_queue.o \ + common/param.o \ common/permute_tx.o \ common/pseudorand.o \ common/sphinx.o \ @@ -64,7 +67,6 @@ LIGHTNINGD_SRC := \ lightningd/htlc_end.c \ lightningd/invoice.c \ lightningd/json.c \ - lightningd/json_escaped.c \ lightningd/json_stream.c \ lightningd/jsonrpc.c \ lightningd/lightningd.c \ @@ -74,7 +76,6 @@ LIGHTNINGD_SRC := \ lightningd/onchain_control.c \ lightningd/opening_control.c \ lightningd/options.c \ - lightningd/param.c \ lightningd/pay.c \ lightningd/payalgo.c \ lightningd/peer_control.c \ @@ -92,8 +93,7 @@ ALL_OBJS += $(LIGHTNINGD_OBJS) # We accumulate all lightningd/ headers in these three: LIGHTNINGD_HEADERS_NOGEN = \ $(LIGHTNINGD_SRC:.c=.h) \ - lightningd/channel_state.h \ - lightningd/jsonrpc_errors.h + lightningd/channel_state.h # Generated headers LIGHTNINGD_HEADERS_GEN = \ diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 9fe3db2279aa..c070169dc2eb 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -13,14 +13,15 @@ #include #include #include +#include +#include #include +#include #include #include #include #include #include -#include -#include /* Mutual recursion via timer. */ static void try_extend_tip(struct chain_topology *topo); diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 68cc51d90ec4..b488f7225de7 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -4,7 +4,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -18,11 +21,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index a0230437f51f..8c3f2538a71f 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -11,6 +11,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -22,12 +26,9 @@ #include #include #include -#include #include -#include #include #include -#include #include #include #include diff --git a/lightningd/invoice.c b/lightningd/invoice.c index bade4f9304c8..2c527971f34f 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1,7 +1,6 @@ #include "invoice.h" #include "json.h" #include "jsonrpc.h" -#include "jsonrpc_errors.h" #include "lightningd.h" #include #include @@ -10,6 +9,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -18,11 +21,8 @@ #include #include #include -#include -#include #include #include -#include #include #include #include diff --git a/lightningd/json.c b/lightningd/json.c index 16c0068e970a..e94be2c34db9 100644 --- a/lightningd/json.c +++ b/lightningd/json.c @@ -3,15 +3,17 @@ #include #include #include +#include +#include +#include #include +#include #include #include #include #include -#include #include #include -#include #include #include #include @@ -95,175 +97,6 @@ void json_add_txid(struct json_stream *result, const char *fieldname, json_add_string(result, fieldname, hex); } -bool json_tok_array(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - const jsmntok_t **arr) -{ - if (tok->type == JSMN_ARRAY) - return (*arr = tok); - - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be an array, not '%.*s'", - name, tok->end - tok->start, buffer + tok->start); - return false; -} - -bool json_tok_bool(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - bool **b) -{ - *b = tal(cmd, bool); - if (tok->type == JSMN_PRIMITIVE) { - if (memeqstr(buffer + tok->start, tok->end - tok->start, "true")) { - **b = true; - return true; - } - if (memeqstr(buffer + tok->start, tok->end - tok->start, "false")) { - **b = false; - return true; - } - } - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be 'true' or 'false', not '%.*s'", - name, tok->end - tok->start, buffer + tok->start); - return false; -} - -bool json_tok_double(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - double **num) -{ - *num = tal(cmd, double); - if (json_to_double(buffer, tok, *num)) - return true; - - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be a double, not '%.*s'", - name, tok->end - tok->start, buffer + tok->start); - return false; -} - -bool json_tok_escaped_string(struct command *cmd, const char *name, - const char * buffer, const jsmntok_t *tok, - const char **str) -{ - struct json_escaped *esc = json_to_escaped_string(cmd, buffer, tok); - if (esc) { - *str = json_escaped_unescape(cmd, esc); - if (*str) - return true; - } - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be a string, not '%.*s'" - " (note, we don't allow \\u)", - name, - tok->end - tok->start, buffer + tok->start); - return false; -} - -bool json_tok_string(struct command *cmd, const char *name, - const char * buffer, const jsmntok_t *tok, - const char **str) -{ - *str = tal_strndup(cmd, buffer + tok->start, - tok->end - tok->start); - return true; -} - -bool json_tok_label(struct command *cmd, const char *name, - const char * buffer, const jsmntok_t *tok, - struct json_escaped **label) -{ - /* We accept both strings and number literals here. */ - *label = json_escaped_string_(cmd, buffer + tok->start, tok->end - tok->start); - if (*label && (tok->type == JSMN_STRING || json_tok_is_num(buffer, tok))) - return true; - - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be a string or number, not '%.*s'", - name, tok->end - tok->start, buffer + tok->start); - return false; -} - -bool json_tok_number(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - unsigned int **num) -{ - *num = tal(cmd, unsigned int); - if (json_to_number(buffer, tok, *num)) - return true; - - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be an integer, not '%.*s'", - name, tok->end - tok->start, buffer + tok->start); - return false; -} - -bool json_tok_sha256(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - struct sha256 **hash) -{ - *hash = tal(cmd, struct sha256); - if (hex_decode(buffer + tok->start, - tok->end - tok->start, - *hash, sizeof(**hash))) - return true; - - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be a 32 byte hex value, not '%.*s'", - name, tok->end - tok->start, buffer + tok->start); - return false; -} - -bool json_tok_msat(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t * tok, - u64 **msatoshi_val) -{ - if (json_tok_streq(buffer, tok, "any")) { - *msatoshi_val = NULL; - return true; - } - *msatoshi_val = tal(cmd, u64); - - if (json_to_u64(buffer, tok, *msatoshi_val) && *msatoshi_val != 0) - return true; - - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be a positive number or 'any', not '%.*s'", - name, - tok->end - tok->start, - buffer + tok->start); - return false; -} - -bool json_tok_percent(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - double **num) -{ - *num = tal(cmd, double); - if (json_to_double(buffer, tok, *num) && **num >= 0.0) - return true; - - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be a positive double, not '%.*s'", - name, tok->end - tok->start, buffer + tok->start); - return false; -} - -bool json_tok_u64(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - uint64_t **num) -{ - *num = tal(cmd, uint64_t); - if (json_to_u64(buffer, tok, *num)) - return true; - - command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be an unsigned 64 bit integer, not '%.*s'", - name, tok->end - tok->start, buffer + tok->start); - return false; -} - bool json_to_pubkey(const char *buffer, const jsmntok_t *tok, struct pubkey *pubkey) { @@ -470,13 +303,6 @@ void json_add_address_internal(struct json_stream *response, abort(); } -bool json_tok_tok(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t * tok, - const jsmntok_t **out) -{ - return (*out = tok); -} - void json_add_num(struct json_stream *result, const char *fieldname, unsigned int value) { json_add_member(result, fieldname, "%u", value); diff --git a/lightningd/json.h b/lightningd/json.h index e960eb7a7c13..00f723bd0672 100644 --- a/lightningd/json.h +++ b/lightningd/json.h @@ -47,56 +47,6 @@ void json_add_pubkey(struct json_stream *response, void json_add_txid(struct json_stream *result, const char *fieldname, const struct bitcoin_txid *txid); -/* Extract json array token */ -bool json_tok_array(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - const jsmntok_t **arr); - -/* Extract boolean this (must be a true or false) */ -bool json_tok_bool(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - bool **b); - -/* Extract double from this (must be a number literal) */ -bool json_tok_double(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - double **num); - -/* Extract an escaped string (and unescape it) */ -bool json_tok_escaped_string(struct command *cmd, const char *name, - const char * buffer, const jsmntok_t *tok, - const char **str); - -/* Extract a string */ -bool json_tok_string(struct command *cmd, const char *name, - const char * buffer, const jsmntok_t *tok, - const char **str); - -/* Extract a label. It is either an escaped string or a number. */ -bool json_tok_label(struct command *cmd, const char *name, - const char * buffer, const jsmntok_t *tok, - struct json_escaped **label); - -/* Extract number from this (may be a string, or a number literal) */ -bool json_tok_number(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - unsigned int **num); - -/* Extract sha256 hash */ -bool json_tok_sha256(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - struct sha256 **hash); - -/* Extract positive integer, or NULL if tok is 'any'. */ -bool json_tok_msat(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t * tok, - u64 **msatoshi_val); - -/* Extract double in range [0.0, 100.0] */ -bool json_tok_percent(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - double **num); - /* Extract a pubkey from this */ bool json_to_pubkey(const char *buffer, const jsmntok_t *tok, struct pubkey *pubkey); @@ -113,11 +63,6 @@ bool json_tok_short_channel_id(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct short_channel_id **scid); -/* Extract number from this (may be a string, or a number literal) */ -bool json_tok_u64(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - uint64_t **num); - enum feerate_style { FEERATE_PER_KSIPA, FEERATE_PER_KBYTE @@ -152,14 +97,6 @@ void json_add_address_internal(struct json_stream *response, const char *fieldname, const struct wireaddr_internal *addr); -/* - * Set the address of @out to @tok. Used as a callback by handlers that - * want to unmarshal @tok themselves. - */ -bool json_tok_tok(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t * tok, - const jsmntok_t **out); - /* '"fieldname" : "value"' or '"value"' if fieldname is NULL. Turns * any non-printable chars into JSON escapes, but leaves existing escapes alone. diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index f7dff296cf8f..1ccd0ed29d4e 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -24,7 +24,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -33,12 +36,9 @@ #include #include #include -#include #include -#include #include #include -#include #include #include #include diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index ce5ae71ba64e..ffc98a63bf4c 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -64,7 +64,7 @@ struct json_stream *json_stream_success(struct command *cmd); /** * json_stream_fail - start streaming a failed json result. * @cmd: the command we're running. - * @code: the error code from lightningd/jsonrpc_errors.h + * @code: the error code from common/jsonrpc_errors.h * @errmsg: the error string. * * The returned value should go to command_failed() when done; @@ -77,7 +77,7 @@ struct json_stream *json_stream_fail(struct command *cmd, /** * json_stream_fail_nodata - start streaming a failed json result. * @cmd: the command we're running. - * @code: the error code from lightningd/jsonrpc_errors.h + * @code: the error code from common/jsonrpc_errors.h * @errmsg: the error string. * * This is used by command_fail(), which doesn't add any JSON data. @@ -89,8 +89,6 @@ struct json_stream *json_stream_fail_nodata(struct command *cmd, struct json_stream *null_response(struct command *cmd); void command_success(struct command *cmd, struct json_stream *response); void command_failed(struct command *cmd, struct json_stream *result); -void PRINTF_FMT(3, 4) command_fail(struct command *cmd, int code, - const char *fmt, ...); /* Mainly for documentation, that we plan to close this later. */ void command_still_pending(struct command *cmd); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 3708fa223179..d300c1a74199 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -57,6 +57,7 @@ /*~ This is common code: routines shared by one or more executables * (separate daemons, or the lightning-cli program). */ #include +#include #include #include #include @@ -68,7 +69,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/log.c b/lightningd/log.c index 3dfd18f2c7f1..c8bb4c956050 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -10,17 +10,18 @@ #include #include #include +#include +#include #include +#include #include #include #include #include #include #include -#include #include #include -#include #include #include #include diff --git a/lightningd/memdump.c b/lightningd/memdump.c index 25797b7d3097..5b3d683c72ac 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -4,7 +4,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -12,11 +15,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index c08c7f16fe4b..99c95cbb0e6c 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -4,7 +4,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -16,11 +19,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include diff --git a/lightningd/options.c b/lightningd/options.c index 9d3881cb92b5..8fd78f8ca37c 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -10,7 +10,11 @@ #include #include #include +#include +#include +#include #include +#include #include #include #include @@ -19,13 +23,10 @@ #include #include #include -#include #include -#include #include #include #include -#include #include #include #include diff --git a/lightningd/pay.c b/lightningd/pay.c index 61b4a7ce4c2f..52ebd99d5c61 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -2,16 +2,17 @@ #include #include #include +#include +#include +#include #include #include #include #include #include -#include #include #include #include -#include #include #include #include diff --git a/lightningd/payalgo.c b/lightningd/payalgo.c index ef525db957dc..f657bf0b1e15 100644 --- a/lightningd/payalgo.c +++ b/lightningd/payalgo.c @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -12,10 +15,8 @@ #include #include #include -#include #include #include -#include #include #include #include diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 51e026e19973..2984e4a5221d 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -15,7 +15,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -33,13 +36,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index d7f620260683..bcac32520d81 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -5,19 +5,20 @@ #include #include #include +#include +#include +#include #include +#include #include #include #include #include #include #include -#include #include -#include #include #include -#include #include #include #include diff --git a/lightningd/ping.c b/lightningd/ping.c index 115853f69953..b379b424428f 100644 --- a/lightningd/ping.c +++ b/lightningd/ping.c @@ -1,14 +1,15 @@ #include +#include +#include +#include #include #include #include #include #include #include -#include #include #include -#include #include #include #include diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 0d7150bba2d5..91962aa3d490 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -9,12 +9,14 @@ #include #include #include +#include +#include #include +#include #include #include #include #include -#include #include #include #include diff --git a/lightningd/test/Makefile b/lightningd/test/Makefile index 2941065087c3..f0b7c3504d84 100644 --- a/lightningd/test/Makefile +++ b/lightningd/test/Makefile @@ -13,6 +13,7 @@ LIGHTNINGD_TEST_COMMON_OBJS := \ common/htlc_state.o \ common/io_lock.o \ common/json.o \ + common/json_escaped.o \ common/key_derive.o \ common/pseudorand.o \ common/memleak.o \ diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 9044ea432bba..47c5e7ed4086 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -91,9 +91,6 @@ void hsm_init(struct lightningd *ld UNNEEDED) /* Generated stub for htlcs_notify_new_block */ void htlcs_notify_new_block(struct lightningd *ld UNNEEDED, u32 height UNNEEDED) { fprintf(stderr, "htlcs_notify_new_block called!\n"); abort(); } -/* Generated stub for json_escape */ -struct json_escaped *json_escape(const tal_t *ctx UNNEEDED, const char *str TAKES UNNEEDED) -{ fprintf(stderr, "json_escape called!\n"); abort(); } /* Generated stub for jsonrpc_listen */ void jsonrpc_listen(struct jsonrpc *rpc UNNEEDED, struct lightningd *ld UNNEEDED) { fprintf(stderr, "jsonrpc_listen called!\n"); abort(); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index f4953520d134..58205b817029 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -164,9 +164,6 @@ void json_array_end(struct json_stream *js UNNEEDED) /* Generated stub for json_array_start */ void json_array_start(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_array_start called!\n"); abort(); } -/* Generated stub for json_escape */ -struct json_escaped *json_escape(const tal_t *ctx UNNEEDED, const char *str TAKES UNNEEDED) -{ fprintf(stderr, "json_escape called!\n"); abort(); } /* Generated stub for json_object_end */ void json_object_end(struct json_stream *js UNNEEDED) { fprintf(stderr, "json_object_end called!\n"); abort(); } diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 24f5dbee742b..7b1e787f43d6 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -1,4 +1,3 @@ -#include "../json_escaped.c" #include "../json_stream.c" #include "../jsonrpc.c" #include "../json.c" @@ -26,6 +25,21 @@ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr bool json_feerate_estimate(struct command *cmd UNNEEDED, u32 **feerate_per_kw UNNEEDED, enum feerate feerate UNNEEDED) { fprintf(stderr, "json_feerate_estimate called!\n"); abort(); } +/* Generated stub for json_tok_number */ +bool json_tok_number(struct command *cmd UNNEEDED, const char *name UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + unsigned int **num UNNEEDED) +{ fprintf(stderr, "json_tok_number called!\n"); abort(); } +/* Generated stub for json_tok_sha256 */ +bool json_tok_sha256(struct command *cmd UNNEEDED, const char *name UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct sha256 **hash UNNEEDED) +{ fprintf(stderr, "json_tok_sha256 called!\n"); abort(); } +/* Generated stub for json_tok_tok */ +bool json_tok_tok(struct command *cmd UNNEEDED, const char *name UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t * tok UNNEEDED, + const jsmntok_t **out UNNEEDED) +{ fprintf(stderr, "json_tok_tok called!\n"); abort(); } /* Generated stub for log_ */ void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) diff --git a/wallet/db.c b/wallet/db.c index 6b4574e29ea0..a7523161f6d4 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1,9 +1,9 @@ #include "db.h" #include +#include #include #include -#include #include #include diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index a3771d43e098..e2410c7e0dae 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -3,7 +3,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -16,10 +19,8 @@ #include #include #include -#include #include #include -#include #include #include #include