Commit 7bfb7a1c authored by Rusty Russell's avatar Rusty Russell

ccan/io: save errno on io_close, for finish functions.

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 306b6b0e
......@@ -325,6 +325,7 @@ struct io_plan io_close(void)
plan.pollflag = 0;
/* This means we're closing. */
plan.next = NULL;
plan.u.close.saved_errno = errno;
io_plan_debug(&plan);
return plan;
......
......@@ -38,6 +38,9 @@ struct io_plan {
const char *buf;
size_t *lenp;
} writepart;
struct {
int saved_errno;
} close;
struct {
void *p;
size_t len;
......@@ -84,7 +87,8 @@ struct io_conn *io_new_conn_(int fd, struct io_plan plan);
* @arg: the argument to @finish.
*
* @finish will be called when an I/O operation fails, or you call
* io_close() on the connection.
* io_close() on the connection. errno will be set to the value
* after the failed I/O, or at the call to io_close().
*/
#define io_set_finish(conn, finish, arg) \
io_set_finish_((conn), \
......
......@@ -7,6 +7,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <limits.h>
#include <errno.h>
static size_t num_fds = 0, max_fds = 0, num_closing = 0, num_waiting = 0;
static struct pollfd *pollfds = NULL;
......@@ -173,8 +174,10 @@ bool add_duplex(struct io_conn *c)
static void del_conn(struct io_conn *conn)
{
if (conn->finish)
if (conn->finish) {
errno = conn->plan.u.close.saved_errno;
conn->finish(conn, conn->finish_arg);
}
if (timeout_active(conn))
backend_del_timeout(conn);
free(conn->timeout);
......@@ -341,6 +344,7 @@ void *io_loop(void)
} else if (events & (POLLHUP|POLLNVAL|POLLERR)) {
r--;
set_current(c);
errno = EBADF;
set_plan(c, io_close());
if (c->duplex) {
set_current(c->duplex);
......
#define DEBUG
#define PORT "64018"
#define main real_main
int real_main(void);
#include "run-18-errno.c"
#undef main
static bool always_debug(struct io_conn *conn) { return true; }
int main(void) { io_debug = always_debug; return real_main(); }
#include <ccan/io/io.h>
/* Include the C files directly. */
#include <ccan/io/poll.c>
#include <ccan/io/io.c>
#include <ccan/tap/tap.h>
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#define PORT "65018"
#endif
static void finish_100(struct io_conn *conn, int *state)
{
ok1(errno == 100);
ok1(*state == 1);
(*state)++;
}
static void finish_EBADF(struct io_conn *conn, int *state)
{
ok1(errno == EBADF);
ok1(*state == 3);
(*state)++;
io_break(state + 1, io_close());
}
static void init_conn(int fd, int *state)
{
if (*state == 0) {
(*state)++;
errno = 100;
io_set_finish(io_new_conn(fd, io_close()), finish_100, state);
} else {
ok1(*state == 2);
(*state)++;
close(fd);
errno = 0;
io_set_finish(io_new_conn(fd, io_read(state, 0,
io_close_cb, NULL)),
finish_EBADF, state);
}
}
static int make_listen_fd(const char *port, struct addrinfo **info)
{
int fd, on = 1;
struct addrinfo *addrinfo, hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
if (getaddrinfo(NULL, port, &hints, &addrinfo) != 0)
return -1;
fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
addrinfo->ai_protocol);
if (fd < 0)
return -1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) {
close(fd);
return -1;
}
if (listen(fd, 1) != 0) {
close(fd);
return -1;
}
*info = addrinfo;
return fd;
}
int main(void)
{
int state = 0;
struct addrinfo *addrinfo;
struct io_listener *l;
int fd;
/* This is how many tests you plan to run */
plan_tests(12);
fd = make_listen_fd(PORT, &addrinfo);
ok1(fd >= 0);
l = io_new_listener(fd, init_conn, &state);
ok1(l);
fflush(stdout);
if (!fork()) {
io_close_listener(l);
fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
addrinfo->ai_protocol);
if (fd < 0)
exit(1);
if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
exit(2);
close(fd);
fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
addrinfo->ai_protocol);
if (fd < 0)
exit(3);
if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
exit(4);
close(fd);
freeaddrinfo(addrinfo);
exit(0);
}
freeaddrinfo(addrinfo);
ok1(io_loop() == &state + 1);
ok1(state == 4);
io_close_listener(l);
ok1(wait(&state));
ok1(WIFEXITED(state));
ok1(WEXITSTATUS(state) == 0);
/* This exits depending on whether all tests passed */
return exit_status();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment