|
Packit |
6bd9ab |
/*
|
|
Packit |
6bd9ab |
tio.c - timed io functions
|
|
Packit |
6bd9ab |
This file is part of the nss-pam-ldapd library.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
Copyright (C) 2007-2014 Arthur de Jong
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
This library is free software; you can redistribute it and/or
|
|
Packit |
6bd9ab |
modify it under the terms of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
License as published by the Free Software Foundation; either
|
|
Packit |
6bd9ab |
version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
This library is distributed in the hope that it will be useful,
|
|
Packit |
6bd9ab |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
6bd9ab |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
6bd9ab |
Lesser General Public License for more details.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
License along with this library; if not, write to the Free Software
|
|
Packit |
6bd9ab |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
6bd9ab |
02110-1301 USA
|
|
Packit |
6bd9ab |
*/
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include "config.h"
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#ifdef HAVE_STDINT_H
|
|
Packit |
6bd9ab |
#include <stdint.h>
|
|
Packit |
6bd9ab |
#endif /* HAVE_STDINT_H */
|
|
Packit |
6bd9ab |
#include <stdlib.h>
|
|
Packit |
6bd9ab |
#include <unistd.h>
|
|
Packit |
6bd9ab |
#include <sys/time.h>
|
|
Packit |
6bd9ab |
#include <sys/types.h>
|
|
Packit |
6bd9ab |
#include <sys/socket.h>
|
|
Packit |
6bd9ab |
#include <errno.h>
|
|
Packit |
6bd9ab |
#include <string.h>
|
|
Packit |
6bd9ab |
#include <signal.h>
|
|
Packit |
6bd9ab |
#include <stdio.h>
|
|
Packit |
6bd9ab |
#include <limits.h>
|
|
Packit |
6bd9ab |
#include <poll.h>
|
|
Packit |
6bd9ab |
#include <time.h>
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include "tio.h"
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* for platforms that don't have ETIME use ETIMEDOUT */
|
|
Packit |
6bd9ab |
#ifndef ETIME
|
|
Packit |
6bd9ab |
#define ETIME ETIMEDOUT
|
|
Packit |
6bd9ab |
#endif /* ETIME */
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* structure that holds a buffer
|
|
Packit |
6bd9ab |
the buffer contains the data that is between the application and the
|
|
Packit |
6bd9ab |
file descriptor that is used for efficient transfer
|
|
Packit |
6bd9ab |
the buffer is built up as follows:
|
|
Packit |
6bd9ab |
|.....********......|
|
|
Packit |
6bd9ab |
^start ^size
|
|
Packit |
6bd9ab |
^--len--^ */
|
|
Packit |
6bd9ab |
struct tio_buffer {
|
|
Packit |
6bd9ab |
uint8_t *buffer;
|
|
Packit |
6bd9ab |
size_t size; /* the size of the buffer */
|
|
Packit |
6bd9ab |
size_t maxsize; /* the maximum size of the buffer */
|
|
Packit |
6bd9ab |
size_t start; /* the start of the data (before start is unused) */
|
|
Packit |
6bd9ab |
size_t len; /* size of the data (from the start) */
|
|
Packit |
6bd9ab |
};
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* structure that holds all the state for files */
|
|
Packit |
6bd9ab |
struct tio_fileinfo {
|
|
Packit |
6bd9ab |
int fd;
|
|
Packit |
6bd9ab |
struct tio_buffer readbuffer;
|
|
Packit |
6bd9ab |
struct tio_buffer writebuffer;
|
|
Packit |
6bd9ab |
int readtimeout;
|
|
Packit |
6bd9ab |
int writetimeout;
|
|
Packit |
6bd9ab |
int read_resettable; /* whether the tio_reset() function can be called */
|
|
Packit |
6bd9ab |
#ifdef DEBUG_TIO_STATS
|
|
Packit |
6bd9ab |
/* this is used to collect statistics on the use of the streams
|
|
Packit |
6bd9ab |
and can be used to tune the buffer sizes */
|
|
Packit |
6bd9ab |
size_t byteswritten;
|
|
Packit |
6bd9ab |
size_t bytesread;
|
|
Packit |
6bd9ab |
#endif /* DEBUG_TIO_STATS */
|
|
Packit |
6bd9ab |
};
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* some older versions of Solaris don't provide CLOCK_MONOTONIC but do have
|
|
Packit |
6bd9ab |
a CLOCK_HIGHRES that has the same properties we need */
|
|
Packit |
6bd9ab |
#ifndef CLOCK_MONOTONIC
|
|
Packit |
6bd9ab |
#ifdef CLOCK_HIGHRES
|
|
Packit |
6bd9ab |
#define CLOCK_MONOTONIC CLOCK_HIGHRES
|
|
Packit |
6bd9ab |
#endif /* CLOCK_HIGHRES */
|
|
Packit |
6bd9ab |
#endif /* not CLOCK_MONOTONIC */
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* update the timeout to the value that is remaining before the deadline
|
|
Packit |
6bd9ab |
returns the number of milliseconds before the deadline (or a negative
|
|
Packit |
6bd9ab |
value of the deadline has expired) */
|
|
Packit |
6bd9ab |
static inline int tio_time_remaining(struct timespec *deadline, int timeout)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
struct timespec tv;
|
|
Packit |
6bd9ab |
/* if this is the first call, set the deadline and return the full time */
|
|
Packit |
6bd9ab |
if ((deadline->tv_sec == 0) && (deadline->tv_nsec == 0))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (clock_gettime(CLOCK_MONOTONIC, deadline) == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
deadline->tv_sec += timeout / 1000;
|
|
Packit |
6bd9ab |
deadline->tv_nsec += (timeout % 1000) * 1000000;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return timeout;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* get the current time (fall back to full time on error) */
|
|
Packit |
6bd9ab |
if (clock_gettime(CLOCK_MONOTONIC, &tv))
|
|
Packit |
6bd9ab |
return timeout;
|
|
Packit |
6bd9ab |
/* calculate time remaining in milliseconds */
|
|
Packit |
6bd9ab |
return (deadline->tv_sec - tv.tv_sec) * 1000 +
|
|
Packit |
6bd9ab |
(deadline->tv_nsec - tv.tv_nsec) / 1000000;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* open a new TFILE based on the file descriptor */
|
|
Packit |
6bd9ab |
TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout,
|
|
Packit |
6bd9ab |
size_t initreadsize, size_t maxreadsize,
|
|
Packit |
6bd9ab |
size_t initwritesize, size_t maxwritesize)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
struct tio_fileinfo *fp;
|
|
Packit |
6bd9ab |
fp = (struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
|
|
Packit |
6bd9ab |
if (fp == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
fp->fd = fd;
|
|
Packit |
6bd9ab |
/* initialize read buffer */
|
|
Packit |
6bd9ab |
fp->readbuffer.buffer = (uint8_t *)malloc(initreadsize);
|
|
Packit |
6bd9ab |
if (fp->readbuffer.buffer == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
free(fp);
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
fp->readbuffer.size = initreadsize;
|
|
Packit |
6bd9ab |
fp->readbuffer.maxsize = maxreadsize;
|
|
Packit |
6bd9ab |
fp->readbuffer.start = 0;
|
|
Packit |
6bd9ab |
fp->readbuffer.len = 0;
|
|
Packit |
6bd9ab |
/* initialize write buffer */
|
|
Packit |
6bd9ab |
fp->writebuffer.buffer = (uint8_t *)malloc(initwritesize);
|
|
Packit |
6bd9ab |
if (fp->writebuffer.buffer == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
free(fp->readbuffer.buffer);
|
|
Packit |
6bd9ab |
free(fp);
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
fp->writebuffer.size = initwritesize;
|
|
Packit |
6bd9ab |
fp->writebuffer.maxsize = maxwritesize;
|
|
Packit |
6bd9ab |
fp->writebuffer.start = 0;
|
|
Packit |
6bd9ab |
fp->writebuffer.len = 0;
|
|
Packit |
6bd9ab |
/* initialize other attributes */
|
|
Packit |
6bd9ab |
fp->readtimeout = readtimeout;
|
|
Packit |
6bd9ab |
fp->writetimeout = writetimeout;
|
|
Packit |
6bd9ab |
fp->read_resettable = 0;
|
|
Packit |
6bd9ab |
#ifdef DEBUG_TIO_STATS
|
|
Packit |
6bd9ab |
fp->byteswritten = 0;
|
|
Packit |
6bd9ab |
fp->bytesread = 0;
|
|
Packit |
6bd9ab |
#endif /* DEBUG_TIO_STATS */
|
|
Packit |
6bd9ab |
return fp;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* wait for any activity on the specified file descriptor using
|
|
Packit |
6bd9ab |
the specified deadline */
|
|
Packit |
6bd9ab |
static int tio_wait(int fd, short events, int timeout,
|
|
Packit |
6bd9ab |
struct timespec *deadline)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int t;
|
|
Packit |
6bd9ab |
struct pollfd fds[1];
|
|
Packit |
6bd9ab |
int rv;
|
|
Packit |
6bd9ab |
while (1)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
fds[0].fd = fd;
|
|
Packit |
6bd9ab |
fds[0].events = events;
|
|
Packit |
6bd9ab |
/* figure out the time we need to wait */
|
|
Packit |
6bd9ab |
if ((t = tio_time_remaining(deadline, timeout)) < 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
errno = ETIME;
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* sanitiy check for moving clock */
|
|
Packit |
6bd9ab |
if (t > timeout)
|
|
Packit |
6bd9ab |
t = timeout;
|
|
Packit |
6bd9ab |
/* wait for activity */
|
|
Packit |
6bd9ab |
rv = poll(fds, 1, t);
|
|
Packit |
6bd9ab |
if (rv > 0)
|
|
Packit |
6bd9ab |
return 0; /* we have activity */
|
|
Packit |
6bd9ab |
else if (rv == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* no file descriptors were available within the specified time */
|
|
Packit |
6bd9ab |
errno = ETIME;
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if ((errno != EINTR) && (errno != EAGAIN))
|
|
Packit |
6bd9ab |
/* some error ocurred */
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
/* we just try again on EINTR or EAGAIN */
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* do a read on the file descriptor, returning the data in the buffer
|
|
Packit |
6bd9ab |
if no data was read in the specified time an error is returned */
|
|
Packit |
6bd9ab |
int tio_read(TFILE *fp, void *buf, size_t count)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
struct timespec deadline = {0, 0};
|
|
Packit |
6bd9ab |
int rv;
|
|
Packit |
6bd9ab |
uint8_t *tmp;
|
|
Packit |
6bd9ab |
size_t newsz;
|
|
Packit |
6bd9ab |
size_t len;
|
|
Packit |
6bd9ab |
/* have a more convenient storage type for the buffer */
|
|
Packit |
6bd9ab |
uint8_t *ptr = (uint8_t *)buf;
|
|
Packit |
6bd9ab |
/* loop until we have returned all the needed data */
|
|
Packit |
6bd9ab |
while (1)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* check if we have enough data in the buffer */
|
|
Packit |
6bd9ab |
if (fp->readbuffer.len >= count)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (count > 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (ptr != NULL)
|
|
Packit |
6bd9ab |
memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start, count);
|
|
Packit |
6bd9ab |
/* adjust buffer position */
|
|
Packit |
6bd9ab |
fp->readbuffer.start += count;
|
|
Packit |
6bd9ab |
fp->readbuffer.len -= count;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* empty what we have and continue from there */
|
|
Packit |
6bd9ab |
if (fp->readbuffer.len > 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (ptr != NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start,
|
|
Packit |
6bd9ab |
fp->readbuffer.len);
|
|
Packit |
6bd9ab |
ptr += fp->readbuffer.len;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
count -= fp->readbuffer.len;
|
|
Packit |
6bd9ab |
fp->readbuffer.start += fp->readbuffer.len;
|
|
Packit |
6bd9ab |
fp->readbuffer.len = 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* after this point until the read fp->readbuffer.len is 0 */
|
|
Packit |
6bd9ab |
if (!fp->read_resettable)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* the stream is not resettable, re-use the buffer */
|
|
Packit |
6bd9ab |
fp->readbuffer.start = 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* buffer is running empty, try to grow buffer */
|
|
Packit |
6bd9ab |
if (fp->readbuffer.size < fp->readbuffer.maxsize)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
newsz = fp->readbuffer.size * 2;
|
|
Packit |
6bd9ab |
if (newsz > fp->readbuffer.maxsize)
|
|
Packit |
6bd9ab |
newsz = fp->readbuffer.maxsize;
|
|
Packit |
6bd9ab |
tmp = realloc(fp->readbuffer.buffer, newsz);
|
|
Packit |
6bd9ab |
if (tmp != NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
fp->readbuffer.buffer = tmp;
|
|
Packit |
6bd9ab |
fp->readbuffer.size = newsz;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* if buffer still does not contain enough room, clear resettable */
|
|
Packit |
6bd9ab |
if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
fp->readbuffer.start = 0;
|
|
Packit |
6bd9ab |
fp->read_resettable = 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* wait until we have input */
|
|
Packit |
6bd9ab |
if (tio_wait(fp->fd, POLLIN, fp->readtimeout, &deadline))
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
/* read the input in the buffer */
|
|
Packit |
6bd9ab |
len = fp->readbuffer.size - fp->readbuffer.start;
|
|
Packit |
6bd9ab |
#ifdef SSIZE_MAX
|
|
Packit |
6bd9ab |
if (len > SSIZE_MAX)
|
|
Packit |
6bd9ab |
len = SSIZE_MAX;
|
|
Packit |
6bd9ab |
#endif /* SSIZE_MAX */
|
|
Packit |
6bd9ab |
rv = read(fp->fd, fp->readbuffer.buffer + fp->readbuffer.start, len);
|
|
Packit |
6bd9ab |
/* check for errors */
|
|
Packit |
6bd9ab |
if (rv == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
errno = ECONNRESET;
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
|
|
Packit |
6bd9ab |
return -1; /* something went wrong with the read */
|
|
Packit |
6bd9ab |
else if (rv > 0)
|
|
Packit |
6bd9ab |
fp->readbuffer.len = rv; /* skip the read part in the buffer */
|
|
Packit |
6bd9ab |
#ifdef DEBUG_TIO_STATS
|
|
Packit |
6bd9ab |
fp->bytesread += rv;
|
|
Packit |
6bd9ab |
#endif /* DEBUG_TIO_STATS */
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* Read and discard the specified number of bytes from the stream. */
|
|
Packit |
6bd9ab |
int tio_skip(TFILE *fp, size_t count)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
return tio_read(fp, NULL, count);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* Read all available data from the stream and empty the read buffer. */
|
|
Packit |
6bd9ab |
int tio_skipall(TFILE *fp, int timeout)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
struct timespec deadline = {0, 0};
|
|
Packit |
6bd9ab |
int rv;
|
|
Packit |
6bd9ab |
size_t len;
|
|
Packit |
6bd9ab |
/* clear the read buffer */
|
|
Packit |
6bd9ab |
fp->readbuffer.start = 0;
|
|
Packit |
6bd9ab |
fp->readbuffer.len = 0;
|
|
Packit |
6bd9ab |
fp->read_resettable = 0;
|
|
Packit |
6bd9ab |
/* read until we can't read no more */
|
|
Packit |
6bd9ab |
len = fp->readbuffer.size;
|
|
Packit |
6bd9ab |
#ifdef SSIZE_MAX
|
|
Packit |
6bd9ab |
if (len > SSIZE_MAX)
|
|
Packit |
6bd9ab |
len = SSIZE_MAX;
|
|
Packit |
6bd9ab |
#endif /* SSIZE_MAX */
|
|
Packit |
6bd9ab |
while (1)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* wait until we have input */
|
|
Packit |
6bd9ab |
if (tio_wait(fp->fd, POLLIN, timeout, &deadline))
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
/* read data from the stream */
|
|
Packit |
6bd9ab |
rv = read(fp->fd, fp->readbuffer.buffer, len);
|
|
Packit |
6bd9ab |
if (rv == 0)
|
|
Packit |
6bd9ab |
return 0; /* end-of-file */
|
|
Packit |
6bd9ab |
if ((rv < 0) && (errno == EWOULDBLOCK))
|
|
Packit |
6bd9ab |
return 0; /* we've ready everything we can without blocking */
|
|
Packit |
6bd9ab |
if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
|
|
Packit |
6bd9ab |
return -1; /* something went wrong with the read */
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* the caller has assured us that we can write to the file descriptor
|
|
Packit |
6bd9ab |
and we give it a shot */
|
|
Packit |
6bd9ab |
static int tio_writebuf(TFILE *fp)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int rv;
|
|
Packit |
6bd9ab |
/* write the buffer */
|
|
Packit |
6bd9ab |
#ifdef MSG_NOSIGNAL
|
|
Packit |
6bd9ab |
rv = send(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
|
|
Packit |
6bd9ab |
fp->writebuffer.len, MSG_NOSIGNAL);
|
|
Packit |
6bd9ab |
#else /* not MSG_NOSIGNAL */
|
|
Packit |
6bd9ab |
/* on platforms that cannot use send() with masked signals, we change the
|
|
Packit |
6bd9ab |
signal mask and change it back after the write (note that there is a
|
|
Packit |
6bd9ab |
race condition here) */
|
|
Packit |
6bd9ab |
struct sigaction act, oldact;
|
|
Packit |
6bd9ab |
/* set up sigaction */
|
|
Packit |
6bd9ab |
memset(&act, 0, sizeof(struct sigaction));
|
|
Packit |
6bd9ab |
act.sa_sigaction = NULL;
|
|
Packit |
6bd9ab |
act.sa_handler = SIG_IGN;
|
|
Packit |
6bd9ab |
sigemptyset(&act.sa_mask);
|
|
Packit |
6bd9ab |
act.sa_flags = SA_RESTART;
|
|
Packit |
6bd9ab |
/* ignore SIGPIPE */
|
|
Packit |
6bd9ab |
if (sigaction(SIGPIPE, &act, &oldact) != 0)
|
|
Packit |
6bd9ab |
return -1; /* error setting signal handler */
|
|
Packit |
6bd9ab |
/* write the buffer */
|
|
Packit |
6bd9ab |
rv = write(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
|
|
Packit |
6bd9ab |
fp->writebuffer.len);
|
|
Packit |
6bd9ab |
/* restore the old handler for SIGPIPE */
|
|
Packit |
6bd9ab |
if (sigaction(SIGPIPE, &oldact, NULL) != 0)
|
|
Packit |
6bd9ab |
return -1; /* error restoring signal handler */
|
|
Packit |
6bd9ab |
#endif
|
|
Packit |
6bd9ab |
/* check for errors */
|
|
Packit |
6bd9ab |
if ((rv == 0) || ((rv < 0) && (errno != EINTR) && (errno != EAGAIN)))
|
|
Packit |
6bd9ab |
return -1; /* something went wrong with the write */
|
|
Packit |
6bd9ab |
/* skip the written part in the buffer */
|
|
Packit |
6bd9ab |
if (rv > 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
fp->writebuffer.start += rv;
|
|
Packit |
6bd9ab |
fp->writebuffer.len -= rv;
|
|
Packit |
6bd9ab |
#ifdef DEBUG_TIO_STATS
|
|
Packit |
6bd9ab |
fp->byteswritten += rv;
|
|
Packit |
6bd9ab |
#endif /* DEBUG_TIO_STATS */
|
|
Packit |
6bd9ab |
/* reset start if len is 0 */
|
|
Packit |
6bd9ab |
if (fp->writebuffer.len == 0)
|
|
Packit |
6bd9ab |
fp->writebuffer.start = 0;
|
|
Packit |
6bd9ab |
/* move contents of the buffer to the front if it will save enough room */
|
|
Packit |
6bd9ab |
if (fp->writebuffer.start >= (fp->writebuffer.size / 4))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
memmove(fp->writebuffer.buffer,
|
|
Packit |
6bd9ab |
fp->writebuffer.buffer + fp->writebuffer.start,
|
|
Packit |
6bd9ab |
fp->writebuffer.len);
|
|
Packit |
6bd9ab |
fp->writebuffer.start = 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* write all the data in the buffer to the stream */
|
|
Packit |
6bd9ab |
int tio_flush(TFILE *fp)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
struct timespec deadline = {0, 0};
|
|
Packit |
6bd9ab |
/* loop until we have written our buffer */
|
|
Packit |
6bd9ab |
while (fp->writebuffer.len > 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* wait until we can write */
|
|
Packit |
6bd9ab |
if (tio_wait(fp->fd, POLLOUT, fp->writetimeout, &deadline))
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
/* write one block */
|
|
Packit |
6bd9ab |
if (tio_writebuf(fp))
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* try a single write of data in the buffer if the file descriptor
|
|
Packit |
6bd9ab |
will accept data */
|
|
Packit |
6bd9ab |
static int tio_flush_nonblock(TFILE *fp)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
struct pollfd fds[1];
|
|
Packit |
6bd9ab |
int rv;
|
|
Packit |
6bd9ab |
/* see if we can write without blocking */
|
|
Packit |
6bd9ab |
fds[0].fd = fp->fd;
|
|
Packit |
6bd9ab |
fds[0].events = POLLOUT;
|
|
Packit |
6bd9ab |
rv = poll(fds, 1, 0);
|
|
Packit |
6bd9ab |
/* check if any file descriptors were ready (timeout) or we were
|
|
Packit |
6bd9ab |
interrupted */
|
|
Packit |
6bd9ab |
if ((rv == 0) || ((rv < 0) && ((errno == EINTR) || (errno == EAGAIN))))
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
/* any other errors? */
|
|
Packit |
6bd9ab |
if (rv < 0)
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
/* so file descriptor will accept writes */
|
|
Packit |
6bd9ab |
return tio_writebuf(fp);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
int tio_write(TFILE *fp, const void *buf, size_t count)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
size_t fr;
|
|
Packit |
6bd9ab |
uint8_t *tmp;
|
|
Packit |
6bd9ab |
size_t newsz;
|
|
Packit |
6bd9ab |
const uint8_t *ptr = (const uint8_t *)buf;
|
|
Packit |
6bd9ab |
/* keep filling the buffer until we have bufferred everything */
|
|
Packit |
6bd9ab |
while (count > 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* figure out free size in buffer */
|
|
Packit |
6bd9ab |
fr = fp->writebuffer.size - (fp->writebuffer.start + fp->writebuffer.len);
|
|
Packit |
6bd9ab |
if (count <= fr)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* the data fits in the buffer */
|
|
Packit |
6bd9ab |
memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
|
|
Packit |
6bd9ab |
fp->writebuffer.len, ptr, count);
|
|
Packit |
6bd9ab |
fp->writebuffer.len += count;
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if (fr > 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* fill the buffer with data that will fit */
|
|
Packit |
6bd9ab |
memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
|
|
Packit |
6bd9ab |
fp->writebuffer.len, ptr, fr);
|
|
Packit |
6bd9ab |
fp->writebuffer.len += fr;
|
|
Packit |
6bd9ab |
ptr += fr;
|
|
Packit |
6bd9ab |
count -= fr;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* try to flush some of the data that is in the buffer */
|
|
Packit |
6bd9ab |
if (tio_flush_nonblock(fp))
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
/* if we have room now, try again */
|
|
Packit |
6bd9ab |
if (fp->writebuffer.size > (fp->writebuffer.start + fp->writebuffer.len))
|
|
Packit |
6bd9ab |
continue;
|
|
Packit |
6bd9ab |
/* try to grow the buffer */
|
|
Packit |
6bd9ab |
if (fp->writebuffer.size < fp->writebuffer.maxsize)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
newsz = fp->writebuffer.size * 2;
|
|
Packit |
6bd9ab |
if (newsz > fp->writebuffer.maxsize)
|
|
Packit |
6bd9ab |
newsz = fp->writebuffer.maxsize;
|
|
Packit |
6bd9ab |
tmp = realloc(fp->writebuffer.buffer, newsz);
|
|
Packit |
6bd9ab |
if (tmp != NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
fp->writebuffer.buffer = tmp;
|
|
Packit |
6bd9ab |
fp->writebuffer.size = newsz;
|
|
Packit |
6bd9ab |
continue; /* try again */
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* write the buffer to the stream */
|
|
Packit |
6bd9ab |
if (tio_flush(fp))
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
int tio_close(TFILE *fp)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int retv;
|
|
Packit |
6bd9ab |
/* write any buffered data */
|
|
Packit |
6bd9ab |
retv = tio_flush(fp);
|
|
Packit |
6bd9ab |
#ifdef DEBUG_TIO_STATS
|
|
Packit |
6bd9ab |
/* dump statistics to stderr */
|
|
Packit |
6bd9ab |
fprintf(stderr, "DEBUG_TIO_STATS READ=%lu WRITTEN=%lu\n",
|
|
Packit |
6bd9ab |
(unsigned long)fp->bytesread, (unsigned long)fp->byteswritten);
|
|
Packit |
6bd9ab |
#endif /* DEBUG_TIO_STATS */
|
|
Packit |
6bd9ab |
/* close file descriptor */
|
|
Packit |
6bd9ab |
if (close(fp->fd))
|
|
Packit |
6bd9ab |
retv = -1;
|
|
Packit |
6bd9ab |
/* free any allocated buffers */
|
|
Packit |
6bd9ab |
memset(fp->readbuffer.buffer, 0, fp->readbuffer.size);
|
|
Packit |
6bd9ab |
memset(fp->writebuffer.buffer, 0, fp->writebuffer.size);
|
|
Packit |
6bd9ab |
free(fp->readbuffer.buffer);
|
|
Packit |
6bd9ab |
free(fp->writebuffer.buffer);
|
|
Packit |
6bd9ab |
/* free the tio struct itself */
|
|
Packit |
6bd9ab |
free(fp);
|
|
Packit |
6bd9ab |
/* return the result of the earlier operations */
|
|
Packit |
6bd9ab |
return retv;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
void tio_mark(TFILE *fp)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* move any data in the buffer to the start of the buffer */
|
|
Packit |
6bd9ab |
if ((fp->readbuffer.start > 0) && (fp->readbuffer.len > 0))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
memmove(fp->readbuffer.buffer,
|
|
Packit |
6bd9ab |
fp->readbuffer.buffer + fp->readbuffer.start, fp->readbuffer.len);
|
|
Packit |
6bd9ab |
fp->readbuffer.start = 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* mark the stream as resettable */
|
|
Packit |
6bd9ab |
fp->read_resettable = 1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
int tio_reset(TFILE *fp)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* check if the stream is (still) resettable */
|
|
Packit |
6bd9ab |
if (!fp->read_resettable)
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
/* reset the buffer */
|
|
Packit |
6bd9ab |
fp->readbuffer.len += fp->readbuffer.start;
|
|
Packit |
6bd9ab |
fp->readbuffer.start = 0;
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|