add sock_stream_release/line_proto_release/irc_conn_release functions, and add proper cleanup to irc_net_create
authorTero Marttila <terom@fixme.fi>
Tue, 10 Mar 2009 02:34:11 +0200
changeset 28 9c1050bc8709
parent 27 e6639132bead
child 29 3f0f2898fea3
add sock_stream_release/line_proto_release/irc_conn_release functions, and add proper cleanup to irc_net_create
src/irc_conn.c
src/irc_conn.h
src/irc_net.c
src/line_proto.c
src/line_proto.h
src/sock.c
src/sock.h
src/sock_gnutls.c
src/sock_gnutls.h
src/sock_internal.h
src/sock_tcp.c
src/sock_tcp.h
--- a/src/irc_conn.c	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/irc_conn.c	Tue Mar 10 02:34:11 2009 +0200
@@ -1,4 +1,3 @@
-
 #include "irc_conn.h"
 #include "irc_cmd.h"
 #include "log.h"
@@ -99,16 +98,45 @@
     
     // add the core handlers 
     if ((ERROR_CODE(err) = irc_conn_register_handler_chain(conn, _cmd_handlers, NULL)))
-        return ERROR_CODE(err);
+        goto error;
 
     // create the line_proto, with our on_line handler
     if (line_proto_create(&conn->lp, sock, IRC_LINE_MAX * 1.5, &irc_conn_on_line, conn, err))
-        return ERROR_CODE(err);
+        goto error;
 
     // ok
     *conn_ptr = conn;
 
     return SUCCESS;
+
+error:
+    // release
+    irc_conn_destroy(conn);
+
+    return ERROR_CODE(err);    
+}
+
+void irc_conn_destroy (struct irc_conn *conn)
+{
+    struct irc_cmd_chain *next = STAILQ_FIRST(&conn->handlers);
+
+    // release the line_proto
+    if (conn->lp)
+        line_proto_release(conn->lp);
+
+    // clean up any handler chains
+    while (next) {
+        struct irc_cmd_chain *node = next;
+
+        // update next
+        next = STAILQ_NEXT(node, node);
+
+        // free
+        free(node);
+    }
+
+    // free the irc_conn itself
+    free(conn);
 }
 
 err_t irc_conn_register_handler_chain (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg)
