Commit b85c47bb authored by Rusty Russell's avatar Rusty Russell

ccan/io: io_connect()

Not a perfect solution (we'd ideally want to go to another plan
immediately, but that would involve a malloc).
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent c321a1d0
......@@ -9,6 +9,8 @@
#include <stdlib.h>
#include <assert.h>
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
void *io_loop_return;
......@@ -337,6 +339,59 @@ struct io_plan io_write_partial_(const void *data, size_t *len,
return plan;
}
static int already_connected(int fd, struct io_plan *plan)
{
return io_debug_io(1);
}
static int do_connect(int fd, struct io_plan *plan)
{
int err, ret;
socklen_t len = sizeof(err);
/* Has async connect finished? */
ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
if (ret < 0)
return -1;
if (err == 0) {
/* Restore blocking if it was initially. */
fcntl(fd, F_SETFD, plan->u.len_len.len1);
return 1;
}
return 0;
}
struct io_plan io_connect_(int fd, const struct addrinfo *addr,
struct io_plan (*cb)(struct io_conn*, void *),
void *arg)
{
struct io_plan plan;
assert(cb);
plan.next = cb;
plan.next_arg = arg;
/* Save old flags, set nonblock if not already. */
plan.u.len_len.len1 = fcntl(fd, F_GETFD);
fcntl(fd, F_SETFD, plan.u.len_len.len1 | O_NONBLOCK);
/* Immediate connect can happen. */
if (connect(fd, addr->ai_addr, addr->ai_addrlen) == 0) {
/* Dummy will be called immediately. */
plan.pollflag = POLLOUT;
plan.io = already_connected;
} else {
if (errno != EINPROGRESS)
return io_close_();
plan.pollflag = POLLIN;
plan.io = do_connect;
}
return plan;
}
struct io_plan io_idle_(void)
{
struct io_plan plan;
......
......@@ -160,6 +160,31 @@ struct io_plan io_write_partial_(const void *data, size_t *len,
struct io_plan (*cb)(struct io_conn *, void*),
void *arg);
/**
* io_connect - plan to connect to a listening socket.
* @fd: file descriptor.
* @addr: where to connect.
* @cb: function to call once it's done.
* @arg: @cb argument
*
* This initiates a connection, and creates a plan for
* (asynchronously). completing it. Once complete, @len is updated
* and the @cb function will be called: on an error, the finish
* function is called instead.
*
* Note that the connect may actually be done immediately.
*/
struct addrinfo;
#define io_connect(fd, addr, cb, arg) \
io_debug(io_connect_((fd), (addr), \
typesafe_cb_preargs(struct io_plan, void *, \
(cb), (arg), \
struct io_conn *), \
(arg)))
struct io_plan io_connect_(int fd, const struct addrinfo *addr,
struct io_plan (*cb)(struct io_conn *, void*),
void *arg);
/**
* io_idle - plan to do nothing.
*
......
#define DEBUG
#define PORT "64009"
#define main real_main
int real_main(void);
#include "run-09-connect.c"
#undef main
static bool always_debug(struct io_conn *conn) { return true; }
int main(void) { io_debug_conn = 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 "65009"
#endif
static struct io_listener *l;
struct data {
int state;
char buf[10];
};
static struct io_plan closer(struct io_conn *conn, struct data *d)
{
d->state++;
return io_close();
}
static struct io_plan connected(struct io_conn *conn, struct data *d2)
{
ok1(d2->state == 0);
d2->state++;
return io_read(d2->buf, sizeof(d2->buf), closer, d2);
}
static void init_conn(int fd, struct data *d)
{
ok1(d->state == 0);
d->state++;
io_new_conn(fd, io_write(d->buf, sizeof(d->buf), closer, d));
io_close_listener(l);
}
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)
{
struct data *d = malloc(sizeof(*d)), *d2 = malloc(sizeof(*d2));
struct addrinfo *addrinfo;
int fd;
/* This is how many tests you plan to run */
plan_tests(8);
d->state = 0;
memset(d->buf, 'a', sizeof(d->buf));
fd = make_listen_fd(PORT, &addrinfo);
ok1(fd >= 0);
l = io_new_listener(fd, init_conn, d);
ok1(l);
fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
addrinfo->ai_protocol);
d2->state = 0;
ok1(io_new_conn(fd, io_connect(fd, addrinfo, connected, d2)));
ok1(io_loop() == NULL);
ok1(d->state == 2);
ok1(d2->state == 2);
freeaddrinfo(addrinfo);
free(d);
free(d2);
/* 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