mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 06:41:44 +01:00
connectd: implement @ correctly.
dev_blackhole_fd was a hack, and doesn't work well now we are async (it worked for sync comms in per-peer daemons, but now we could sneak through a read before we get to the next write). So, make explicit flags and use them. This is much easier now we have all peer comms in one place. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
bb5beeddd7
commit
a93c49ca65
5 changed files with 37 additions and 71 deletions
|
@ -105,56 +105,4 @@ void dev_sabotage_fd(int fd, bool close_fd)
|
||||||
dup2(fds[1], fd);
|
dup2(fds[1], fd);
|
||||||
close(fds[1]);
|
close(fds[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Replace fd with blackhole until dev_disconnect file is truncated. */
|
|
||||||
void dev_blackhole_fd(int fd)
|
|
||||||
{
|
|
||||||
int fds[2];
|
|
||||||
int i;
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
int maxfd;
|
|
||||||
|
|
||||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0)
|
|
||||||
err(1, "dev_blackhole_fd: creating socketpair");
|
|
||||||
|
|
||||||
switch (fork()) {
|
|
||||||
case -1:
|
|
||||||
err(1, "dev_blackhole_fd: forking");
|
|
||||||
case 0:
|
|
||||||
/* Close everything but the dev_disconnect_fd, the socket
|
|
||||||
* which is pretending to be the peer, and stderr.
|
|
||||||
* The "correct" way to do this would be to move the
|
|
||||||
* fds we want to preserve to the low end (0, 1, 2...)
|
|
||||||
* of the fd space and then just do a single closefrom
|
|
||||||
* call, but dup2 could fail with ENFILE (which is a
|
|
||||||
* *system*-level error, i.e. the entire system has too
|
|
||||||
* many processes with open files) and we have no
|
|
||||||
* convenient way to inform the parent of the error.
|
|
||||||
* So loop until we reach whichever is higher of fds[0]
|
|
||||||
* or dev_disconnect_fd, and *then* closefrom after that.
|
|
||||||
*/
|
|
||||||
maxfd = (fds[0] > dev_disconnect_fd) ? fds[0] :
|
|
||||||
dev_disconnect_fd ;
|
|
||||||
for (i = 0; i < maxfd; i++)
|
|
||||||
if (i != fds[0]
|
|
||||||
&& i != dev_disconnect_fd
|
|
||||||
&& i != STDERR_FILENO)
|
|
||||||
close(i);
|
|
||||||
closefrom(maxfd + 1);
|
|
||||||
|
|
||||||
/* Close once dev_disconnect file is truncated. */
|
|
||||||
for (;;) {
|
|
||||||
if (fstat(dev_disconnect_fd, &st) != 0)
|
|
||||||
err(1, "fstat of dev_disconnect_fd failed");
|
|
||||||
if (st.st_size == 0)
|
|
||||||
_exit(0);
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fds[0]);
|
|
||||||
dup2(fds[1], fd);
|
|
||||||
close(fds[1]);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -25,9 +25,6 @@ enum dev_disconnect dev_disconnect(const struct node_id *id, int pkt_type);
|
||||||
/* Make next write on fd fail as if they'd disconnected. */
|
/* Make next write on fd fail as if they'd disconnected. */
|
||||||
void dev_sabotage_fd(int fd, bool close_fd);
|
void dev_sabotage_fd(int fd, bool close_fd);
|
||||||
|
|
||||||
/* No more data to arrive, what's written is swallowed. */
|
|
||||||
void dev_blackhole_fd(int fd);
|
|
||||||
|
|
||||||
/* For debug code to set in daemon. */
|
/* For debug code to set in daemon. */
|
||||||
void dev_disconnect_init(int fd);
|
void dev_disconnect_init(int fd);
|
||||||
|
|
||||||
|
|
|
@ -358,6 +358,11 @@ static struct peer *new_peer(struct daemon *daemon,
|
||||||
peer->peer_outq = msg_queue_new(peer);
|
peer->peer_outq = msg_queue_new(peer);
|
||||||
peer->subd_outq = msg_queue_new(peer);
|
peer->subd_outq = msg_queue_new(peer);
|
||||||
|
|
||||||
|
#if DEVELOPER
|
||||||
|
peer->dev_writes_enabled = NULL;
|
||||||
|
peer->dev_read_enabled = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Aim for connection to shuffle data back and forth: sets up
|
/* Aim for connection to shuffle data back and forth: sets up
|
||||||
* peer->to_subd */
|
* peer->to_subd */
|
||||||
if (!multiplex_subd_setup(peer, fd_for_subd))
|
if (!multiplex_subd_setup(peer, fd_for_subd))
|
||||||
|
|
|
@ -61,6 +61,12 @@ struct peer {
|
||||||
|
|
||||||
/* We stream from the gossip_store for them, when idle */
|
/* We stream from the gossip_store for them, when idle */
|
||||||
struct gossip_state gs;
|
struct gossip_state gs;
|
||||||
|
|
||||||
|
#if DEVELOPER
|
||||||
|
bool dev_read_enabled;
|
||||||
|
/* If non-NULL, this counts down; 0 means disable */
|
||||||
|
u32 *dev_writes_enabled;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/*~ The HTABLE_DEFINE_TYPE() macro needs a keyof() function to extract the key:
|
/*~ The HTABLE_DEFINE_TYPE() macro needs a keyof() function to extract the key:
|
||||||
|
|
|
@ -155,20 +155,6 @@ static struct io_plan *after_final_msg(struct io_conn *peer_conn,
|
||||||
return io_close(peer_conn);
|
return io_close(peer_conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEVELOPER
|
|
||||||
static struct io_plan *write_to_peer(struct io_conn *peer_conn,
|
|
||||||
struct peer *peer);
|
|
||||||
|
|
||||||
static struct io_plan *dev_leave_hanging(struct io_conn *peer_conn,
|
|
||||||
struct peer *peer)
|
|
||||||
{
|
|
||||||
/* We don't tell the peer we're disconnecting, but from now on
|
|
||||||
* our writes go nowhere, and there's nothing to read. */
|
|
||||||
dev_sabotage_fd(io_conn_fd(peer_conn), false);
|
|
||||||
return write_to_peer(peer_conn, peer);
|
|
||||||
}
|
|
||||||
#endif /* DEVELOPER */
|
|
||||||
|
|
||||||
/* We're happy for the kernel to batch update and gossip messages, but a
|
/* We're happy for the kernel to batch update and gossip messages, but a
|
||||||
* commitment message, for example, should be instantly sent. There's no
|
* commitment message, for example, should be instantly sent. There's no
|
||||||
* great way of doing this, unfortunately.
|
* great way of doing this, unfortunately.
|
||||||
|
@ -287,15 +273,21 @@ static struct io_plan *encrypt_and_send(struct peer *peer,
|
||||||
tal_free(msg);
|
tal_free(msg);
|
||||||
return io_close(peer->to_peer);
|
return io_close(peer->to_peer);
|
||||||
case DEV_DISCONNECT_AFTER:
|
case DEV_DISCONNECT_AFTER:
|
||||||
|
/* Disallow reads from now on */
|
||||||
|
peer->dev_read_enabled = false;
|
||||||
next = (void *)io_close_cb;
|
next = (void *)io_close_cb;
|
||||||
break;
|
break;
|
||||||
case DEV_DISCONNECT_BLACKHOLE:
|
case DEV_DISCONNECT_BLACKHOLE:
|
||||||
dev_blackhole_fd(io_conn_fd(peer->to_peer));
|
/* Disable both reads and writes from now on */
|
||||||
|
peer->dev_read_enabled = false;
|
||||||
|
peer->dev_writes_enabled = talz(peer, u32);
|
||||||
break;
|
break;
|
||||||
case DEV_DISCONNECT_NORMAL:
|
case DEV_DISCONNECT_NORMAL:
|
||||||
break;
|
break;
|
||||||
case DEV_DISCONNECT_DISABLE_AFTER:
|
case DEV_DISCONNECT_DISABLE_AFTER:
|
||||||
next = dev_leave_hanging;
|
peer->dev_read_enabled = false;
|
||||||
|
peer->dev_writes_enabled = tal(peer, u32);
|
||||||
|
*peer->dev_writes_enabled = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -426,6 +418,18 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* dev_disconnect can disable writes */
|
||||||
|
#if DEVELOPER
|
||||||
|
if (peer->dev_writes_enabled) {
|
||||||
|
if (*peer->dev_writes_enabled == 0) {
|
||||||
|
tal_free(msg);
|
||||||
|
/* Continue, to drain queue */
|
||||||
|
return write_to_peer(peer_conn, peer);
|
||||||
|
}
|
||||||
|
(*peer->dev_writes_enabled)--;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return encrypt_and_send(peer, take(msg), write_to_peer);
|
return encrypt_and_send(peer, take(msg), write_to_peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,6 +492,12 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn,
|
||||||
}
|
}
|
||||||
tal_free(peer->peer_in);
|
tal_free(peer->peer_in);
|
||||||
|
|
||||||
|
/* dev_disconnect can disable read */
|
||||||
|
if (!IFDEV(peer->dev_read_enabled, true)) {
|
||||||
|
tal_free(decrypted);
|
||||||
|
return read_hdr_from_peer(peer_conn, peer);
|
||||||
|
}
|
||||||
|
|
||||||
/* If we swallow this, just try again. */
|
/* If we swallow this, just try again. */
|
||||||
if (handle_message_locally(peer, decrypted)) {
|
if (handle_message_locally(peer, decrypted)) {
|
||||||
tal_free(decrypted);
|
tal_free(decrypted);
|
||||||
|
|
Loading…
Add table
Reference in a new issue