fence-virt/common/fdops.c
Lon Hohberger 55357fcd85 initial checkin
Signed-off-by: Lon Hohberger <lhh@redhat.com>
2009-07-28 10:46:53 -04:00

194 lines
4.5 KiB
C

/*
Copyright Red Hat, Inc. 2002-2003
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
MA 02139, USA.
*/
/** @file
* Wrapper functions around read/write/select to retry in the event
* of interrupts.
*/
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
/**
* This is a wrapper around select which will retry in the case we receive
* EINTR. This is necessary for _read_retry, since it wouldn't make sense
* to have _read_retry terminate if and only if two EINTRs were received
* in a row - one during the read() call, one during the select call...
*
* See select(2) for description of parameters.
*/
int
_select_retry(int fdmax, fd_set * rfds, fd_set * wfds, fd_set * xfds,
struct timeval *timeout)
{
int rv;
while (1) {
rv = select(fdmax, rfds, wfds, xfds, timeout);
if ((rv == -1) && (errno == EINTR))
/* return on EBADF/EINVAL/ENOMEM; continue on EINTR */
continue;
return rv;
}
}
/**
* Retries a write in the event of a non-blocked interrupt signal.
*
* @param fd File descriptor to which we are writing.
* @param buf Data buffer to send.
* @param count Number of bytes in buf to send.
* @param timeout (struct timeval) telling us how long we should retry.
* @return The number of bytes written to the file descriptor,
* or -1 on error (with errno set appropriately).
*/
ssize_t
_write_retry(int fd, void *buf, int count, struct timeval * timeout)
{
int n, total = 0, remain = count, rv = 0;
fd_set wfds, xfds;
while (total < count) {
/* Create the write FD set of 1... */
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
FD_ZERO(&xfds);
FD_SET(fd, &xfds);
/* wait for the fd to be available for writing */
rv = _select_retry(fd + 1, NULL, &wfds, &xfds, timeout);
if (rv == -1)
return -1;
else if (rv == 0) {
errno = ETIMEDOUT;
return -1;
}
if (FD_ISSET(fd, &xfds)) {
errno = EPIPE;
return -1;
}
/*
* Attempt to write to fd
*/
n = write(fd, buf + (off_t) total, remain);
/*
* When we know our fd was select()ed and we receive 0 bytes
* when we write, the fd was closed.
*/
if ((n == 0) && (rv == 1)) {
errno = EPIPE;
return -1;
}
if (n == -1) {
if ((errno == EAGAIN) || (errno == EINTR)) {
/*
* Not ready?
*/
continue;
}
/* Other errors: EIO, EINVAL, etc */
return -1;
}
total += n;
remain -= n;
}
return total;
}
/**
* Retry reads until we (a) time out or (b) get our data. Of course, if
* timeout is NULL, it'll wait forever.
*
* @param sockfd File descriptor we want to read from.
* @param buf Preallocated buffer into which we will read data.
* @param count Number of bytes to read.
* @param timeout (struct timeval) describing how long we should retry.
* @return The number of bytes read on success, or -1 on failure.
Note that we will always return (count) or (-1).
*/
ssize_t
_read_retry(int sockfd, void *buf, int count, struct timeval * timeout)
{
int n, total = 0, remain = count, rv = 0;
fd_set rfds, xfds;
while (total < count) {
FD_ZERO(&rfds);
FD_SET(sockfd, &rfds);
FD_ZERO(&xfds);
FD_SET(sockfd, &xfds);
/*
* Select on the socket, in case it closes while we're not
* looking...
*/
rv = _select_retry(sockfd + 1, &rfds, NULL, &xfds, timeout);
if (rv == -1)
return -1;
else if (rv == 0) {
errno = ETIMEDOUT;
return -1;
}
if (FD_ISSET(sockfd, &xfds)) {
errno = EPIPE;
return -1;
}
/*
* Attempt to read off the socket
*/
n = read(sockfd, buf + (off_t) total, remain);
/*
* When we know our socket was select()ed and we receive 0 bytes
* when we read, the socket was closed.
*/
if ((n == 0) && (rv == 1)) {
errno = EPIPE;
return -1;
}
if (n == -1) {
if ((errno == EAGAIN) || (errno == EINTR)) {
/*
* Not ready? Wait for data to become available
*/
continue;
}
/* Other errors: EPIPE, EINVAL, etc */
return -1;
}
total += n;
remain -= n;
}
return total;
}