initial code towards transport implementation, doesn't compile new-transport
authorTero Marttila <terom@fixme.fi>
Tue, 28 Apr 2009 17:52:48 +0300
branchnew-transport
changeset 154 f4472119de3b
parent 153 d35e7cb3a489
child 155 c59d3eaff0fb
initial code towards transport implementation, doesn't compile
TODO
src/config.c
src/config.h
src/error.h
src/fifo.h
src/irc_net.c
src/irc_net_connect.c
src/irc_net_internal.h
src/sock.h
src/sock_fd.c
src/sock_fd.h
src/sock_tcp.h
src/sock_tcp_internal.h
src/transport.c
src/transport.h
src/transport_fd.c
src/transport_fd.h
src/transport_internal.h
--- a/TODO	Fri Apr 24 23:01:34 2009 +0300
+++ b/TODO	Tue Apr 28 17:52:48 2009 +0300
@@ -1,8 +1,5 @@
 sock:
- * async SSL handshake
- * sock_openssl, or improve sock_gnutls
- * client certs for sock_ssl_connect
- * server cert validation for sock_ssl_connect
+ * sock_openssl (as sock_gnutls is kind of 'meh' somehow)
  * tests...
 
 irc_queue:
@@ -16,18 +13,18 @@
  * reconnect, maybe cycling servers?
 
 config:
- * user-defined types
+ * user-defined types (!)
  * return values
 
 console:
  * improve console_print further, to act more like rlwrap
 
 lua_console:
- * some kind of remote console
+ * some kind of remote console?
 
 irc_log:
  * recode to valid UTF8
 
 logwatch:
- * figure out how to handle overflow
+ * figure out how to handle message length overflow
 
--- a/src/config.c	Fri Apr 24 23:01:34 2009 +0300
+++ b/src/config.c	Tue Apr 28 17:52:48 2009 +0300
@@ -213,21 +213,6 @@
     return config_apply_opt(option, ctx, value, err);
 }
 
-/**
- * Look up an option's param by name, returning NULL if not found
- */
-static const struct config_param* config_get_param (const struct config_option *option, const char *name)
-{
-    const struct config_param *param;
-
-    for (param = option->params; param->name && param->type; param++)
-        if (strcmp(param->name, name) == 0)
-            return param;
-    
-    // not found
-    return NULL;
-}
-
 const struct config_value* config_get_value (const struct config_option *option, const struct config_value values[], const char *name)
 {
     const struct config_param *param;
@@ -263,10 +248,10 @@
     return (value = config_get_value(option, values, name)) ? value->irc_chan : NULL;
 }
 
-void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const char *user_type)
+void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const struct config_user_type *user_type)
 {
     const struct config_value *value;
 
-    return ((value = config_get_value(option, values, name)) && strcmp(value->user.type, user_type) == 0) ? value->user.ptr : NULL;
+    return ((value = config_get_value(option, values, name)) && value->user.type == user_type) ? value->user.ptr : NULL;
 }
 
--- a/src/config.h	Fri Apr 24 23:01:34 2009 +0300
+++ b/src/config.h	Tue Apr 28 17:52:48 2009 +0300
@@ -29,6 +29,14 @@
 };
 
 /**
+ * A CONFIG_USER type info
+ */
+struct config_user_type {
+    /** The name of the type */
+    const char *name;
+};
+
+/**
  * Structure to hold a value as defined by config_type
  */
 struct config_value {
@@ -46,9 +54,9 @@
         /** Value for CONFIG_USER */
         struct {
             /** The specific user type */
-            const char *type;
+            const struct config_user_type *type;
 
-            /** A pointer to the user type */
+            /** The pointer value */
             void *ptr;
         } user;
     };
@@ -65,7 +73,7 @@
     enum config_type type;
 
     /** The specific type for CONFIG_USER */
-    const char *user_type;
+    const struct config_user_type *user_type;
 
     /** Description */
     const char *description;
@@ -268,6 +276,6 @@
 
 const char* config_get_string (const struct config_option *option, const struct config_value values[], const char *name);
 struct irc_chan* config_get_irc_chan (const struct config_option *option, const struct config_value values[], const char *name);
-void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const char *user_type);
+void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const struct config_user_type *user_type);
 
 #endif
--- a/src/error.h	Fri Apr 24 23:01:34 2009 +0300
+++ b/src/error.h	Tue Apr 28 17:52:48 2009 +0300
@@ -155,6 +155,11 @@
 };
 
 /**
+ * The public names
+ */
+typedef struct error_info error_t;
+
+/**
  * Translate an err_t into a function name.
  */
 const char *error_name (err_t code);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fifo.h	Tue Apr 28 17:52:48 2009 +0300
@@ -0,0 +1,18 @@
+#ifndef FIFO_H
+#define FIFO_H
+
+#include "transport.h"
+
+/**
+ * A read-only "socket" based on a FIFO, this provides nonblocking read operations by re-opening the FIFO on EOF.
+ *
+ * The transport will be ready for use right away, transport_callbacks::on_connect will never be called.
+ *
+ * @param transport the unconnected transport.
+ * @param path the path to the filesystem fifo object
+ * @param err returned error info
+ */
+err_t fifo_open_read (transport_t *transport, const char *path, error_t *err);
+
+
+#endif
--- a/src/irc_net.c	Fri Apr 24 23:01:34 2009 +0300
+++ b/src/irc_net.c	Tue Apr 28 17:52:48 2009 +0300
@@ -65,7 +65,7 @@
     log_err_info(err, "irc_conn failed");
     
     // tear down state