--- a/src/irc_conn.h	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/irc_conn.h	Tue Mar 10 02:34:11 2009 +0200
@@ -85,6 +85,13 @@
         void *cb_arg, struct error_info *err);
 
 /**
+ * Destroy the irc_conn state, terminating any connection and releasing all resources.
+ *
+ * This does not end the session cleanly, and is intended mainly to be used after clean exit, or to clean up after errors.
+ */
+void irc_conn_destroy (struct irc_conn *conn);
+
+/**
  * Add a new chain of command handler callbacks to be used to handle irc_lines from the server. The given arg will be
  * passed to the callbacks as the context argument. The chain will be appended to the end of the current list of chains.
  *
--- a/src/irc_net.c	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/irc_net.c	Tue Mar 10 02:34:11 2009 +0200
@@ -30,7 +30,7 @@
 err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, struct error_info *err)
 {
     struct irc_net *net;
-    struct sock_stream *sock;
+    struct sock_stream *sock = NULL;
     
     // allocate
     if ((net = calloc(1, sizeof(*net))) == NULL)
@@ -54,13 +54,13 @@
 
     }
 
-    log_info("connected, registering");
-
     // create the irc connection state
     if (irc_conn_create(&net->conn, sock, &_conn_callbacks, net, err))
         goto error;
 
     // register
+    log_info("connected, registering");
+
     if ((ERROR_CODE(err) = irc_conn_register(net->conn, &info->register_info)))
         goto error;
     
@@ -70,7 +70,17 @@
     return SUCCESS;
 
 error:
-    // XXX: cleanup
+    // cleanup
+    if (net->conn)
+        // irc_conn takes care of the socket as well
+        irc_conn_destroy(net->conn);
+    
+    else if (sock)
+        // we need to clean up the socket ourself
+        sock_stream_release(sock);
+    
+    // release our state
+    free(net);
 
     return ERROR_CODE(err);
 }
--- a/src/line_proto.c	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/line_proto.c	Tue Mar 10 02:34:11 2009 +0200
@@ -133,14 +133,9 @@
     return SUCCESS;
 
 error:
-    if (lp) {
-        free(lp->in_buf);
-        free(lp->out_buf);
-
-        // XXX: handle sock init errors
-    }
-
-    free(lp);
+    // cleanup the lp
+    if (lp)
+        line_proto_release(lp);
 
     return ERROR_CODE(err);
 }
@@ -250,7 +245,7 @@
 int line_proto_send (struct line_proto *lp, const char *line)
 {
     int ret;
-    size_t len = strlen(line);
+    size_t len = strlen(line), ret_len;
 
     // drop line if we already have output buffered
     if (lp->out_offset)
@@ -263,16 +258,19 @@
         return -ERROR_CODE(&lp->err);
     }
 
+    // length of the sent data
+    ret_len = ret;
+
     // EAGAIN or partial?
-    if (ret < len) {
-        size_t trailing = len - ret;
+    if (ret_len < len) {
+        size_t trailing = len - ret_len;
 
         // ensure it's not waaaay too long
         if (trailing > lp->buf_len)
             return -ERR_LINE_TOO_LONG;
 
         // copy remaining portion to buffer
-        memcpy(lp->out_buf, line + ret, trailing);
+        memcpy(lp->out_buf, line + ret_len, trailing);
 
         // update offset
         lp->out_offset = trailing;
@@ -294,6 +292,7 @@
 int line_proto_flush (struct line_proto *lp)
 {
     int ret;
+    size_t ret_len;
 
     // try and write the line
     if ((ret = sock_stream_write(lp->sock, lp->out_buf, lp->out_offset)) < 0) {
@@ -302,19 +301,21 @@
         return -ERROR_CODE(&lp->err);
     }
 
+    ret_len = ret;
+
     // empty now?
-    if (ret == lp->out_offset) {
+    if (ret_len == lp->out_offset) {
         lp->out_offset = 0;
 
         return SUCCESS;
     }
 
     // partial?
-    if (ret > 0) {
-        size_t remaining = lp->out_offset - ret;
+    if (ret_len > 0) {
+        size_t remaining = lp->out_offset - ret_len;
 
         // move the rest up
-        memmove(lp->out_buf, lp->out_buf + ret, remaining);
+        memmove(lp->out_buf, lp->out_buf + ret_len, remaining);
 
         // update offset
         lp->out_offset = remaining;
@@ -334,3 +335,17 @@
     return &lp->err;
 }
 
+void line_proto_release (struct line_proto *lp)
+{
+    // free buffers
+    free(lp->in_buf);
+    free(lp->out_buf);
+
+    // socket?
+    if (lp->sock)
+        sock_stream_release(lp->sock);
+
+    // free the state itself
+    free(lp);
+}
+
--- a/src/line_proto.h	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/line_proto.h	Tue Mar 10 02:34:11 2009 +0200
@@ -17,40 +17,58 @@
  */
 typedef void (*line_proto_read_cb)(char *line, void *arg);
 
-/*
+/**
  * Create a new line_proto off the the given sock_stream. The newly allocated line_proto will be returned via *lp_ptr.
  *
  * The incoming lines are buffered in a buffer of \a buf_size bytes. This imposes a maximum limit on the line length.
  *
  * The given callback function/argument will be used to provide event-based recv support.
+ *
+ * @param lp_ptr a pointer to the new line_proto will be returned via this pointer
+ * @param sock the sock_stream to use
+ * @param buf_size the incoming/outgoing buffer size, should be enough to hold the biggest possible line
+ * @param cb_func the read_cb callback
+ * @param cb_arg the read_cb callback argument
+ * @param err error information is returned via this pointer
  */
 err_t line_proto_create (struct line_proto **lp_ptr, struct sock_stream *sock, size_t buf_size,
         line_proto_read_cb cb_func, void *cb_arg, struct error_info *err);
 
-/*
+/**
  * Runs the socket recv() into our internal buffer. If a full line was received, a pointer to our internal bufffer is
  * returned via *line_ptr, and we return SUCCESS. If we don't yet have a full line, and receiving more would block,
  * NULL is returned via *line_ptr instead. Otherwise, nonzero error return code.
+ *
+ * @param line_ptr a pointer to the received line is returned via this pointer
  */
 err_t line_proto_recv (struct line_proto *lp, char **line_ptr);
 
