src/sock_fd.c
author Tero Marttila <terom@fixme.fi>
Fri, 24 Apr 2009 23:01:34 +0300
changeset 153 d35e7cb3a489
parent 118 05b8d5150313
permissions -rw-r--r--
implement irc_net reconnect, requires testing
#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);
}