-    irc_net_disconnect(net, err);
+    irc_net_disconnect(net);
     
     // reconnect, either right away, or at the five-minute interval
     if (irc_net_connect(net, (time(NULL) - net->connected_ts > IRC_NET_RECONNECT_INTERVAL), err))
@@ -78,10 +78,11 @@
 static void irc_net_conn_quit (struct irc_conn *conn, void *arg)
 {
     struct irc_net *net = arg;
+    
+    (void) conn;
 
     // clean up the conn
-    irc_conn_destroy(conn);
-    net->conn = NULL;
+    irc_net_disconnect(net);
 
     // XXX: notify user
 }
--- a/src/irc_net_connect.c	Fri Apr 24 23:01:34 2009 +0300
+++ b/src/irc_net_connect.c	Tue Apr 28 17:52:48 2009 +0300
@@ -5,12 +5,10 @@
 #include <time.h>
 #include <assert.h>
 
-void irc_net_disconnect (struct irc_net *net, struct error_info *err)
+void irc_net_disconnect (struct irc_net *net)
 {
     struct irc_chan *chan = NULL;
 
-    (void) err;
-
     // mark
     net->connected = false;
 
--- a/src/irc_net_internal.h	Fri Apr 24 23:01:34 2009 +0300
+++ b/src/irc_net_internal.h	Tue Apr 28 17:52:48 2009 +0300
@@ -19,7 +19,7 @@
 /**
  * Destroy our irc_conn, and mark ourselves as disconnected.
  */
-void irc_net_disconnect (struct irc_net *net, struct error_info *err);
+void irc_net_disconnect (struct irc_net *net);
 
 /**
  * Fixed delay between reconnection attempts in seconds
--- a/src/sock.h	Fri Apr 24 23:01:34 2009 +0300
+++ b/src/sock.h	Tue Apr 28 17:52:48 2009 +0300
@@ -4,37 +4,13 @@
 /**
  * @file
  *
- * Low-level socket-related functions
- *
- * XXX: not just sockets anymore
+ * General sock_* interface.
  */
 #include "error.h"
 #include <sys/types.h>
 #include <event2/event.h>
 
 /**
- * The generic stream socket handle
- */
-struct sock_stream;
-
-/**
- * Callback for connect_async completion notification. If err is NULL, the connection completed succesfully,
- * otherwise, it failed.
- */
-typedef void (*sock_stream_connect_cb) (struct sock_stream *sock, struct error_info *err, void *arg);
-
-/**
- * Async callbacks for socket operation
- */
-struct sock_stream_callbacks {
-    /** Socket is readable */
-    void (*on_read) (struct sock_stream *sock, void *arg);
-
-    /** Socket is writeable */
-    void (*on_write) (struct sock_stream *sock, void *arg);
-};
-
-/**
  * Socket function error codes
  */
 enum sock_error_code {
@@ -59,106 +35,6 @@
  * @param ev_base the libevent base to use for events
  * @param err returned error info
  */
-err_t sock_init (struct event_base *ev_base, struct error_info *err);
-
-/**
- * A simple TCP connect to the given host/service, using getaddrinfo. The connected socket is returned via *sock_ptr. 
- * In case of errors, additional error information is stored in *err.
- *
- * @param sock_ptr the new sock_stream
- * @param host the hostname to connect to
- * @param service the service name (i.e. port) to connect to
- * @param err returned error info
- */
-err_t sock_tcp_connect (struct sock_stream **sock_ptr, const char *host, const char *service, struct error_info *err);
-
-/**
- * Start a non-blocking TCP connect to the given host/service. The socket will not yet be connected once the function
- * returns, but rather, the readyness of the socket will be indicated later using the given \a cb_func.
- *
- * Note that currently it is an error to call sock_stream_event_init before the cb_func has been called.
- *
- * XXX: blocking DNS resolution
- *
- * @param sock_ptr the new sock_stream
- * @param host the hostname to connect to
- * @param service the service name (i.e. port) to connect to
- * @param cb_func the callback used to handle the result of the async operation
- * @param cb_arg opaque context argument passed back to cb_func
- * @param err returned error info
- */
-err_t sock_tcp_connect_async (struct sock_stream **sock_ptr, const char *host, const char *service, 
-        sock_stream_connect_cb cb_func, void *cb_arg, struct error_info *err);
-
-/**
- * A read-only "socket" based on a FIFO, this provides nonblocking read operations by re-opening the FIFO on EOF.
- */
-err_t fifo_open_read (struct sock_stream **stream_ptr, const char *path, struct error_info *err);
-
-/**
- * Read a series of bytes from the socket into the given \a buf (up to \a len bytes). If succesfull, this returns
- * the number of bytes read (which will be less than or equal to \a len). If the socket is nonblocking (i.e.
- * sock_stream_event_init() was set), and there is no data available, this returns zero, and one should use
- * sock_stream_event_enable() to wait for more data.
- *
- * On errors, this returns the negative err_t code, and the specific error information can be accessed using
- * sock_stream_error()..
- *
- * @param sock the socket to read the bytes from
- * @param buf the byte buffer to write the bytes into
- * @param len the number of bytes to read into the buffer
- * @return bytes read, zero if none available, -err_t
- */
-int sock_stream_read (struct sock_stream *sock, void *buf, size_t len);
-
-/**
- * Write a series of bytes from the given \a buf (containing \a len bytes) to the socket. If succesfull, this returns
- * the number of bytes written (which may be less than \a len if the OS write buffer was full). If the socket is
- * nonblocking (i.e. sock_stream_event_init() was set), and the operation would have blocked, no data was written, and
- * this returns zero, and one should use sock_stream_event_enable() to retry.
- *
- * On errors, this returns the negative err_t code, and the specific error information can be accessed using
- * sock_stream_error().
- *
- * @param sock the socket to write the bytes to
- * @param buf the byte buffer
- * @param len number of bytes to write
- * @return bytes written, zero if would have blocked, -err_t
- */
-int sock_stream_write (struct sock_stream *sock, const void *buf, size_t len);
-
-/**
- * Initialize event-based operation for this sock_stream. This will set the stream into nonblocking mode, and the given
- * callbacks will be fired once enabled using sock_stream_event_enable().
- *
- * Note that the callbacks struct isn't copied - it's used as-is-given.
- *
- * @param sock the socket to set up for nonblocking operation
- * @param callbacks the on_read/on_write callbacks to invoke
- * @param arg the context argument for the callbacks
- */
-err_t sock_stream_event_init (struct sock_stream *sock, const struct sock_stream_callbacks *callbacks, void *arg);
-
-/**
- * Enable some events for this sock, as set up earlier with event_init. Mask should contain EV_READ/EV_WRITE.
- *
- * The implementation of this is slightly hazy for complex protocols; this should only be used to map from
- * sock_stream_read/write to the corresponding sock_stream_callback. That is, if sock_stream_read returns zero, then
- * call event_enable(EV_READ), wherepon on_read will later be called. Other operations (such as calling
- * sock_stream_write with *different* data after it once returns zero) might result in errors.
- */
-err_t sock_stream_event_enable (struct sock_stream *sock, short mask);
-
-/**
- * Get current error_info for \a sock.
- */
-const struct error_info* sock_stream_error (struct sock_stream *sock);
-
-/**
- * Close and release the given socket, ignoring errors. It must not be used anymore after this.
- *
- * This is intended to be used to abort in case of errors, and does not close the connection cleanly.
- */
-void sock_stream_release (struct sock_stream *sock);
+err_t sock_init (struct event_base *ev_base, error_t *err);
 
 #endif
--- a/src/sock_fd.c	Fri Apr 24 23:01:34 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +0,0 @@
-#include "sock_fd.h"
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <assert.h>
-
-void sock_fd_event_handler (evutil_socket_t fd, short what, void *arg) 
-{
-    struct sock_fd *sock = arg;
-
-    (void) fd;
-
-    // invoke appropriate callback
-    sock_stream_invoke_callbacks(SOCK_FD_BASE(sock), what);
-}
-
-err_t sock_fd_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err)
-{
-    struct sock_fd *sock = SOCK_FROM_BASE(base_sock, struct sock_fd);
-    int ret;
-    
-    // read(), and detect non-EAGAIN or EOF
-    if ((ret = read(sock->fd, buf, *len)) < 0 && errno != EAGAIN)
-        // unexpected error
-        RETURN_SET_ERROR_ERRNO(err, ERR_READ);
-    
-    else if (ret == 0)
-        // EOF
-        return SET_ERROR(err, ERR_READ_EOF);
-
-
-    if (ret < 0) {
-        // EAGAIN -> zero bytes
-        *len = 0;
-
-    } else {
-        // normal -> bytes read
-        *len = ret;
-    }
-
-    // ok
-    return SUCCESS;
-}
-
-err_t sock_fd_write (struct sock_stream *base_sock, const void *buf, size_t *len, struct error_info *err)
-{
-    struct sock_fd *sock = SOCK_FROM_BASE(base_sock, struct sock_fd);
-    int ret;
-    
-    // write(), and detect non-EAGAIN or EOF
-    if ((ret = write(sock->fd, buf, *len)) < 0 && errno != EAGAIN)
-        // unexpected error
-        RETURN_SET_ERROR_ERRNO(err, ERR_WRITE);
-    
-    else if (ret == 0)
-        // EOF
-        return SET_ERROR(err, ERR_WRITE_EOF);
-
-
-    if (ret < 0) {
-        // EAGAIN -> zero bytes
-        *len = 0;
-
-    } else {
-        // normal -> bytes read
-        *len = ret;
-    }
-
-    return SUCCESS;
-}
-
-err_t sock_fd_event_init (struct sock_stream *base_sock)
-{
-    struct sock_fd *sock = SOCK_FROM_BASE(base_sock, struct sock_fd);
-    err_t err;
-
-    // set nonblocking
-    if ((err = sock_fd_set_nonblock(sock, 1)))
-        return err;
-
-    // add ourselves as the event handler
-    if ((err = sock_fd_init_ev(sock, &sock_fd_event_handler, sock)))
-        return err;
-    
-    // done
-    return SUCCESS;
-}
-
-err_t sock_fd_event_enable (struct sock_stream *base_sock, short mask)
-{
-    struct sock_fd *sock = SOCK_FROM_BASE(base_sock, struct sock_fd);
-    
-    // implemented in sock_fd_add_event
-    return sock_fd_enable_events(sock, mask);
-}
-
-void sock_fd_init (struct sock_fd *sock, int fd)
-{
-    assert(!sock->ev_read && !sock->ev_write);
-
-    // initialize
-    sock->fd = fd;
-}
-
-err_t sock_fd_set_nonblock (struct sock_fd *sock, bool nonblock)
-{
-    // fcntl it
-    // XXX: maintain old flags?
-    if (fcntl(sock->fd, F_SETFL, nonblock ? O_NONBLOCK : 0) < 0)
-        RETURN_SET_ERROR_ERRNO(SOCK_FD_ERR(sock), ERR_FCNTL);
-
-    // ok
-    return SUCCESS;
-}
-
-err_t sock_fd_init_ev (struct sock_fd *sock, void (*ev_cb)(evutil_socket_t, short, void *), void *cb_arg)
-{
-    // require valid fd
-    assert(sock->fd >= 0);
-
-    // this is initialization
-    assert(sock->ev_read == NULL && sock->ev_write == NULL);
-
-    // store
-    sock->ev_cb = ev_cb;
-    sock->ev_arg = cb_arg;
-    
-    // create new event
-    if ((sock->ev_read = event_new(_sock_stream_ctx.ev_base, sock->fd, EV_READ, ev_cb, cb_arg)) == NULL)
-        return SET_ERROR(SOCK_FD_ERR(sock), ERR_EVENT_NEW);
-
-    if ((sock->ev_write = event_new(_sock_stream_ctx.ev_base, sock->fd, EV_WRITE, ev_cb, cb_arg)) == NULL)
-        return SET_ERROR(SOCK_FD_ERR(sock), ERR_EVENT_NEW);
-
-    // ok
-    return SUCCESS;
-}
-
-err_t sock_fd_enable_events (struct sock_fd *sock, short mask)
-{
-    // just add the appropraite events
-    if (mask & EV_READ && event_add(sock->ev_read, NULL))
-        return SET_ERROR(SOCK_FD_ERR(sock), ERR_EVENT_ADD);
- 
-    if (mask & EV_WRITE && event_add(sock->ev_write, NULL))
-        return SET_ERROR(SOCK_FD_ERR(sock), ERR_EVENT_ADD);
-    
-    // done
-    return SUCCESS;
-}
-
-static void sock_fd_free_ev (struct sock_fd *sock)
-{
-    if (sock->ev_read) {
-        event_free(sock->ev_read);
-
-        sock->ev_read = NULL;
-    }
-
-    if (sock->ev_write) {
-        event_free(sock->ev_write);
-        
-        sock->ev_write = NULL;
-    }
-}
-
-void sock_fd_deinit_ev (struct sock_fd *sock)
-{
-    sock_fd_free_ev(sock);
-    sock->ev_cb = NULL;
-    sock->ev_arg = NULL;
-}
-
-err_t sock_fd_set (struct sock_fd *sock, int fd)
-{
-    // close the old one?
-    if (sock->fd >= 0)
-        // XXX: warn on errors
-        close(sock->fd);
-    
-    // remove any old events
-    sock_fd_free_ev(sock);
-
-    // set the new one
-    sock->fd = fd;
-    
-    // restore them
-    if (sock->ev_cb)
-        return sock_fd_init_ev(sock, sock->ev_cb, sock->ev_arg);
-
-    // ok
-    return SUCCESS;
-}
-
-err_t sock_fd_close (struct sock_fd *sock)
-{
-    struct error_info *err = SOCK_FD_ERR(sock);
-    
-    // no errors yet
-    RESET_ERROR(err);
-
-    // must be connected
-    assert(sock->fd >= 0);
-
-    // kill any events
-    sock_fd_deinit_ev(sock);
-
-    // close the socket itself
-    if (close(sock->fd))
-        SET_ERROR_ERRNO(err, ERR_CLOSE);
-
-    // invalidate
-    sock->fd = -1;
-
-    return ERROR_CODE(err);
-}
-
--- a/src/sock_fd.h	Fri Apr 24 23:01:34 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-#ifndef SOCK_FD_H
-#define SOCK_FD_H
-/**
- * @file
- *
- * A generic sock_stream implementation for normal POSIX file descriptor based byte streams.
- */
-#include "sock_internal.h"
-#include <event2/event.h>
-#include <stdbool.h>
-
-/**
- * The fd-based sock_stream base implementation
- */
-struct sock_fd {
-    /** The base struct for sock_stream_* functions */
-    struct sock_stream base;
-    
-    /** The OS file descriptor */
-    int fd;
-
-    /** The callback and arg used for sock_fd_init_ev - required for sock_fd_set */
-    void (*ev_cb) (evutil_socket_t, short, void *);
-    void *ev_arg;
-
-    /** The IO events */
-    struct event *ev_read, *ev_write;
-
-};
-
-/**
- * Get a sock_stream pointer from a sock_fd
- */
-#define SOCK_FD_BASE(sock_ptr) (&(sock_ptr)->base)
-
-/**
- * Get the sock_stream.err pointer from a sock_fd
- */
-#define SOCK_FD_ERR(sock_ptr) SOCK_ERR(SOCK_FD_BASE(sock_ptr))
-
-
-
-/**
- * Callback suitable for use with sock_fd_init_ev, which just invoke's the sock_stream's callbacks as appropriate.
- */
-void sock_fd_event_handler (evutil_socket_t fd, short what, void *arg);
-
-/**
- * sock_stream_methods::read implementation.
- */
-err_t sock_fd_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err);
-
-/**
- * sock_stream_methods::write implementation.
- */
-err_t sock_fd_write (struct sock_stream *base_sock, const void *buf, size_t *len, struct error_info *err);
-
-/**
- * sock_stream_methods::event_init implementation.
- */
-err_t sock_fd_event_init (struct sock_stream *base_sock);
-
-/**
- * sock_stream_methods::event_enable implementation.
- */
-err_t sock_fd_event_enable (struct sock_stream *base_sock, short mask);
-
-
-
-/**
- * Initialize the sock_fd with the given fd, or -1, if no valid fd yet.
- */
-void sock_fd_init (struct sock_fd *sock, int fd);
-
-/**
- * Set the socket's nonblock mode. This should not do anything (apart from an extraneous syscall) if non-blocking
- * mode is already set.
- */
-err_t sock_fd_set_nonblock (struct sock_fd *sock, bool nonblock);
-
-/**
- * Initialize sock_fd.ev_* to use the socket's fd with the given callback. The ev's are not activated yet.
- *
- * The sock_fd must *not* have any ev's set.
- */
-err_t sock_fd_init_ev (struct sock_fd *sock, void (*ev_cb) (evutil_socket_t, short, void *), void *arg);
-
-/**
- * event_add the specified ev_* events, so they are enabled and the callback will be executed.
- */
-err_t sock_fd_enable_events (struct sock_fd *sock, short mask);
-
-/**
- * The opposite of init_ev, this clears any set events, so that they can be re-initialized with init_ev.
- */
-void sock_fd_deinit_ev (struct sock_fd *sock);
-
-/**
- * Update a sock_fd's fd, also updating any events set with sock_fd_init_ev. If any events were enabled before, they
- * are not enabled anymore.
- */
-err_t sock_fd_set (struct sock_fd *sock, int fd);
-
-/**
- * Close an opened sock_fd, restoring it to a state suitable for sock_fd_init
- */
-err_t sock_fd_close (struct sock_fd *sock);
-
-
-
-#endif
--- a/src/sock_tcp.h	Fri Apr 24 23:01:34 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-#ifndef SOCK_TCP_H
-#define SOCK_TCP_H
-
-/**
- * @file
- *
- * TCP implementation of sock_stream interface.
- */
-#include "sock_internal.h"
-#include "sock_fd.h"
-#include <netdb.h>
-
-/**
- * Contains the base sock_stream struct, and the file descriptor
- */
-struct sock_tcp {
-    /** The base struct for sock_stream_* functions */
-    struct sock_fd base_fd;
-    
-    /** The current connect_async resolved address */
-    struct addrinfo *async_res, *async_cur;
-};
-
-/**
- * Get a sock_fd pointer from a sock_tcp pointer
- */
-#define SOCK_TCP_FD(sock_ptr) (&(sock_ptr)->base_fd)
-
-/**
- * Get a sock_base pointer from a sock_tcp pointer
- */
-#define SOCK_TCP_BASE(sock_ptr) SOCK_FD_BASE(SOCK_TCP_FD(sock_ptr))
-
-/**
- * Get the sock_stream.err pointer from a sock_tcp pointer
- */
-#define SOCK_TCP_ERR(sock_ptr) SOCK_ERR(SOCK_TCP_BASE(sock_ptr))
-
-/**
- * Initialize a blank sock_tcp by creating a new socket (using the socket() syscall), but doesn't do anything further.
- *
- * This uses the ai_family, ai_socktype and ai_protocol fields from the given addrinfo.
- */
-err_t sock_tcp_init_socket (struct sock_tcp *sock, struct addrinfo *addr, struct error_info *err);
-
-/**
- * Initiate an async connection operation on the given socket to the given address. Once the connect() completes,
- * either the on_connect or the on_error callback will be called.
- *
- * If, for some weird reason, this ends up doing a blocking connect(), the on_connect callback will be called directly.
- * If the async connect fails, this just returns the error.
- *
- * @param sock the unconnected TCP sockect to connect with
- * @param addr the address to connect to
- * @param err returned error info
- */
-err_t sock_tcp_connect_async_addr (struct sock_tcp *sock, struct addrinfo *addr, struct error_info *err);
-
-/**
- * Attempt to connect asyncronously to the given hostname/service. Once a connection has been established, the
- * on_connect() callback will be called.
- *
- * In case of errors, either on_error() will be called, or an error returned - depending on when the error happaned.
- *
- * @param sock the unconnected TCP socket to connect with
- * @param hostname the hostname to resolve
- * @param service the service to connect to
- * @param err returned error info
- */
-err_t sock_tcp_connect_async_begin (struct sock_tcp *sock, const char *hostname, const char *service, struct error_info *err);
-
-/**
- * Initialize a blank sock_tcp by connecting in a blocking fashion.
- */
-err_t sock_tcp_connect_blocking (struct sock_tcp *sock, const char *hostname, const char *service, struct error_info *err);
-
-/**
- * Free a non-connected sock_tcp
- */
-void sock_tcp_free (struct sock_tcp *sock);
-
-#endif /* SOCK_TCP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sock_tcp_internal.h	Tue Apr 28 17:52:48 2009 +0300
@@ -0,0 +1,82 @@
+#ifndef SOCK_TCP_INTERNAL_H
+#define SOCK_TCP_INTERNAL_H
+
+/**
+ * @file
+ *
+ * Internal interface of the sock_tcp transport implementation.
+ */
+#include "sock_internal.h"
+#include "sock_fd.h"
+#include <netdb.h>
+
+/**
+ * Contains the base sock_stream struct, and the file descriptor
+ */
+struct sock_tcp {
+    /** The base struct for sock_stream_* functions */
+    struct sock_fd base_fd;
+    
+    /** The current connect_async resolved address */
+    struct addrinfo *async_res, *async_cur;
+};
+
+/**
+ * Get a sock_fd pointer from a sock_tcp pointer
+ */
+#define SOCK_TCP_FD(sock_ptr) (&(sock_ptr)->base_fd)
+
+/**
+ * Get a sock_base pointer from a sock_tcp pointer
+ */
+#define SOCK_TCP_BASE(sock_ptr) SOCK_FD_BASE(SOCK_TCP_FD(sock_ptr))
+
+/**
+ * Get the sock_stream.err pointer from a sock_tcp pointer
+ */
+#define SOCK_TCP_ERR(sock_ptr) SOCK_ERR(SOCK_TCP_BASE(sock_ptr))
+
+/**
+ * Initialize a blank sock_tcp by creating a new socket (using the socket() syscall), but doesn't do anything further.
+ *
+ * This uses the ai_family, ai_socktype and ai_protocol fields from the given addrinfo.
+ */
+err_t sock_tcp_init_socket (struct sock_tcp *sock, struct addrinfo *addr, struct error_info *err);
+
+/**
+ * Initiate an async connection operation on the given socket to the given address. Once the connect() completes,
+ * either the on_connect or the on_error callback will be called.
+ *
+ * If, for some weird reason, this ends up doing a blocking connect(), the on_connect callback will be called directly.
+ * If the async connect fails, this just returns the error.
+ *
+ * @param sock the unconnected TCP sockect to connect with
+ * @param addr the address to connect to
+ * @param err returned error info
+ */
+err_t sock_tcp_connect_async_addr (struct sock_tcp *sock, struct addrinfo *addr, struct error_info *err);
+
+/**
+ * Attempt to connect asyncronously to the given hostname/service. Once a connection has been established, the
+ * on_connect() callback will be called.
+ *
+ * In case of errors, either on_error() will be called, or an error returned - depending on when the error happaned.
+ *
+ * @param sock the unconnected TCP socket to connect with
+ * @param hostname the hostname to resolve
+ * @param service the service to connect to
+ * @param err returned error info
+ */
+err_t sock_tcp_connect_async_begin (struct sock_tcp *sock, const char *hostname, const char *service, struct error_info *err);
+
+/**
+ * Initialize a blank sock_tcp by connecting in a blocking fashion.
+ */
+err_t sock_tcp_connect_blocking (struct sock_tcp *sock, const char *hostname, const char *service, struct error_info *err);
+
+/**
+ * Free a non-connected sock_tcp
+ */
+void sock_tcp_free (struct sock_tcp *sock);
+
+#endif /* SOCK_TCP_INTERNAL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/transport.c	Tue Apr 28 17:52:48 2009 +0300
@@ -0,0 +1,21 @@
+#include "transport_internal.h"
+
+
+void transport_bind (transport_t *transport, const struct transport_type *type, const struct transport_info *info)
+{
+    // not already bound
+    assert(!transport->type && !transport->info);
+
+    // store
+    transport->type = type;
+    *transport->info = info;
+}
+
+void* transport_check (transport_t *transport, const struct transport_type *type)
+{
+    // sanity check
+    assert(type && transport->type == type);
+
+    // ok
+    return transport;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/transport.h	Tue Apr 28 17:52:48 2009 +0300
@@ -0,0 +1,99 @@
+#ifndef TRANSPORT_H
+#define TRANSPORT_H
+
+/**
+ * @file
+ *
+ * Defines a intermediate-level (as opposed to high-level or low-level) API for connected streams of data, presumeably
+ * non-blocking ones.
+ */
+#include "error.h"
+
+/**
+ * Opaque transport state
+ */
+struct transport;
+
+/**
+ * Because it's so annoying seeing "struct" everywhere
+ */
+typedef struct transport transport_t;
+
+/**
+ * Callbacks for structs
+ */
+struct transport_callbacks {
+    /**
+     * The transport is now connected
+     */
+    void (*on_connect) (transport_t *transport, void *arg);
+
+    /**
+     * Data is now available for reading from the transport
+     */
+    void (*on_read) (transport_t *transport, void *arg);
+
+    /**
+     * The transport has become writeable
+     */
+    void (*on_write) (transport_t *transport, void *arg);
+
+    /**
+     * An asynchronous error has occured. This is only called for errors that occur while being called directly from
+     * the underlying event loop, and never from inside an API function.
+     */
+    void (*on_error) (transport_t *transport, error_t *err, void *arg);
+};
+
+/**
+ * User info required to build a transport
+ */
+struct transport_info {
+    /** The callbacks table */
+    const struct transport_callbacks *cb_tbl;
+
+    /** The callback context argument */
+    void *cb_arg;
+};
+
+/**
+ * Read a series of bytes from the transport into the given \a buf (up to \a len bytes). If succesfull, this returns
+ * the number of bytes read (which will be less than or equal to \a len). If the transport is nonblocking, and there is
+ * no data available, this returns zero, and need not be called again until transport_callbacks::on_read is invoked.
+ *
+ * On errors, this returns the negative error code, and more info via \a err.
+ *
+ * @param transport the transport state
+ * @param buf the buffer to read the bytes into
+ * @param len the number of bytes to read into the buffer
+ * @param err returned error info
+ * @return bytes read, zero if none available, -err_t
+ */
+int transport_read (transport_t *transport, void *buf, size_t len, error_t *err);
+
+/**
+ * Write a series of bytes from the given \a buf (containing \a len bytes) to the transport. If succesfull, this
+ * returns the number of bytes written (which may be less than \a len). If the transport is nonblocking, and the
+ * operation would have blocked, no data will be written, and zero is returned.
+ *
+ * XXX: behaviour of transport_callbacks::on_write?
+ *
+ * On errors, this returns the negative error code, along with extended info via \a err.
+ *
+ * @param transport the transport state
+ * @param buf the buffer to write the bytes from
+ * @param len number of bytes to write
+ * @param err returned error info
+ * @return bytes written, zero if would have blocked, -err_t
+ */
+int transport_write (transport_t *transport, const void *buf, size_t len, error_t *err);
+
+/**
+ * Close and destroy the transport immediately, severing any established connection rudely.
+ *
+ * This will release all resources associated with the transport, including the transport itself, which must not be
+ * used anymore.
+ */
+void transport_destroy (transport_t *transport);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/transport_fd.c	Tue Apr 28 17:52:48 2009 +0300
@@ -0,0 +1,233 @@
+#include "transport_fd.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+/**
+ * Our pseudo-transport_type
+ */
+static struct transport_type transport_fd_type;
+
+/**
+ * Our libevent callback
+ */
+static void transport_fd_on_event (evutil_socket_t _fd, short what, void *arg) 
+{
+    struct transport_fd *fd = arg;
+
+    (void) _fd;
+
+    // invoke user callback
+    fd->cb_func(fd, what, fd->cb_arg);
+}
+
+/**
+ * Our transport_methods implementations
+ */
+err_t transport_fd_read (transport_t *transport, void *buf, size_t *len, error_t *err)
+{
+    struct transport_fd *fd = transport_check(transport, &transport_fd_type);
+    int ret;
+    
+    // read(), and detect non-EAGAIN or EOF
+    if ((ret = read(fd->fd, buf, *len)) < 0 && errno != EAGAIN)
+        // unexpected error
+        RETURN_SET_ERROR_ERRNO(err, ERR_READ);
+    
+    else if (ret == 0)
+        // EOF
+        return SET_ERROR(err, ERR_READ_EOF);
+
+
+    if (ret < 0) {
+        // EAGAIN -> zero bytes
+        *len = 0;
+
+    } else {
+        // normal -> bytes read
+        *len = ret;
+    }
+
+    // ok
+    return SUCCESS;
+}
+
+err_t transport_fd_write (transport_t *transport, const void *buf, size_t *len, struct error_info *err)
+{
+    struct transport_fd *sock = transport_check(transport, &transport_fd_type);
+    int ret;
+    
+    // write(), and detect non-EAGAIN or EOF
+    if ((ret = write(fd->fd, buf, *len)) < 0 && errno != EAGAIN)
+        // unexpected error
+        RETURN_SET_ERROR_ERRNO(err, ERR_WRITE);
+    
+    else if (ret == 0)
+        // EOF
+        return SET_ERROR(err, ERR_WRITE_EOF);
+
+
+    if (ret < 0) {
+        // EAGAIN -> zero bytes
+        *len = 0;
+
+    } else {
+        // normal -> bytes read
+        *len = ret;
+    }
+
+    return SUCCESS;
+}
+
+err_t transport_fd_destroy (transport_t *transport)
+{
+    // XXX: implement
+}
+
+struct transport_methods transport_fd_methods = {
+    .read       = transport_fd_read,
+    .write      = transport_fd_write,
+    .destroy    = transport_fd_destroy
+};
+
+/**
+ * Function implementations
+ */
+void transport_fd_init (struct transport_fd *fd, struct event_base *ev_base, int _fd)
+{
+    // sanity-check
+    assert(!fd->fd);
+    assert(!fd->ev_read && !fd->ev_write);
+
+    // initialize
+    fd->ev_base = ev_base;
+    fd->fd = _fd;
+    fd->cb_func = fd->cb_arg = NULL;
+}
+
+err_t transport_fd_nonblock (struct transport_fd *fd, bool nonblock)
+{
+    // XXX: maintain old flags?
+
+
+    // set new flags
+    if (fcntl(fd->fd, F_SETFL, nonblock ? O_NONBLOCK : 0) < 0)
+        return ERR_FCNTL;
+
+    return SUCCESS;
+}
+
+/**
+ * Install our internal event handler.
+ *
+ * The events should not already be set up.
+ *
+ * Cleans up partial events on errors
+ */
+err_t transport_fd_install (struct transport_fd *fd)
+{
+    assert(!fd->ev_read && !fd->ev_write);
+
+    // create new events
+    if ((fd->ev_read = event_new(fd->ev_base, fd->fd, EV_READ, transport_fd_on_event, fd)) == NULL)
+        goto err_event_add;
+
+    if ((fd->ev_write = event_new(fd->ev_base, fd->fd, EV_WRITE, transport_fd_on_event, fd)) == NULL)
+        goto err_event_add;
+    
+    // ok
+    return SUCCESS;
+
+err_event_add:
+    // remove partial events
+    transport_fd_clear(fd);
+
+    return ERR_EVENT_NEW;
+}
+
+err_t transport_fd_setup (struct transport_fd *fd, transport_fd_callback_func cb_func, void *cb_arg)
+{
+    // requires a valid fd
+    assert(fd->fd >= 0);
+    
+    // store
+    fd->ev_cb = ev_cb;
+    fd->ev_arg = cb_arg;
+    
+    // install the event handlers?
+    if (!fd->ev_read || !fd->ev_write)
+        transport_fd_install(fd);
+
+
+    return SUCCESS;
+}
+
+err_t transport_fd_enable (struct transport_fd *fd, short mask)
+{
+    // just add the appropraite events
+    if (mask & EV_READ && event_add(fd->ev_read, NULL))
+        return ERR_EVENT_ADD;
+ 
+    if (mask & EV_WRITE && event_add(fd->ev_write, NULL))
+        return ERR_EVENT_ADD;
+    
+
+    return SUCCESS;
+}
+
+/**
+ * Remove our current ev_* events, but leave the cb_* intact.
+ */
+static void transport_fd_remove (struct transport_fd *fd)
+{
+    if (fd->ev_read)
+        event_free(fd->ev_read);
+
+    if (sock->ev_write)
+        event_free(sock->ev_write);
+    
+    fd->ev_read = NULL;
+    fd->ev_write = NULL;
+}
+
+void transport_fd_clear (struct transport_fd *fd);
+{
+    // remove the events
+    transport_fd_remove(fd);
+    
+    // clear the callbacks
+    fd->cb_func = fd->cb_arg = NULL;
+}
+
+err_t transport_fd_set (struct transport_fd *fd, int _fd)
+{
+    // close the old stuff
+    transport_fd_close(fd);
+    
+    // set the new one
+    sock->fd = fd;
+    
+    // do we have callbacks that we need to setup?
+    if (sock->cb_func)
+        return transport_fd_install(fd);
+    else 
+        return SUCCESS;
+}
+
+err_t transport_fd_close (struct transport_fd *fd)
+{
+    // remove any installed events
+    transport_fd_remove(fd);
+
+    // close the socket itself
+    if (fd->fd >= 0 && close(fd->fd))
+        return ERR_CLOSE;
+
+    // invalidate
+    fd->fd = -1;
+    
+
+    return SUCCESS;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/transport_fd.h	Tue Apr 28 17:52:48 2009 +0300
@@ -0,0 +1,102 @@
+#ifndef TRANSPORT_FD_H
+#define TRANSPORT_FD_H
+
+/**
+ * @file
+ *
+ * Support for transport implementations that use POSIX file descriptor streams.
+ *
+ * This provides the read/write methods, as well as functions to help implement the event-based behaviour
+ */
+#include "transport_internal.h"
+
+#include <event2/event.h>
+#include <stdbool.h>
+
+/**
+ * Low-level callback
+ */
+typedef void (*transport_fd_callback_func) (struct transport_fd *tp_fd, short what, void *arg);
+
+/**
+ * The fd-based transport implementation
+ */
+struct transport_fd {
+    /** Base transport state */
+    struct transport base;
+
+    /** Libevent base to use */
+    struct ev_base *ev_base;
+    
+    /** OS file descriptor */
+    evutil_socket_t fd;
+
+    /** IO events */
+    struct event *ev_read, *ev_write;
+
+    /** Low-level callback */
+    transport_fd_callback_func cb_func;
+
+    /** Callback context argument */
+    void *cb_arg;
+
+};
+
+/**
+ * Get a transport_t pointer from a transport_fd
+ */
+#define TRANSPORT_FD_BASE(tp_ptr) (&(tp_ptr)->base)
+
+
+/**
+ * Generic implementations of the transport_methods
+ */
+extern const struct transport_methods transport_fd_methods;
+
+
+/**
+ * Initialize the transport_fd to use the given, connected fd, or -1 if we don't yet have an fd.
+ *
+ * It is an error to call this if the transport_fd already has an fd set
+ *
+ * @param fd the transport_fd state
+ * @param ev_base the libevent base to use
+ * @param _fd the OS file descriptor, or -1
+ */
+void transport_fd_init (struct transport_fd *fd, struct event_base *ev_base, int _fd);
+
+/**
+ * Set the fd's nonblocking mode using fcntl.
+ */
+err_t transport_fd_nonblock (struct transport_fd *fd, bool nonblock);
+
+/**
+ * Set or replace the fd's event callback. The callback will not be enabled.
+ *
+ * The transport must have a valid fd bound to it.
+ */
+err_t transport_fd_setup (struct transport_fd *fd, transport_fd_callback_func cb_func, void *cb_arg);
+
+/**
+ * Enable the callbacks for the specified events, any of { EV_WRITE, EV_READ }.
+ */
+err_t transport_fd_enable (struct transport_fd *fd, short mask);
+
+/**
+ * Remove any old event callback present, so it will not be called anymore.
+ */
+void transport_fd_clear (struct transport_fd *fd);
+
+/**
+ * Replace the old fd with a new one, maintaining any event callbacks set with transport_fd_callback. If any events were
+ * enabled before, they are not enabled anymore.
+ */
+err_t transport_fd_set (struct transport_fd *fd, int _fd);
+
+/**
+ * Close an opened fd, releasing all resources within our state.
+ */
+err_t transport_fd_close (struct transport_fd *fd);
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/transport_internal.h	Tue Apr 28 17:52:48 2009 +0300
@@ -0,0 +1,60 @@
+#ifndef TRANSPORT_INTERNAL_H
+#define TRANSPORT_INTERNAL_H
+
+/**
+ * @file
+ *
+ * The internal interface for transport implementations.
+ */ 
+#include "transport.h"
+
+/**
+ * Method table for implementation stuff
+ */
+struct transport_methods {
+    /** For transport_read() */
+    err_t (*read) (transport_t *transport, void *buf, size_t *len, error_t *err);
+
+    /** For transport_write() */
+    err_t (*write) (transport_t *transport, const void *buf, size_t *len, error_t *err);
+
+    /** Release the transport's state, but not the transport itself */
+    void (*destroy) (transport_t *transport);
+};
+
+/**
+ * The definition of a transport type
+ */
+struct transport_type {
+    /** Method table */
+    struct transport_methods methods;
+};
+
+/**
+ * The base transport type
+ */
+struct transport {
+    /** The type info, or NULL if not yet bound */
+    const struct transport_type *type;
+
+    /** User info */
+    struct transport_info info;
+};
+
+/**
+ * Bind the given transport to the given type with the given user info.
+ *
+ * It is a bug to call this with a transport that is already bound.
+ */
+void transport_bind (transport_t *transport, const struct transport_type *type, const struct transport_info *info);
+
+/**
+ * Check the type of the transport, and return the transport as a void* suitable for casting to the appropriate struct
+ * for the type.
+ *
+ * It is a bug to call this with a transport of a different type.
+ */
+void* transport_check (transport_t *transport, const struct transport_type *type);
+
+
+#endif /* TRANSPORT_INTERNAL_H */