Appendix B. Restart Library

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

int r_close(int fildes)

similar to close

int r_dup2(int fildes,
     int fildes2)

similar to dup2

int r_open2(const char *path,
     int oflag)

similar to open called with two parameters

int r_open3(const char *path,
     int oflag, mode_t mode)

similar to open called with three parameters

int r_read(int fd, void *buf,
     size_t size)

similar to read

pid_t r_wait(int *stat_loc)

similar to wait

pid_t r_waitpid(pid_t pid,
     int *stat_loc, int options)

similar to waitpid

int r_write(int fd, void *buf,
     size_t size)

similar to write but restarts if fewer than size bytes are written (The only possible return values are size and –1.)

struct timeval
     add2currenttime(
     double seconds)

returns a struct timeval structure corresponding to the current time plus seconds seconds

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 size bytes into the buffer or returns an error

int readline(int fd,
     char *buf, int nbytes)

reads a line into buf, which has size nbytes

int readwrite(int fromfd,
     int tofd)

copies at most PIPE_BUF bytes from one open file descriptor to another

int readwriteblock(int fromfd,
     int tofd, char *buf,
     int size)

copies exactly size bytes from one open file descriptor to another, using the given buffer and size

int waitfdtimed(int fd,
     struct timeval end)

waits for data to be available on given file descriptor or until time end

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 plus seconds seconds. The implementation calls gettimeofday to get the current time, converts the seconds 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 descriptor tofd until either end-of-file or an error occurs. If successful, copyfile returns the number of bytes copied. If unsuccessful, copyfile returns –1 and sets errno. The copyfile 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 sets errno. The implementation calls close in a loop, restarting if close returns –1 with errno set to EINTR.

int r_dup2(int fildes, int fildes2);

closes fildes2 if it was open and causes fildes2 to refer to the same file as fildes. If successful, r_dup2 returns fildes2. If unsuccessful, r_dup2 returns –1 and sets errno. The implementation calls dup2 in a loop, restarting if dup2 returns –1 with errno set to EINTR.

int r_open2(const char *path, int oflag);

opens a file descriptor for path. The oflag should not have the O_CREAT bit set. If successful, r_open2 returns an open file descriptor. If unsuccessful, r_open2 returns –1 and sets errno. The implementation calls open in a loop, restarting if open returns –1 with errno set to EINTR.

int r_open3(const char *path, int oflag, mode_t mode);

opens a file descriptor for path. The oflag should have the O_CREAT bit set. If successful, r_open3 returns an open file descriptor. If unsuccessful, r_open3 returns –1 and sets errno. The implementation calls open in a loop, restarting if open returns –1 with errno set to EINTR.

ssize_t r_read(int fd, void *buf, size_t size);

reads at most size bytes from the open file descriptor fd into buf. If successful, r_read returns the number of bytes read. If unsuccessful, r_read returns –1 and sets errno. The implementation calls read in a loop, restarting if read returns –1 with errno set to EINTR.

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 sets errno. The implementation calls wait in a loop, restarting if wait returns –1 with errno set to EINTR.

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 sets errno. The implementation calls waitpid in a loop, restarting if waitpid returns –1 with errno set to EINTR.

ssize_t r_write(int fd, void *buf, size_t size);

attempts to write exactly size bytes from buf to the open file descriptor fd. If successful, r_write returns size. If unsuccessful, r_write returns –1 and sets errno. The only possible return values are size and –1. The implementation calls write in a loop, restarting if write returns –1 with errno set to EINTR. If write does not output all the requested bytes, r_write continues to call write 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 descriptor fd into the buf. If readblock reaches end-of-file before reading any bytes, it returns 0. If exactly size bytes are read, readblock returns size. If unsuccessful, readblock returns –1 and sets errno. If readblock encounters end-of-file after some but not all of the needed bytes, the function returns –1 and sets errno to EINVAL.

int readline(int fd, void *buf, size_t size);

attempts to read a line from the open file descriptor fd into buf, a buffer of size size. If readline reaches end-of-file before reading any bytes, it returns 0. If successful, buf contains a string ending with a newline. The readline function returns the length of the string. If unsuccessful, readline returns –1 and sets errno. Two errors are possible other than an error reading from fd: end-of-file before newline or size-1 bytes read before newline. Both errors cause readline to set errno to EINVAL.

ssize_t readtimed(int fd, void *buf, size_t nbyte,
                   double seconds);

attempts to read at most nbyte bytes from the open file descriptor fd into the buffer buf. The readtimed function behaves the same as r_read unless no bytes are available in a number of seconds given by seconds. If no bytes are available within the timeout period, readtimed returns –1 and sets errno to ETIME. 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 descriptor fromfd and writes the bytes read to the open file descriptor tofd. If successful, readwrite returns the number of bytes copied. If readwrite reaches end-of-file on fromfd, it returns 0. If unsuccessful, readwrite returns –1 and sets errno.

int readwriteblock(int fromfd, int tofd, char *buf, int size);

reads exactly size bytes from the open file descriptor fromfd and writes them to the open file descriptor tofd. The buf parameter is a buffer of size size. If successful, readwriteblock returns size and the bytes read are in buf. If readwriteblock reaches end-of-file on fromfd before any bytes are read, it returns 0. If unsuccessful, readwriteblock returns –1 and sets errno.

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 in end. If a read on fd will not block, waitfdtimed returns 0. If unsuccessful, waitfdtimed returns –1 and sets errno. If fd will still block when time end occurs, waitfdtimed sets errno to ETIME. If fd is negative or greater than or equal to FD_SETSIZE, waitfdtimed sets errno to EINVAL.

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;
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset