src/sock_fifo.c
author Tero Marttila <terom@fixme.fi>
Sat, 11 Apr 2009 06:03:08 +0300
changeset 129 361740b82fe5
parent 118 05b8d5150313
child 139 55b9dcc2b73a
permissions -rw-r--r--
implement str_format with tests
/**
 * @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);
}