-/*
+/**
  * Write a single line to the sock_stream, buffering any incomplete fragment that remains unset. Returns zero if the
  * line was succesfully sent, >0 if it was only partially sent, or -err on errors.
  *
  * The given line should already include the terminating '\r\n' character sequence.
+ *
+ * @param line pointer to buffer containing \r\n\0 terminated line
  */
 int line_proto_send (struct line_proto *lp, const char *line);
 
-/*
+/**
  * Flush out any buffered line fragment. Returns zero if the buffer was flushed empty, >0 if there's still fragments
  * remaining, or -err on errors.
  */
 int line_proto_flush (struct line_proto *lp);
 
-/*
+/**
  * Get current error_info*
  */
 const struct error_info* line_proto_error (struct line_proto *lp);
 
+/**
+ * Release any allocated buffers, and the underlying sock_stream.
+ *
+ * This does not close the connection cleanly, and is intended for use to abort after errors.
+ */
+void line_proto_release (struct line_proto *lp);
+
 #endif /* LINE_PROTO_H */
--- a/src/sock.c	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/sock.c	Tue Mar 10 02:34:11 2009 +0200
@@ -83,3 +83,9 @@
     if (what & EV_WRITE && sock->cb_info->on_write)
         sock->cb_info->on_read(sock, sock->cb_arg);
 }
+
+void sock_stream_release (struct sock_stream *sock)
+{
+    sock->type->methods.release(sock);
+}
+
--- a/src/sock.h	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/sock.h	Tue Mar 10 02:34:11 2009 +0200
@@ -81,4 +81,11 @@
  */
 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);
+
 #endif
--- a/src/sock_gnutls.c	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/sock_gnutls.c	Tue Mar 10 02:34:11 2009 +0200
@@ -125,6 +125,21 @@
     return SUCCESS;
 }
 
+static void sock_gnutls_release (struct sock_stream *base_sock)
+{
+    struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
+
+    // close the session rudely
+    // XXX: does this actually do everything we need it to? Don't want to call gnutls_bye here, since we're void...
+    gnutls_deinit(sock->session);
+    
+    // terminate the TCP transport
+    sock_tcp_close(SOCK_GNUTLS_TCP(sock));
+
+    // free
+    free(sock);
+}
+
 /*
  * Our sock_stream_Type
  */
@@ -134,6 +149,7 @@
         .write              = &sock_gnutls_write,
         .event_init         = &sock_gnutls_event_init,
         .event_enable       = &sock_gnutls_event_enable,
+        .release            = &sock_gnutls_release,
     },
 };
 
--- a/src/sock_gnutls.h	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/sock_gnutls.h	Tue Mar 10 02:34:11 2009 +0200
@@ -40,7 +40,7 @@
 #define SOCK_GNUTLS_TCP(sock_ptr) (&(sock_ptr)->base_tcp)
 #define SOCK_GNUTLS_ERR(sock_ptr) SOCK_ERR(SOCK_GNUTLS_BASE(sock_ptr))
 
-/*
+/**
  * Initialize the global gnutls state
  */
 err_t sock_gnutls_global_init (struct error_info *err);
--- a/src/sock_internal.h	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/sock_internal.h	Tue Mar 10 02:34:11 2009 +0200
@@ -1,9 +1,14 @@
 #ifndef SOCK_INTERNAL_H
 #define SOCK_INTERNAL_H
 
+/**
+ * @file
+ *
+ * internal sock_* interface
+ */
 #include "sock.h"
 
-/*
+/**
  * Global config related to all sock_streams
  */
 extern struct sock_stream_ctx {
@@ -12,24 +17,26 @@
 
 } _sock_stream_ctx;
 
-/*
+/**
  * The base type struct, which defines the method table.
  */
 struct sock_stream_type {
-    /* method table */
+    /** Method table */
     struct sock_stream_methods {
-        /* Normal read(2) */
+        /** As read(2) */
         err_t (*read) (struct sock_stream *sock, void *buf, size_t *len);
 
-        /* Normal write(2) */
+        /** As write(2) */
         err_t (*write) (struct sock_stream *sock, const void *buf, size_t *len);
 
-        /* Initialize events. cb_info/cb_arg are already updated */
+        /** Initialize events. cb_info/cb_arg are already updated */
         err_t (*event_init) (struct sock_stream *sock);
 
-        /* Enable events as specified */
+        /** Enable events as specified */
         err_t (*event_enable) (struct sock_stream *sock, short mask);
-
+        
+        /** Release all resources and free the sock_stream */
+        void (*release) (struct sock_stream *sock);
     } methods;
 };
 
