#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);
}