add sock_stream_release/line_proto_release/irc_conn_release functions, and add proper cleanup to irc_net_create
--- 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 */