2021-02-01 02:58:50 +00:00
|
|
|
#include "config.h"
|
*: Use new closefrom module from ccan.
This also inadvertently fixes a latent bug: before this patch, in the
`subd` function in `lightningd/subd.c`, we would close `execfail[1]`
*before* doing an `exec`.
We use an EOF on `execfail[1]` as a signal that `exec` succeeded (the
fd is marked CLOEXEC), and otherwise use it to pump `errno` to the
parent.
The intent is that this fd should be kept open until `exec`, at which
point CLOEXEC triggers and close that fd and sends the EOF, *or* if
`exec` fails we can send the `errno` to the parent process vua that
pipe-end.
However, in the previous version, we end up closing that fd *before*
reaching `exec`, either in the loop which `dup2`s passed-in fds (by
overwriting `execfail[1]` with a `dup2`) or in the "close everything"
loop, which does not guard against `execfail[1]`, only
`dev_disconnect_fd`.
2021-10-19 04:35:44 +01:00
|
|
|
#include <ccan/closefrom/closefrom.h>
|
2017-05-24 11:10:16 +01:00
|
|
|
#include <ccan/err/err.h>
|
2017-08-28 17:05:01 +01:00
|
|
|
#include <common/dev_disconnect.h>
|
|
|
|
#include <common/status.h>
|
2019-10-08 02:04:24 +01:00
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/tcp.h>
|
2021-09-19 12:07:58 +01:00
|
|
|
#include <sys/socket.h>
|
2017-09-06 02:07:19 +01:00
|
|
|
#include <sys/stat.h>
|
2017-05-24 11:10:16 +01:00
|
|
|
#include <unistd.h>
|
2020-09-07 22:06:50 +01:00
|
|
|
#include <wire/peer_wire.h>
|
2017-05-24 11:10:16 +01:00
|
|
|
|
2017-10-24 03:06:14 +01:00
|
|
|
#if DEVELOPER
|
2018-01-29 04:46:54 +00:00
|
|
|
/* We move the fd if and only if we do a disconnect. */
|
2017-05-24 11:10:16 +01:00
|
|
|
static int dev_disconnect_fd = -1;
|
|
|
|
static char dev_disconnect_line[200];
|
2017-08-18 05:43:52 +01:00
|
|
|
static int dev_disconnect_count, dev_disconnect_len;
|
2017-09-26 05:57:31 +01:00
|
|
|
|
2017-10-11 11:09:49 +01:00
|
|
|
static void next_dev_disconnect(void)
|
2017-05-24 11:10:16 +01:00
|
|
|
{
|
|
|
|
int r;
|
2017-08-18 05:43:52 +01:00
|
|
|
char *asterisk;
|
2017-05-24 11:10:16 +01:00
|
|
|
|
2017-10-11 11:09:49 +01:00
|
|
|
r = read(dev_disconnect_fd,
|
|
|
|
dev_disconnect_line, sizeof(dev_disconnect_line)-1);
|
2017-05-24 11:10:16 +01:00
|
|
|
if (r < 0)
|
|
|
|
err(1, "Reading dev_disconnect file");
|
2018-01-09 07:13:35 +00:00
|
|
|
if (lseek(dev_disconnect_fd, -r, SEEK_CUR) < 0) {
|
|
|
|
err(1, "lseek failure");
|
|
|
|
}
|
2017-05-24 11:10:16 +01:00
|
|
|
|
|
|
|
/* Get first line */
|
|
|
|
dev_disconnect_line[r] = '\n';
|
2017-08-18 05:43:52 +01:00
|
|
|
dev_disconnect_len = strcspn(dev_disconnect_line, "\n");
|
|
|
|
dev_disconnect_line[dev_disconnect_len] = '\0';
|
2017-10-11 11:08:50 +01:00
|
|
|
|
2017-08-18 05:43:52 +01:00
|
|
|
asterisk = strchr(dev_disconnect_line, '*');
|
|
|
|
if (asterisk) {
|
|
|
|
dev_disconnect_count = atoi(asterisk+1);
|
|
|
|
if (dev_disconnect_count < 1)
|
|
|
|
errx(1, "dev_disconnect invalid count: %s",
|
|
|
|
dev_disconnect_line);
|
|
|
|
*asterisk = '\0';
|
|
|
|
} else
|
|
|
|
dev_disconnect_count = 1;
|
2017-10-11 11:09:49 +01:00
|
|
|
}
|
2017-05-24 11:10:16 +01:00
|
|
|
|
2017-10-11 11:09:49 +01:00
|
|
|
void dev_disconnect_init(int fd)
|
|
|
|
{
|
2017-05-24 11:10:16 +01:00
|
|
|
/* So we can move forward if we do use the line. */
|
|
|
|
dev_disconnect_fd = fd;
|
|
|
|
}
|
|
|
|
|
2017-09-26 05:57:31 +01:00
|
|
|
enum dev_disconnect dev_disconnect(int pkt_type)
|
2017-05-24 11:10:16 +01:00
|
|
|
{
|
2017-10-11 11:09:49 +01:00
|
|
|
if (dev_disconnect_fd == -1)
|
|
|
|
return DEV_DISCONNECT_NORMAL;
|
|
|
|
|
|
|
|
if (!dev_disconnect_count)
|
|
|
|
next_dev_disconnect();
|
|
|
|
|
2020-08-31 02:13:25 +01:00
|
|
|
if (!streq(peer_wire_name(pkt_type), dev_disconnect_line+1))
|
2017-05-24 11:10:16 +01:00
|
|
|
return DEV_DISCONNECT_NORMAL;
|
|
|
|
|
2017-10-11 11:09:49 +01:00
|
|
|
if (--dev_disconnect_count != 0) {
|
2017-08-18 05:43:52 +01:00
|
|
|
return DEV_DISCONNECT_NORMAL;
|
|
|
|
}
|
|
|
|
|
2018-01-09 07:13:35 +00:00
|
|
|
if (lseek(dev_disconnect_fd, dev_disconnect_len+1, SEEK_CUR) < 0) {
|
|
|
|
err(1, "lseek failure");
|
|
|
|
}
|
2017-05-24 11:10:16 +01:00
|
|
|
|
2021-12-29 03:26:40 +00:00
|
|
|
status_debug("dev_disconnect: %s", dev_disconnect_line);
|
2017-05-24 11:10:16 +01:00
|
|
|
return dev_disconnect_line[0];
|
|
|
|
}
|
|
|
|
|
2021-06-14 22:07:36 +01:00
|
|
|
void dev_sabotage_fd(int fd, bool close_fd)
|
2017-05-24 11:10:16 +01:00
|
|
|
{
|
|
|
|
int fds[2];
|
|
|
|
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0)
|
2017-09-06 02:07:19 +01:00
|
|
|
err(1, "dev_sabotage_fd: creating socketpair");
|
2017-05-24 11:10:16 +01:00
|
|
|
|
2019-10-08 02:04:24 +01:00
|
|
|
#if defined(TCP_NODELAY)
|
|
|
|
/* On Linux, at least, this flushes. */
|
|
|
|
int opt = TCP_NODELAY;
|
|
|
|
int val = 1;
|
|
|
|
setsockopt(fd, IPPROTO_TCP, opt, &val, sizeof(val));
|
|
|
|
#else
|
|
|
|
#error No TCP_NODELAY?
|
|
|
|
#endif
|
2021-06-14 22:07:36 +01:00
|
|
|
|
|
|
|
/* Move fd out the way if we don't want to close it. */
|
2021-06-14 22:09:49 +01:00
|
|
|
if (!close_fd) {
|
|
|
|
if (dup(fd) == -1) {
|
|
|
|
; /* -Wunused-result */
|
|
|
|
}
|
|
|
|
} else
|
2021-06-14 22:07:36 +01:00
|
|
|
/* Close other end of socket. */
|
|
|
|
close(fds[0]);
|
|
|
|
|
2017-05-24 11:10:16 +01:00
|
|
|
/* Move other over to the fd we want to sabotage. */
|
|
|
|
dup2(fds[1], fd);
|
|
|
|
close(fds[1]);
|
|
|
|
}
|
2017-09-06 02:07:19 +01:00
|
|
|
|
|
|
|
/* Replace fd with blackhole until dev_disconnect file is truncated. */
|
|
|
|
void dev_blackhole_fd(int fd)
|
|
|
|
{
|
|
|
|
int fds[2];
|
|
|
|
int i;
|
|
|
|
struct stat st;
|
|
|
|
|
*: Use new closefrom module from ccan.
This also inadvertently fixes a latent bug: before this patch, in the
`subd` function in `lightningd/subd.c`, we would close `execfail[1]`
*before* doing an `exec`.
We use an EOF on `execfail[1]` as a signal that `exec` succeeded (the
fd is marked CLOEXEC), and otherwise use it to pump `errno` to the
parent.
The intent is that this fd should be kept open until `exec`, at which
point CLOEXEC triggers and close that fd and sends the EOF, *or* if
`exec` fails we can send the `errno` to the parent process vua that
pipe-end.
However, in the previous version, we end up closing that fd *before*
reaching `exec`, either in the loop which `dup2`s passed-in fds (by
overwriting `execfail[1]` with a `dup2`) or in the "close everything"
loop, which does not guard against `execfail[1]`, only
`dev_disconnect_fd`.
2021-10-19 04:35:44 +01:00
|
|
|
int maxfd;
|
|
|
|
|
2017-09-06 02:07:19 +01:00
|
|
|
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
|
*: Use new closefrom module from ccan.
This also inadvertently fixes a latent bug: before this patch, in the
`subd` function in `lightningd/subd.c`, we would close `execfail[1]`
*before* doing an `exec`.
We use an EOF on `execfail[1]` as a signal that `exec` succeeded (the
fd is marked CLOEXEC), and otherwise use it to pump `errno` to the
parent.
The intent is that this fd should be kept open until `exec`, at which
point CLOEXEC triggers and close that fd and sends the EOF, *or* if
`exec` fails we can send the `errno` to the parent process vua that
pipe-end.
However, in the previous version, we end up closing that fd *before*
reaching `exec`, either in the loop which `dup2`s passed-in fds (by
overwriting `execfail[1]` with a `dup2`) or in the "close everything"
loop, which does not guard against `execfail[1]`, only
`dev_disconnect_fd`.
2021-10-19 04:35:44 +01:00
|
|
|
* 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++)
|
2017-09-06 02:07:19 +01:00
|
|
|
if (i != fds[0]
|
|
|
|
&& i != dev_disconnect_fd
|
|
|
|
&& i != STDERR_FILENO)
|
|
|
|
close(i);
|
*: Use new closefrom module from ccan.
This also inadvertently fixes a latent bug: before this patch, in the
`subd` function in `lightningd/subd.c`, we would close `execfail[1]`
*before* doing an `exec`.
We use an EOF on `execfail[1]` as a signal that `exec` succeeded (the
fd is marked CLOEXEC), and otherwise use it to pump `errno` to the
parent.
The intent is that this fd should be kept open until `exec`, at which
point CLOEXEC triggers and close that fd and sends the EOF, *or* if
`exec` fails we can send the `errno` to the parent process vua that
pipe-end.
However, in the previous version, we end up closing that fd *before*
reaching `exec`, either in the loop which `dup2`s passed-in fds (by
overwriting `execfail[1]` with a `dup2`) or in the "close everything"
loop, which does not guard against `execfail[1]`, only
`dev_disconnect_fd`.
2021-10-19 04:35:44 +01:00
|
|
|
closefrom(maxfd + 1);
|
2017-09-06 02:07:19 +01:00
|
|
|
|
|
|
|
/* 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]);
|
|
|
|
}
|
2017-10-24 03:06:14 +01:00
|
|
|
#endif
|