--- a/src/CMakeLists.txt Tue Apr 07 18:09:16 2009 +0300
+++ b/src/CMakeLists.txt Tue Apr 07 19:41:48 2009 +0300
@@ -10,7 +10,7 @@
# define our source code modules
set (CORE_SOURCES error.c log.c)
-set (SOCK_SOURCES sock.c sock_fd.c sock_tcp.c sock_gnutls.c sock_test.c line_proto.c)
+set (SOCK_SOURCES sock.c sock_fd.c sock_tcp.c sock_gnutls.c sock_test.c sock_fifo.c line_proto.c)
set (IRC_SOURCES irc_line.c irc_conn.c irc_net.c irc_chan.c chain.c irc_cmd.c irc_proto.c irc_client.c irc_user.c irc_queue.c)
set (LUA_SOURCES nexus_lua.c lua_objs.c lua_config.c lua_irc.c)
set (CONSOLE_SOURCES console.c lua_console.c)
--- a/src/log.c Tue Apr 07 18:09:16 2009 +0300
+++ b/src/log.c Tue Apr 07 19:41:48 2009 +0300
@@ -3,6 +3,7 @@
#include <stdio.h>
#include <stdarg.h>
+#include <string.h>
/**
* The global log level
@@ -106,3 +107,24 @@
// err_code and newline
printf(": %s\n", error_msg(err));
}
+
+void _log_perr (enum log_level level, const char *func, const char *format, ...)
+{
+ va_list vargs;
+
+ // filter out?
+ if (level < _log_level)
+ return;
+
+ // header
+ _log_header(level, func);
+
+ // formatted output
+ va_start(vargs, format);
+ vprintf(format, vargs);
+ va_end(vargs);
+
+ // err_code and newline
+ printf(": %s\n", strerror(errno));
+}
+
--- a/src/log.h Tue Apr 07 18:09:16 2009 +0300
+++ b/src/log.h Tue Apr 07 19:41:48 2009 +0300
@@ -59,22 +59,33 @@
void _log_err_info (enum log_level level, struct error_info *err, const char *func, const char *format, ...)
__attribute__ ((format (printf, 4, 5)));
+/**
+ * Log using errno.
+ */
+void _log_perr (enum log_level level, const char *func, const char *format, ...)
+ __attribute__ ((format (printf, 3, 4)));
+
#define log_err(err, ...) _log_err(LOG_ERROR, err, __func__, __VA_ARGS__)
#define log_err_info(err_info, ...) _log_err_info(LOG_ERROR, err_info, __func__, __VA_ARGS__)
-/*
+/**
* log_fatal + exit failure
*/
#define FATAL(...) do { log_fatal(__VA_ARGS__); exit(EXIT_FAILURE); } while (0)
-/*
+/**
* log_err + exit failure
*/
#define FATAL_ERR(err_code, ...) do { _log_err(LOG_FATAL, err_code, __func__, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)
-/*
+/**
* log_err_info + exit failure
*/
#define FATAL_ERROR(err_info, ...) do { _log_err_info(LOG_FATAL, err_info, __func__, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)
+/**
+ * log_perr + exit failure
+ */
+#define FATAL_PERROR(...) do { _log_perr(LOG_FATAL, __func__, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)
+
#endif /* LOG_H */
--- a/src/sock.c Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock.c Tue Apr 07 19:41:48 2009 +0300
@@ -31,11 +31,15 @@
int sock_stream_read (struct sock_stream *sock, void *buf, size_t len)
{
- err_t err;
+ struct error_info *err = SOCK_ERR(sock);
+
+ // XXX: not readable
+ if (!sock->type->methods.read)
+ return -1;
// proxy off to method handler
- if ((err = sock->type->methods.read(sock, buf, &len)))
- return -err;
+ if (sock->type->methods.read(sock, buf, &len, err))
+ return -ERROR_CODE(err);
// return updated bytes-read len
return len;
@@ -43,11 +47,15 @@
int sock_stream_write (struct sock_stream *sock, const void *buf, size_t len)
{
- err_t err;
+ struct error_info *err = SOCK_ERR(sock);
+
+ // XXX: not writeable
+ if (!sock->type->methods.write)
+ return -1;
// proxy off to method handler
- if ((err = sock->type->methods.write(sock, buf, &len)))
- return -err;
+ if (sock->type->methods.write(sock, buf, &len, err))
+ return -ERROR_CODE(err);
// return updated bytes-written len
return len;
--- a/src/sock.h Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock.h Tue Apr 07 19:41:48 2009 +0300
@@ -107,7 +107,7 @@
/**
* A read-only "socket" based on a FIFO, this provides nonblocking read operations by re-opening the FIFO on EOF.
*/
-err_t fifo_read_open (struct sock_stream **stream_ptr, const char *path, struct error_info *err);
+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
--- a/src/sock_fd.c Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock_fd.c Tue Apr 07 19:41:48 2009 +0300
@@ -14,7 +14,7 @@
sock_stream_invoke_callbacks(SOCK_FD_BASE(sock), what);
}
-err_t sock_fd_read (struct sock_stream *base_sock, void *buf, size_t *len)
+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;
@@ -22,11 +22,11 @@
// read(), and detect non-EAGAIN or EOF
if ((ret = read(sock->fd, buf, *len)) < 0 && errno != EAGAIN)
// unexpected error
- RETURN_SET_ERROR_ERRNO(SOCK_FD_ERR(sock), ERR_READ);
+ RETURN_SET_ERROR_ERRNO(err, ERR_READ);
else if (ret == 0)
// EOF
- return SET_ERROR(SOCK_FD_ERR(sock), ERR_READ_EOF);
+ return SET_ERROR(err, ERR_READ_EOF);
if (ret < 0) {
@@ -42,7 +42,7 @@
return SUCCESS;
}
-err_t sock_fd_write (struct sock_stream *base_sock, const void *buf, size_t *len)
+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;
@@ -50,11 +50,11 @@
// write(), and detect non-EAGAIN or EOF
if ((ret = write(sock->fd, buf, *len)) < 0 && errno != EAGAIN)
// unexpected error
- RETURN_SET_ERROR_ERRNO(SOCK_FD_ERR(sock), ERR_WRITE);
+ RETURN_SET_ERROR_ERRNO(err, ERR_WRITE);
else if (ret == 0)
// EOF
- return SET_ERROR(SOCK_FD_ERR(sock), ERR_WRITE_EOF);
+ return SET_ERROR(err, ERR_WRITE_EOF);
if (ret < 0) {
@@ -120,6 +120,10 @@
// 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)
@@ -145,7 +149,7 @@
return SUCCESS;
}
-void sock_fd_deinit_ev (struct sock_fd *sock)
+static void sock_fd_free_ev (struct sock_fd *sock)
{
if (sock->ev_read) {
event_free(sock->ev_read);
@@ -160,6 +164,34 @@
}
}
+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);
--- a/src/sock_fd.h Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock_fd.h Tue Apr 07 19:41:48 2009 +0300
@@ -19,6 +19,10 @@
/** 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;
@@ -44,12 +48,12 @@
/**
* sock_stream_methods::read implementation.
*/
-err_t sock_fd_read (struct sock_stream *base_sock, void *buf, size_t *len);
+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);
+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.
@@ -92,6 +96,12 @@
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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sock_fifo.c Tue Apr 07 19:41:48 2009 +0300
@@ -0,0 +1,153 @@
+/**
+ * @file
+ *
+ * A read-only sock_stream implementation for linux fifo(7).
+ */
+#include "sock_fd.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+struct fifo {
+ /** The base fd operations */
+ struct sock_fd base_fd;
+
+ /** The path to the fifo */
+ char *path;
+};
+
+/**
+ * Get a sock_fd pointer from a sock_fifo pointer
+ */
+#define FIFO_FD(sock_ptr) (&(sock_ptr)->base_fd)
+
+/**
+ * Get a sock_base pointer from a sock_fifo pointer
+ */
+#define FIFO_BASE(sock_ptr) SOCK_FD_BASE(FIFO_FD(sock_ptr))
+
+/**
+ * Get the sock_stream.err pointer from a sock_fifo pointer
+ */
+#define FIFO_ERR(sock_ptr) SOCK_ERR(FIFO_BASE(sock_ptr))
+
+
+
+/**
+ * (re)open the fifo, closing it if already open, and keeping any event callbacks registered.
+ */
+static err_t fifo_open (struct fifo *fifo, struct error_info *err)
+{
+ int fd;
+
+ // open(2) the path in non-blocking mode
+ // XXX: hardoded read-only
+ if ((fd = open(fifo->path, O_RDONLY | O_NONBLOCK)) < 0)
+ RETURN_SET_ERROR_ERRNO(err, ERR_OPEN);
+
+ // set the new fd
+ if ((ERROR_CODE(err) = sock_fd_set(FIFO_FD(fifo), fd)))
+ return ERROR_CODE(err);
+
+ // ok
+ return SUCCESS;
+}
+
+/**
+ * Destroy the fifo, releasing all resources
+ */
+static void fifo_destroy (struct fifo *fifo)
+{
+ // close if open
+ if (FIFO_FD(fifo)->fd >= 0)
+ sock_fd_close(FIFO_FD(fifo));
+
+ // release the path
+ free(fifo->path);
+ free(fifo);
+}
+
+/**
+ * sock_stream_methods::read implementation.
+ *
+ * Try and do a normal sock_fd_read call, but re-open with EAGAIN on EOF
+ */
+err_t fifo_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err)
+{
+ struct fifo *fifo = SOCK_FROM_BASE(base_sock, struct fifo);
+
+ // passthru ERR_READ_EOF unless it's READ_EOF
+ if (sock_fd_read(base_sock, buf, len, err) != ERR_READ_EOF)
+ return ERROR_CODE(err);
+
+ // re-open it
+ // XXX: re-add events?
+ if (fifo_open(fifo, err))
+ goto error;
+
+ // ok, act as if it was EAGAIN
+ *len = 0;
+
+ return SUCCESS;
+
+error:
+ return ERROR_CODE(err);
+}
+
+/**
+ * sock_stream_methods::release implementation
+ */
+static void fifo_release (struct sock_stream *base_sock)
+{
+ struct fifo *fifo = SOCK_FROM_BASE(base_sock, struct fifo);
+
+ fifo_destroy(fifo);
+}
+
+/*
+ * Our sock_stream_type
+ */
+static struct sock_stream_type fifo_stream_type = {
+ .methods = {
+ .read = &fifo_read,
+ .write = NULL,
+ .event_init = &sock_fd_event_init,
+ .event_enable = &sock_fd_event_enable,
+ .release = &fifo_release,
+ },
+};
+
+err_t fifo_open_read (struct sock_stream **stream_ptr, const char *path, struct error_info *err)
+{
+ struct fifo *fifo;
+
+ // alloc
+ if ((fifo = calloc(1, sizeof(*fifo))) == NULL)
+ return SET_ERROR(err, ERR_CALLOC);
+
+ // copy the path
+ if ((fifo->path = strdup(path)) == NULL)
+ return SET_ERROR(err, ERR_STRDUP);
+
+ // init
+ sock_stream_init(FIFO_BASE(fifo), &fifo_stream_type);
+ sock_fd_init(FIFO_FD(fifo), -1);
+
+ // open the fifo
+ if (fifo_open(fifo, err))
+ goto error;
+
+ // ok
+ *stream_ptr = FIFO_BASE(fifo);
+
+ return SUCCESS;
+
+error:
+ // cleanup
+ fifo_destroy(fifo);
+
+ return ERROR_CODE(err);
+}
--- a/src/sock_gnutls.c Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock_gnutls.c Tue Apr 07 19:41:48 2009 +0300
@@ -4,10 +4,9 @@
#include <stdlib.h>
#include <err.h>
-static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t *len)
+static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err)
{
struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
- struct error_info *err = SOCK_GNUTLS_ERR(sock);
int ret;
// read gnutls record
@@ -34,10 +33,9 @@
return SUCCESS;
}
-static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t *len)
+static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t *len, struct error_info *err)
{
struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
- struct error_info *err = SOCK_GNUTLS_ERR(sock);
int ret;
// read gnutls record
--- a/src/sock_internal.h Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock_internal.h Tue Apr 07 19:41:48 2009 +0300
@@ -27,10 +27,10 @@
*/
struct sock_stream_methods {
/** As read(2) */
- err_t (*read) (struct sock_stream *sock, void *buf, size_t *len);
+ err_t (*read) (struct sock_stream *sock, void *buf, size_t *len, struct error_info *err);
/** As write(2) */
- err_t (*write) (struct sock_stream *sock, const void *buf, size_t *len);
+ err_t (*write) (struct sock_stream *sock, const void *buf, size_t *len, struct error_info *err);
/** Initialize events. cb_info/cb_arg are already updated */
err_t (*event_init) (struct sock_stream *sock);
--- a/src/sock_test.c Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock_test.c Tue Apr 07 19:41:48 2009 +0300
@@ -43,7 +43,7 @@
return SUCCESS;
}
-static err_t sock_test_read (struct sock_stream *base_sock, void *buf_ptr, size_t *len)
+static err_t sock_test_read (struct sock_stream *base_sock, void *buf_ptr, size_t *len, struct error_info *err)
{
struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test);
struct io_buf *buf = &sock->recv_buf;
@@ -58,7 +58,7 @@
} else {
// EOF!
- return SET_ERROR(SOCK_TEST_ERR(sock), ERR_READ_EOF);
+ return SET_ERROR(err, ERR_READ_EOF);
}
}
@@ -92,13 +92,14 @@
return SUCCESS;
}
-static err_t sock_test_write (struct sock_stream *base_sock, const void *buf_ptr, size_t *len)
+static err_t sock_test_write (struct sock_stream *base_sock, const void *buf_ptr, size_t *len, struct error_info *err)
{
struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test);
struct io_buf *buf = &sock->send_buf;
-
+
// ensure there's room
- assert(sock_test_grow_buf(buf) == SUCCESS);
+ if ((ERROR_CODE(err) = sock_test_grow_buf(buf)))
+ goto error;
// the next buffer
struct io_vec *vec = buf->write_vec;
@@ -113,6 +114,9 @@
// ok
return SUCCESS;
+
+error:
+ return ERROR_CODE(err);
}
static err_t sock_test_event_init (struct sock_stream *base_sock)
--- a/src/test.c Tue Apr 07 18:09:16 2009 +0300
+++ b/src/test.c Tue Apr 07 19:41:48 2009 +0300
@@ -1079,36 +1079,154 @@
irc_net_destroy(net);
}
+// XXX: needs to be split off into its own test_fifo.c
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+struct test_fifo_ctx {
+ /** Path to the fifo */
+ const char *path;
+
+ /** The write end */
+ int fd;
+
+ /** callback invoked? */
+ bool on_read;
+
+ /** Still running? */
+ bool run;
+};
+
+/**
+ * Open the FIFO and write the test string to it
+ */
+static void test_fifo_open_write (struct test_fifo_ctx *ctx)
+{
+ // ...raw, for writing
+ if ((ctx->fd = open(ctx->path, O_WRONLY)) < 0)
+ FATAL_PERROR("open");
+
+ // write something into it
+ if (write(ctx->fd, "test", 4) != 4)
+ FATAL_PERROR("write");
+
+}
+
+static void test_fifo_close (struct test_fifo_ctx *ctx)
+{
+ close(ctx->fd);
+ ctx->fd = -1;
+}
+
+static void test_fifo_on_read (struct sock_stream *fifo, void *arg)
+{
+ int ret;
+ char buf[16];
+ struct test_fifo_ctx *ctx = arg;
+
+ // read it back out
+ log_info("test fifo_read");
+ if ((ret = sock_stream_read(fifo, buf, 16)) < 0)
+ assert_success(-ret);
+
+ assert(ret == 4);
+ assert_strncmp(buf, "test", 4);
+
+ if (ctx->on_read) {
+ test_fifo_close(ctx);
+ ctx->run = false;
+ return;
+ }
+
+ // re-open the fifo
+ log_info("test fifo-re-open");
+ test_fifo_close(ctx);
+ test_fifo_open_write(ctx);
+
+ assert_success(sock_stream_event_enable(fifo, EV_READ));
+
+ ctx->on_read = true;
+}
+
+static struct sock_stream_callbacks test_fifo_callbacks = {
+ .on_read = test_fifo_on_read,
+};
+
+void test_fifo (void)
+{
+ struct sock_stream *fifo;
+ struct error_info err;
+ struct test_fifo_ctx _ctx, *ctx = &_ctx; memset(ctx, 0, sizeof(*ctx));
+
+ // XXX: requires that this be run in a suitable CWD
+ ctx->path = "test.fifo";
+
+ // create the fifo
+ if ((mkfifo(ctx->path, 0600) < 0) && (errno != EEXIST))
+ FATAL_PERROR("mkfifo");
+
+ // open it
+ log_info("test fifo_open_read");
+ assert_success(fifo_open_read(&fifo, ctx->path, &err));
+ assert_success(sock_stream_event_init(fifo, &test_fifo_callbacks, ctx));
+ assert_success(sock_stream_event_enable(fifo, EV_READ));
+
+ // put some data into it
+ test_fifo_open_write(ctx);
+
+ // run the event loop
+ log_debug("running the event loop...");
+ ctx->run = true;
+
+ while (ctx->run)
+ assert(event_base_loop(_test_ctx.ev_base, EVLOOP_ONCE) == 0);
+
+ // check
+ assert(ctx->fd < 0);
+
+ // cleanup
+ sock_stream_release(fifo);
+}
+
/**
* Test definition
*/
-static struct test {
+struct test {
/** Test name */
const char *name;
/** Test func */
void (*func) (void);
+
+ bool optional;
+};
-} _tests[] = {
- { "dump_str", &test_dump_str },
- { "sock_test", &test_sock_test },
- { "line_proto", &test_line_proto },
- { "irc_queue", &test_irc_queue },
+#define DEF_TEST(name) { #name, &test_ ## name, false }
+#define DEF_TEST_OPTIONAL(name) { #name, &test_ ## name, true }
+#define DEF_TEST_END { NULL, NULL, false }
+
+static struct test _tests[] = {
+ DEF_TEST( dump_str ),
+ DEF_TEST( sock_test ),
+ DEF_TEST( line_proto ),
+ DEF_TEST( irc_queue ),
// XXX: irc_line_parse_invalid_prefix
- { "irc_conn", &test_irc_conn },
- { "irc_conn_self_nick", &test_irc_conn_self_nick },
- { "irc_net", &test_irc_net },
- { "irc_chan_add_offline", &test_irc_chan_add_offline },
- { "irc_chan_namreply", &test_irc_chan_namreply },
- { "irc_chan_user_join", &test_irc_chan_user_join },
- { "irc_chan_user_part", &test_irc_chan_user_part },
- { "irc_chan_user_kick", &test_irc_chan_user_kick },
- { "irc_chan_self_kick", &test_irc_chan_self_kick },
- { "irc_chan_user_nick", &test_irc_chan_user_nick },
- { "irc_chan_user_quit", &test_irc_chan_user_quit },
- { "irc_chan_CTCP_ACTION", &test_irc_chan_CTCP_ACTION },
- { "irc_chan_privmsg", &test_irc_chan_privmsg },
- { NULL, NULL }
+ DEF_TEST( irc_conn ),
+ DEF_TEST( irc_conn_self_nick ),
+ DEF_TEST( irc_net ),
+ DEF_TEST( irc_chan_add_offline ),
+ DEF_TEST( irc_chan_namreply ),
+ DEF_TEST( irc_chan_user_join ),
+ DEF_TEST( irc_chan_user_part ),
+ DEF_TEST( irc_chan_user_kick ),
+ DEF_TEST( irc_chan_self_kick ),
+ DEF_TEST( irc_chan_user_nick ),
+ DEF_TEST( irc_chan_user_quit ),
+ DEF_TEST( irc_chan_CTCP_ACTION ),
+ DEF_TEST( irc_chan_privmsg ),
+ DEF_TEST_OPTIONAL( fifo ),
+ DEF_TEST_END
};
/**
@@ -1208,7 +1326,7 @@
// run tests
for (test = _tests; test->name; test++) {
- if (filter && strcmp(test->name, filter))
+ if ((filter && strcmp(test->name, filter)) || (!filter && test->optional))
continue;
log_info("Running test: %s", test->name);