The restart library is a collection of functions that restart themselves when they have not completed because of a possibly temporary event. We use functions from the restart library throughout the book to simplify programs that must deal with the effects of signals and incomplete I/O. The source code for the restart library is available on the book web site. We have included only those functions that are needed in the book. You can easily add other functions, if necessary.
The restart library addresses two main types of events: interruption by a signal and incomplete I/O. For example, many library functions, including read
and write
, return –1 and set errno
to EINTR
when interrupted by a signal before any I/O takes place. This interruption is not a real error but a natural event that occurs when the program handles a signal in the presence of blocking I/O. The library functions restart when the function they wrap returns –1 with errno
set to EINTR
.
Some functions such write
might return before a full request is satisfied. When a request is made to write n
bytes, the write
call is considered successful when any number of bytes greater than zero has been written. A write
function could return a positive value less than n
if a signal is caught before the requested amount has been written or if an I/O buffer is full, such as when writing to a pipe or network connection. Typically, the program must handle this case and write the remaining bytes. The functions in the restart library simplify the user code by writing the remaining bytes. Table B.1 gives a complete list of the functions in the restart library.
The restart library includes two types of functions. The functions whose names start with r_
are restarted versions of traditional library functions. These functions have the same prototypes as the corresponding traditional functions. For example, the r_read
function takes the same parameters as read
, but restarts read
if the read
function returns –1 with errno
set to EINTR
. For these functions, the table describes only the differences between the function and its traditional counterpart.
Table B.1. The functions in the restart library. The first part of the table shows the functions that correspond to traditional functions. All functions in the restart library restart when interrupted by a signal. None of these functions return –1 with errno
set to EINTR
.
prototype | description |
---|---|
| similar to |
int r_dup2(int fildes, int fildes2) | similar to |
int r_open2(const char *path, int oflag) | similar to |
int r_open3(const char *path, int oflag, mode_t mode) | similar to |
int r_read(int fd, void *buf, size_t size) | similar to |
| similar to |
pid_t r_waitpid(pid_t pid, int *stat_loc, int options) | similar to |
int r_write(int fd, void *buf, size_t size) | similar to |
struct timeval add2currenttime( double seconds) | returns a |
int copyfile(int fromfd, int tofd) | copies bytes from one open file descriptor to another until the end of the file or an error |
int readblock(int fd, void *buf, size_t size) | reads exactly |
int readline(int fd, char *buf, int nbytes) | reads a line into |
int readwrite(int fromfd, int tofd) | copies at most |
int readwriteblock(int fromfd, int tofd, char *buf, int size) | copies exactly |
int waitfdtimed(int fd, struct timeval end) | waits for data to be available on given file descriptor or until time |
The functions shown in the second part of Table B.1 do not correspond to any traditional library functions. For example, readline
handles restarting when a signal occurs and continues reading until the end of a line or the end of the buffer. The readblock
function restarts when the requested number of bytes has not yet been read.
The following is a more complete description of the functions in the restart library.
struct timeval add2currenttime(double seconds);
returns a
struct timeval
corresponding to the current time plusseconds
seconds. The implementation callsgettimeofday
to get the current time, converts theseconds
parameter to integer values representing seconds and microseconds, and adds these values to the current time.
int copyfile(int fromfd, int tofd);
copies bytes from open file descriptor
fromfd
to open file descriptortofd
until either end-of-file or an error occurs. If successful,copyfile
returns the number of bytes copied. If unsuccessful,copyfile
returns –1 and setserrno
. Thecopyfile
function does not return an error if any bytes are successfully copied, even if an error occurs on a subsequent write that follows a successful read.
int r_close(int fildes);
closes
fildes
. If successful,r_close
returns 0. If unsuccessful,r_close
returns –1 and setserrno
. The implementation callsclose
in a loop, restarting ifclose
returns –1 witherrno
set toEINTR
.
int r_dup2(int fildes, int fildes2);
closes
fildes2
if it was open and causesfildes2
to refer to the same file asfildes
. If successful,r_dup2
returnsfildes2
. If unsuccessful,r_dup2
returns –1 and setserrno
. The implementation callsdup2
in a loop, restarting ifdup2
returns –1 witherrno
set toEINTR
.
int r_open2(const char *path, int oflag);
opens a file descriptor for
path
. Theoflag
should not have theO_CREAT
bit set. If successful,r_open2
returns an open file descriptor. If unsuccessful,r_open2
returns –1 and setserrno
. The implementation callsopen
in a loop, restarting ifopen
returns –1 witherrno
set toEINTR
.
int r_open3(const char *path, int oflag, mode_t mode);
opens a file descriptor for
path
. Theoflag
should have theO_CREAT
bit set. If successful,r_open3
returns an open file descriptor. If unsuccessful,r_open3
returns –1 and setserrno
. The implementation callsopen
in a loop, restarting ifopen
returns –1 witherrno
set toEINTR
.
ssize_t r_read(int fd, void *buf, size_t size);
reads at most
size
bytes from the open file descriptorfd
intobuf
. If successful,r_read
returns the number of bytes read. If unsuccessful,r_read
returns –1 and setserrno
. The implementation callsread
in a loop, restarting ifread
returns –1 witherrno
set toEINTR
.
pid_t r_wait(int *stat_loc);
suspends execution of the calling thread until status information for one of its terminated children is available. If successful,
r_wait
returns the process ID of a terminated child process. If unsuccessful,r_wait
returns –1 and setserrno
. The implementation callswait
in a loop, restarting ifwait
returns –1 witherrno
set toEINTR
.
pid_t r_waitpid(pid_t pid, int *stat_loc, int options);
suspends execution of the calling thread until status information is available for a specified child process. If successful,
r_waitpid
returns the process ID of a child process. If unsuccessful,r_waitpid
returns –1 and setserrno
. The implementation callswaitpid
in a loop, restarting ifwaitpid
returns –1 witherrno
set toEINTR
.
ssize_t r_write(int fd, void *buf, size_t size);
attempts to write exactly
size
bytes frombuf
to the open file descriptorfd
. If successful,r_write
returnssize
. If unsuccessful,r_write
returns –1 and setserrno
. The only possible return values aresize
and –1. The implementation callswrite
in a loop, restarting ifwrite
returns –1 witherrno
set toEINTR
. Ifwrite
does not output all the requested bytes,r_write
continues to callwrite
until all the bytes have been written or an error occurs.
ssize_t readblock(int fd, void *buf, size_t size);
attempts to read exactly
size
bytes from the open file descriptorfd
into thebuf
. Ifreadblock
reaches end-of-file before reading any bytes, it returns 0. If exactlysize
bytes are read,readblock
returnssize
. If unsuccessful,readblock
returns –1 and setserrno
. Ifreadblock
encounters end-of-file after some but not all of the needed bytes, the function returns –1 and setserrno
toEINVAL
.
int readline(int fd, void *buf, size_t size);
attempts to read a line from the open file descriptor
fd
intobuf
, a buffer of sizesize
. Ifreadline
reaches end-of-file before reading any bytes, it returns 0. If successful,buf
contains a string ending with a newline. Thereadline
function returns the length of the string. If unsuccessful,readline
returns –1 and setserrno
. Two errors are possible other than an error reading fromfd
: end-of-file before newline orsize-1
bytes read before newline. Both errors causereadline
to seterrno
toEINVAL
.
ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds);
attempts to read at most
nbyte
bytes from the open file descriptorfd
into the bufferbuf
. Thereadtimed
function behaves the same asr_read
unless no bytes are available in a number of seconds given byseconds
. If no bytes are available within the timeout period,readtimed
returns –1 and setserrno
toETIME
. If interrupted by a signal,readtimed
restarts but maintains the original ending timeout.
int readwrite(int fromfd, int tofd);
reads at most
PIPE_BUF
bytes from open file descriptorfromfd
and writes the bytes read to the open file descriptortofd
. If successful,readwrite
returns the number of bytes copied. Ifreadwrite
reaches end-of-file onfromfd
, it returns 0. If unsuccessful,readwrite
returns –1 and setserrno
.
int readwriteblock(int fromfd, int tofd, char *buf, int size);
reads exactly
size
bytes from the open file descriptorfromfd
and writes them to the open file descriptortofd
. Thebuf
parameter is a buffer of sizesize
. If successful,readwriteblock
returnssize
and the bytes read are inbuf
. Ifreadwriteblock
reaches end-of-file onfromfd
before any bytes are read, it returns 0. If unsuccessful,readwriteblock
returns –1 and setserrno
.
int waitfdtimed(int fd, struct timeval end);
waits until data is available to be read from file descriptor
fd
or until the current time is later than the time inend
. If a read onfd
will not block,waitfdtimed
returns 0. If unsuccessful,waitfdtimed
returns –1 and setserrno
. Iffd
will still block when timeend
occurs,waitfdtimed
setserrno
toETIME
. Iffd
is negative or greater than or equal toFD_SETSIZE
,waitfdtimed
setserrno
toEINVAL
.
Program B.1 is the header file containing the prototype for these functions. Program B.2 gives the complete code for the restart library.
Example B.1. restart.h
The header file containing the prototypes for the restart library.
#include <fcntl.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #ifndef ETIME #define ETIME ETIMEDOUT #endif struct timeval add2currenttime(double seconds); int copyfile(int fromfd, int tofd); int r_close(int fildes); int r_dup2(int fildes, int fildes2); int r_open2(const char *path, int oflag); int r_open3(const char *path, int oflag, mode_t mode); ssize_t r_read(int fd, void *buf, size_t size); pid_t r_wait(int *stat_loc); pid_t r_waitpid(pid_t pid, int *stat_loc, int options); ssize_t r_write(int fd, void *buf, size_t size); ssize_t readblock(int fd, void *buf, size_t size); int readline(int fd, char *buf, int nbytes); ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds); int readwrite(int fromfd, int tofd); int readwriteblock(int fromfd, int tofd, char *buf, int size); int waitfdtimed(int fd, struct timeval end);
Example B.2. restart.c
The restart library.
#include <errno.h> #include <fcntl.h> #include <limits.h> #include <string.h> #include <sys/select.h> #include <sys/time.h> #include <sys/wait.h> #include "restart.h" #define BLKSIZE PIPE_BUF #define MILLION 1000000L #define D_MILLION 1000000.0 /* Private functions */ static int gettimeout(struct timeval end, struct timeval *timeoutp) { gettimeofday(timeoutp, NULL); timeoutp->tv_sec = end.tv_sec - timeoutp->tv_sec; timeoutp->tv_usec = end.tv_usec - timeoutp->tv_usec; if (timeoutp->tv_usec >= MILLION) { timeoutp->tv_sec++; timeoutp->tv_usec -= MILLION; } if (timeoutp->tv_usec < 0) { timeoutp->tv_sec--; timeoutp->tv_usec += MILLION; } if ((timeoutp->tv_sec < 0) || ((timeoutp->tv_sec == 0) && (timeoutp->tv_usec == 0))) { errno = ETIME; return -1; } return 0; } /* Restart versions of traditional functions */ int r_close(int fildes) { int retval; while (retval = close(fildes), retval == -1 && errno == EINTR) ; return retval; } int r_dup2(int fildes, int fildes2) { int retval; while (retval = dup2(fildes, fildes2), retval == -1 && errno == EINTR) ; return retval; } int r_open2(const char *path, int oflag) { int retval; while (retval = open(path, oflag), retval == -1 && errno == EINTR) ; return retval; } int r_open3(const char *path, int oflag, mode_t mode) { int retval; while (retval = open(path, oflag, mode), retval == -1 && errno == EINTR) ; return retval; } ssize_t r_read(int fd, void *buf, size_t size) { ssize_t retval; while (retval = read(fd, buf, size), retval == -1 && errno == EINTR) ; return retval; } pid_t r_wait(int *stat_loc) { pid_t retval; while (((retval = wait(stat_loc)) == -1) && (errno == EINTR)) ; return retval; } pid_t r_waitpid(pid_t pid, int *stat_loc, int options) { pid_t retval; while (((retval = waitpid(pid, stat_loc, options)) == -1) && (errno == EINTR)) ; return retval; } ssize_t r_write(int fd, void *buf, size_t size) { char *bufp; size_t bytestowrite; ssize_t byteswritten; size_t totalbytes; for (bufp = buf, bytestowrite = size, totalbytes = 0; bytestowrite > 0; bufp += byteswritten, bytestowrite -= byteswritten) { byteswritten = write(fd, bufp, bytestowrite); if ((byteswritten) == -1 && (errno != EINTR)) return -1; if (byteswritten == -1) byteswritten = 0; totalbytes += byteswritten; } return totalbytes; } /* Utility functions */ struct timeval add2currenttime(double seconds) { struct timeval newtime; gettimeofday(&newtime, NULL); newtime.tv_sec += (int)seconds; newtime.tv_usec += (int)((seconds - (int)seconds)*D_MILLION + 0.5); if (newtime.tv_usec >= MILLION) { newtime.tv_sec++; newtime.tv_usec -= MILLION; } return newtime; } int copyfile(int fromfd, int tofd) { int bytesread; int totalbytes = 0; while ((bytesread = readwrite(fromfd, tofd)) > 0) totalbytes += bytesread; return totalbytes; } ssize_t readblock(int fd, void *buf, size_t size) { char *bufp; ssize_t bytesread; size_t bytestoread; size_t totalbytes; for (bufp = buf, bytestoread = size, totalbytes = 0; bytestoread > 0; bufp += bytesread, bytestoread -= bytesread) { bytesread = read(fd, bufp, bytestoread); if ((bytesread == 0) && (totalbytes == 0)) return 0; if (bytesread == 0) { errno = EINVAL; return -1; } if ((bytesread) == -1 && (errno != EINTR)) return -1; if (bytesread == -1) bytesread = 0; totalbytes += bytesread; } return totalbytes; } int readline(int fd, char *buf, int nbytes) { int numread = 0; int returnval; while (numread < nbytes - 1) { returnval = read(fd, buf + numread, 1); if ((returnval == -1) && (errno == EINTR)) continue; if ((returnval == 0) && (numread == 0)) return 0; if (returnval == 0) break; if (returnval == -1) return -1; numread++; if (buf[numread-1] == ' ') { buf[numread] = ' '; return numread; } } errno = EINVAL; return -1; } ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds) { struct timeval timedone; timedone = add2currenttime(seconds); if (waitfdtimed(fd, timedone) == -1) return (ssize_t)(-1); return r_read(fd, buf, nbyte); } int readwrite(int fromfd, int tofd) { char buf[BLKSIZE]; int bytesread; if ((bytesread = r_read(fromfd, buf, BLKSIZE)) < 0) return -1; if (bytesread == 0) return 0; if (r_write(tofd, buf, bytesread) < 0) return -1; return bytesread; } int readwriteblock(int fromfd, int tofd, char *buf, int size) { int bytesread; bytesread = readblock(fromfd, buf, size); if (bytesread != size) /* can only be 0 or -1 */ return bytesread; return r_write(tofd, buf, size); } int waitfdtimed(int fd, struct timeval end) { fd_set readset; int retval; struct timeval timeout; if ((fd < 0) || (fd >= FD_SETSIZE)) { errno = EINVAL; return -1; } FD_ZERO(&readset); FD_SET(fd, &readset); if (gettimeout(end, &timeout) == -1) return -1; while (((retval = select(fd+1, &readset, NULL, NULL, &timeout)) == -1) && (errno == EINTR)) { if (gettimeout(end, &timeout) == -1) return -1; FD_ZERO(&readset); FD_SET(fd, &readset); } if (retval == 0) { errno = ETIME; return -1; } if (retval == -1) return -1; return 0; }