/**
* @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);
}