diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 730f5723f..da756fbca 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -1,10 +1,12 @@ #include "hsm_control.h" #include "lightningd.h" +#include "peer_control.h" #include "subdaemon.h" #include #include #include #include +#include #include #include @@ -26,6 +28,40 @@ static void hsm_finished(struct subdaemon *hsm, int status) errx(1, "HSM failed (signal %u), exiting.", WTERMSIG(status)); } +static enum subdaemon_status hsm_status(struct subdaemon *hsm, const u8 *msg, + int fd) +{ + enum hsm_status_wire_type t = fromwire_peektype(msg); + u8 *badmsg; + struct peer *peer; + u64 id; + + switch (t) { + case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: + if (!fromwire_hsmstatus_client_bad_request(msg, msg, NULL, + &id, &badmsg)) + errx(1, "HSM bad status %s", tal_hex(msg, msg)); + peer = peer_by_unique_id(hsm->ld, id); + + /* "Shouldn't happen" */ + errx(1, "HSM says bad cmd from %"PRIu64" (%s): %s", + id, + peer ? (peer->id ? type_to_string(msg, struct pubkey, + peer->id) + : "pubkey not yet known") + : "unknown peer", + tal_hex(msg, badmsg)); + + /* We don't get called for failed status. */ + case WIRE_HSMSTATUS_INIT_FAILED: + case WIRE_HSMSTATUS_WRITEMSG_FAILED: + case WIRE_HSMSTATUS_BAD_REQUEST: + case WIRE_HSMSTATUS_FD_FAILED: + break; + } + return STATUS_COMPLETE; +} + void hsm_init(struct lightningd *ld, bool newdir) { bool create; @@ -33,7 +69,7 @@ void hsm_init(struct lightningd *ld, bool newdir) ld->hsm = new_subdaemon(ld, ld, "lightningd_hsm", hsm_status_wire_type_name, hsm_control_wire_type_name, - hsm_finished, -1); + hsm_status, hsm_finished, -1); if (!ld->hsm) err(1, "Could not subdaemon hsm"); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 0f9524c6b..1056acb94 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -16,28 +16,6 @@ #include #include -struct peer { - struct lightningd *ld; - - /* Unique ID (works before we know their pubkey) */ - u64 unique_id; - - /* Inside ld->peers. */ - struct list_node list; - - /* What stage is this in? */ - struct subdaemon *owner; - - /* ID of peer (NULL before initial handshake). */ - struct pubkey *id; - - /* Our fd to the peer. */ - int fd; - - /* HSM connection for this peer. */ - int hsmfd; -}; - static void destroy_peer(struct peer *peer) { list_del_from(&peer->ld->peers, &peer->list); @@ -123,7 +101,7 @@ static void peer_got_hsmfd(struct subdaemon *hsm, const u8 *msg, "lightningd_handshake", handshake_status_wire_type_name, handshake_control_wire_type_name, - NULL, + NULL, NULL, peer->hsmfd, -1); if (!peer->owner) { log_unusual(peer->ld->log, "Could not subdaemon handshake: %s", diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index fd69cada0..2d6e549b1 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -3,7 +3,27 @@ #include "config.h" #include -struct lightningd; +struct peer { + struct lightningd *ld; + + /* Unique ID (works before we know their pubkey) */ + u64 unique_id; + + /* Inside ld->peers. */ + struct list_node list; + + /* What stage is this in? */ + struct subdaemon *owner; + + /* ID of peer (NULL before initial handshake). */ + struct pubkey *id; + + /* Our fd to the peer. */ + int fd; + + /* HSM connection for this peer. */ + int hsmfd; +}; struct peer *peer_by_unique_id(struct lightningd *ld, u64 unique_id); diff --git a/lightningd/subdaemon.c b/lightningd/subdaemon.c index 074fd062a..fa09fb593 100644 --- a/lightningd/subdaemon.c +++ b/lightningd/subdaemon.c @@ -151,17 +151,34 @@ fail: static struct io_plan *status_read(struct io_conn *conn, struct subdaemon *sd); +static struct io_plan *status_process_fd(struct io_conn *conn, + struct subdaemon *sd) +{ + const tal_t *tmpctx = tal_tmpctx(sd); + + /* Ensure we free it iff callback doesn't tal_steal it. */ + tal_steal(tmpctx, sd->status_in); + sd->statuscb(sd, sd->status_in, sd->status_fd_in); + tal_free(tmpctx); + sd->status_in = NULL; + return status_read(conn, sd); +} + static struct io_plan *status_process(struct io_conn *conn, struct subdaemon *sd) { int type = fromwire_peektype(sd->status_in); const char *str; int str_len; + const tal_t *tmpctx = tal_tmpctx(sd); if (type == -1) { log_unusual(sd->log, "ERROR: Invalid status output"); return io_close(conn); } + /* If not stolen, we'll free this below. */ + tal_steal(tmpctx, sd->status_in); + /* If it's a string. */ str_len = tal_count(sd->status_in) - sizeof(be16); str = (const char *)sd->status_in + sizeof(be16); @@ -173,12 +190,26 @@ static struct io_plan *status_process(struct io_conn *conn, struct subdaemon *sd sd->statusname(type), str_len, str); else { log_info(sd->log, "UPDATE %s", sd->statusname(type)); - tal_free(sd->last_status); - /* Keep last status around. */ - sd->last_status = tal_steal(sd, sd->status_in); - sd->status_in = NULL; + if (sd->statuscb) { + enum subdaemon_status s = sd->statuscb(sd, + sd->status_in, + -1); + switch (s) { + case STATUS_NEED_FD: + tal_steal(sd, sd->status_in); + tal_free(tmpctx); + return io_recv_fd(conn, &sd->status_fd_in, + status_process_fd, sd); + case STATUS_COMPLETE: + break; + default: + fatal("Unknown statuscb return for %s:%s: %u", + sd->name, sd->statusname(type), s); + } + } } - sd->status_in = tal_free(sd->status_in); + sd->status_in = NULL; + tal_free(tmpctx); return status_read(conn, sd); } @@ -214,6 +245,8 @@ struct subdaemon *new_subdaemon(const tal_t *ctx, const char *name, const char *(*statusname)(int status), const char *(*reqname)(int req), + enum subdaemon_status (*statuscb) + (struct subdaemon *, const u8 *, int fd), void (*finished)(struct subdaemon *, int), ...) { @@ -235,7 +268,7 @@ struct subdaemon *new_subdaemon(const tal_t *ctx, sd->name = name; sd->finished = finished; sd->statusname = statusname; - sd->last_status = NULL; + sd->statuscb = statuscb; list_head_init(&sd->reqs); tal_add_destructor(sd, destroy_subdaemon); diff --git a/lightningd/subdaemon.h b/lightningd/subdaemon.h index de2b4414f..e0d722f36 100644 --- a/lightningd/subdaemon.h +++ b/lightningd/subdaemon.h @@ -8,6 +8,11 @@ struct io_conn; +enum subdaemon_status { + STATUS_NEED_FD, + STATUS_COMPLETE +}; + /* One of our subdaemons. */ struct subdaemon { /* Name, like John, or "lightningd_hsm" */ @@ -24,15 +29,15 @@ struct subdaemon { /* For logging */ struct log *log; + /* Callback when status comes in. */ + enum subdaemon_status (*statuscb)(struct subdaemon *, const u8 *, int); const char *(*statusname)(int status); const char *(*reqname)(int req); void (*finished)(struct subdaemon *sd, int status); /* Buffer for input. */ u8 *status_in; - - /* Status handler puts last status msg here. */ - u8 *last_status; + int status_fd_in; /* Requests queue up here. */ struct list_head reqs; @@ -45,16 +50,21 @@ struct subdaemon { * @name: basename of daemon * @statusname: function to get name from status messages * @reqname: function to get name from request messages, or NULL if no requests. + * @statuscb: function to call when status message received (or NULL) * @finished: function to call when it's finished (with exit status). * @...: the fds to hand as fd 3, 4... terminated with -1. * - * You should free it from finished(). + * @statuscb is called with fd == -1 when a status message is + * received; if it returns STATUS_NEED_FD, we read an fd from the + * daemon and call it again with that as the third arg. */ struct subdaemon *new_subdaemon(const tal_t *ctx, struct lightningd *ld, const char *name, const char *(*statusname)(int status), const char *(*reqname)(int req), + enum subdaemon_status (*statuscb) + (struct subdaemon *, const u8 *, int fd), void (*finished)(struct subdaemon *, int), ...); /**