--- a/src/sock_tcp.c	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/sock_tcp.c	Tue Mar 10 02:34:11 2009 +0200
@@ -109,6 +109,15 @@
     return sock_tcp_add_event(sock, mask);
 }
 
+static void sock_tcp_release (struct sock_stream *base_sock)
+{
+    struct sock_tcp *sock = SOCK_FROM_BASE(base_sock, struct sock_tcp);
+    
+    // close and free
+    sock_tcp_close(sock);
+    sock_tcp_free(sock);
+}
+
 /*
  * Our sock_stream_type
  */
@@ -118,6 +127,7 @@
         .write              = &sock_tcp_write,
         .event_init         = &sock_tcp_event_init,
         .event_enable       = &sock_tcp_event_enable,
+        .release            = &sock_tcp_release,
     },
 };
 
@@ -251,7 +261,27 @@
     return SUCCESS;
 }
 
-void sock_tcp_release (struct sock_tcp *sock)
+void sock_tcp_close (struct sock_tcp *sock)
+{
+    // must be connected
+    assert(sock->fd >= 0);
+
+    // kill any events
+    if (sock->ev_read)
+        event_del(sock->ev_read);
+
+    if (sock->ev_write)
+        event_del(sock->ev_write);
+
+    // close the socket itself
+    // XXX: errors?
+    close(sock->fd);
+
+    // invalidate
+    sock->fd = -1;
+}
+
+void sock_tcp_free (struct sock_tcp *sock)
 {
     // must not be connected
     assert(sock->fd < 0);
@@ -275,7 +305,7 @@
         SET_ERROR_INFO(err_info, sock_stream_error(SOCK_TCP_BASE(sock)));
 
         // cleanup
-        sock_tcp_release(sock);
+        sock_tcp_free(sock);
         
         // return error code
         return err;
--- a/src/sock_tcp.h	Tue Mar 10 01:46:09 2009 +0200
+++ b/src/sock_tcp.h	Tue Mar 10 02:34:11 2009 +0200
@@ -1,12 +1,14 @@
 #ifndef SOCK_TCP_H
 #define SOCK_TCP_H
 
-/*
+/**
+ * @file
+ *
  * TCP implementation of sock_stream interface.
  */
 #include "sock_internal.h"
 
-/*
+/**
  * Contains the base sock_stream struct, and the file descriptor
  */
 struct sock_tcp {
@@ -20,42 +22,54 @@
     struct event *ev_read, *ev_write;
 };
 
+/**
+ * Get a sock_stream pointer from a sock_tcp pointer
+ */
 #define SOCK_TCP_BASE(sock_ptr) (&(sock_ptr)->base)
+
+/**
+ * Get the sock_stream.err pointer from a sock_tcp pointer
+ */
 #define SOCK_TCP_ERR(sock_ptr) SOCK_ERR(SOCK_TCP_BASE(sock_ptr))
 
-/*
+/**
  * Allocate a new blank sock_tcp with a correctly initialized base
  */
 err_t sock_tcp_alloc (struct sock_tcp **sock_ptr);
 
-/*
+/**
  * Initialize a blank sock_tcp with a given already-existing fd
  */
 err_t sock_tcp_init_fd (struct sock_tcp *sock, int fd);
 
-/*
+/**
  * Initialize sock_tcp.ev_* to use the socket's fd with the given callback. The ev's are not activated yet.
  */
 err_t sock_tcp_init_ev (struct sock_tcp *sock, void (*ev_cb) (evutil_socket_t, short, void *), void *arg);
 
-/*
+/**
  * Initialize a blank sock_tcp by connecting
  */
 err_t sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service);
 
-/*
+/**
  * Set the socket's nonblock mode
  */
 err_t sock_tcp_set_nonblock (struct sock_tcp *sock, int nonblock);
 
-/*
+/**
  * event_add the specified ev_* events.
  */
 err_t sock_tcp_add_event (struct sock_tcp *sock, short mask);
 
-/*
- * Release a non-connected sock_tcp
+/**
+ * Close a connected sock_tcp
  */
-void sock_tcp_release (struct sock_tcp *sock);
+void sock_tcp_close (struct sock_tcp *sock);
+
+/**
+ * Free a non-connected sock_tcp
+ */
+void sock_tcp_free (struct sock_tcp *sock);
 
 #endif /* SOCK_TCP_H */