5 kx
5 kx #ifdef HAVE_CONFIG_H
5 kx #include <config.h>
5 kx #endif
5 kx
5 kx #include <stdlib.h>
5 kx #include <stdio.h>
5 kx #include <sys/sysinfo.h>
5 kx #include <sys/types.h>
5 kx #include <stdint.h>
5 kx #include <dirent.h>
5 kx #include <sys/stat.h> /* chmod(2) */
5 kx #include <sys/file.h>
5 kx #include <sys/mman.h>
5 kx #include <fcntl.h>
5 kx #include <limits.h>
5 kx #include <string.h> /* strdup(3) */
5 kx #include <libgen.h> /* basename(3) */
5 kx #include <ctype.h> /* tolower(3) */
5 kx #include <errno.h>
5 kx #include <time.h>
5 kx #include <sys/time.h>
5 kx #include <pwd.h>
5 kx #include <grp.h>
5 kx #include <stdarg.h>
5 kx #include <poll.h>
5 kx #include <unistd.h>
5 kx
5 kx #include <defs.h>
5 kx #include <wrapper.h>
5 kx
5 kx
5 kx #define WRAPPER_ERRMSG_SIZE 4096
5 kx
5 kx void wrapper_error( const char *fmt, ... )
5 kx {
5 kx va_list arg_ptr;
5 kx char buf[WRAPPER_ERRMSG_SIZE];
5 kx char msg[WRAPPER_ERRMSG_SIZE];
5 kx char *format = "%s: %s\n";
5 kx
5 kx va_start( arg_ptr, fmt );
5 kx
5 kx vsnprintf( msg, WRAPPER_ERRMSG_SIZE, (const void *)fmt, arg_ptr );
5 kx
5 kx va_end( arg_ptr ); /* Reset variable arguments. */
5 kx
5 kx snprintf( buf, WRAPPER_ERRMSG_SIZE, format, "wrapper", msg );
5 kx
5 kx (void)write( STDERR_FILENO, buf, strlen( buf ) );
5 kx
5 kx exit( 1 );
5 kx }
5 kx
5 kx wrapper_errfunc wrapper_fatal = wrapper_error;
5 kx
5 kx
5 kx char *xstrdup( const char *str )
5 kx {
5 kx char *ret;
5 kx
5 kx ret = strdup( str );
5 kx if( !ret )
5 kx wrapper_fatal( "Out of memory, strdup failed" );
5 kx return ret;
5 kx }
5 kx
5 kx void *xmalloc( size_t size )
5 kx {
5 kx void *ret;
5 kx
5 kx ret = malloc( size );
5 kx if( !ret )
5 kx {
5 kx wrapper_fatal( "Out of memory, malloc failed (tried to allocate %lu bytes)", (unsigned long)size );
5 kx return NULL;
5 kx }
5 kx memset( ret, 0, size );
5 kx return ret;
5 kx }
5 kx
5 kx void *xrealloc( void *ptr, size_t size )
5 kx {
5 kx void *ret = NULL;
5 kx
5 kx ret = realloc( ptr, size );
5 kx if( !ret && !size )
5 kx ret = realloc( ptr, 1 );
5 kx if( !ret )
5 kx wrapper_fatal( "Out of memory, realloc failed" );
5 kx return ret;
5 kx }
5 kx
5 kx /************************************************************************
5 kx Limit size of IO chunks, because huge chunks only cause pain.
5 kx OS X 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in
5 kx the absence of bugs, large chunks can result in bad latencies when
5 kx you decide to kill the process.
5 kx
5 kx We pick 8 MiB as our default, but if the platform defines SSIZE_MAX
5 kx that is smaller than that, clip it to SSIZE_MAX, as a call to read(2)
5 kx or write(2) larger than that is allowed to fail. As the last resort,
5 kx we allow a port to pass via CFLAGS e.g. "-DMAX_IO_SIZE=value" to
5 kx override this, if the definition of SSIZE_MAX given by the platform
5 kx is broken.
5 kx ************************************************************************/
5 kx #ifndef MAX_IO_SIZE
5 kx # define MAX_IO_SIZE_DEFAULT (8*1024*1024)
5 kx # if defined(SSIZE_MAX) && (SSIZE_MAX < MAX_IO_SIZE_DEFAULT)
5 kx # define MAX_IO_SIZE SSIZE_MAX
5 kx # else
5 kx # define MAX_IO_SIZE MAX_IO_SIZE_DEFAULT
5 kx # endif
5 kx #endif
5 kx
5 kx /*
5 kx xopen() is the same as open(), but it die()s if the open() fails.
5 kx */
5 kx int xopen( const char *path, int oflag, ... )
5 kx {
5 kx mode_t mode = 0;
5 kx va_list ap;
5 kx
5 kx /*
5 kx va_arg() will have undefined behavior if the specified type is not
5 kx compatible with the argument type. Since integers are promoted to
5 kx ints, we fetch the next argument as an int, and then cast it to a
5 kx mode_t to avoid undefined behavior.
5 kx */
5 kx va_start( ap, oflag );
5 kx if( oflag & O_CREAT )
5 kx mode = va_arg( ap, int );
5 kx va_end( ap );
5 kx
5 kx for( ;; )
5 kx {
5 kx int fd = open( path, oflag, mode );
5 kx if( fd >= 0 )
5 kx return fd;
5 kx if( errno == EINTR )
5 kx continue;
5 kx
5 kx if( (oflag & O_RDWR) == O_RDWR )
5 kx wrapper_fatal( "could not open '%s' for reading and writing", path );
5 kx else if( (oflag & O_WRONLY) == O_WRONLY )
5 kx wrapper_fatal( "could not open '%s' for writing", path );
5 kx else
5 kx wrapper_fatal( "could not open '%s' for reading", path );
5 kx }
5 kx }
5 kx
5 kx static int handle_nonblock( int fd, short poll_events, int err )
5 kx {
5 kx struct pollfd pfd;
5 kx
5 kx if( err != EAGAIN && err != EWOULDBLOCK )
5 kx return 0;
5 kx
5 kx pfd.fd = fd;
5 kx pfd.events = poll_events;
5 kx
5 kx /*
5 kx no need to check for errors, here;
5 kx a subsequent read/write will detect unrecoverable errors
5 kx */
5 kx poll( &pfd, 1, -1 );
5 kx return 1;
5 kx }
5 kx
5 kx /*
5 kx xread() is the same a read(), but it automatically restarts read()
5 kx operations with a recoverable error (EAGAIN and EINTR). xread()
5 kx DOES NOT GUARANTEE that "len" bytes is read even if the data is available.
5 kx */
5 kx ssize_t xread(int fd, void *buf, size_t len)
5 kx {
5 kx ssize_t nr;
5 kx
5 kx if( len > MAX_IO_SIZE )
5 kx len = MAX_IO_SIZE;
5 kx
5 kx while( 1 )
5 kx {
5 kx nr = read( fd, buf, len );
5 kx if( nr < 0 )
5 kx {
5 kx if( errno == EINTR )
5 kx continue;
5 kx if( handle_nonblock(fd, POLLIN, errno) )
5 kx continue;
5 kx }
5 kx return nr;
5 kx }
5 kx }
5 kx
5 kx /*
5 kx xwrite() is the same a write(), but it automatically restarts write()
5 kx operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT
5 kx GUARANTEE that "len" bytes is written even if the operation is successful.
5 kx */
5 kx ssize_t xwrite( int fd, const void *buf, size_t len )
5 kx {
5 kx ssize_t nr;
5 kx
5 kx if( len > MAX_IO_SIZE )
5 kx len = MAX_IO_SIZE;
5 kx
5 kx while( 1 )
5 kx {
5 kx nr = write( fd, buf, len );
5 kx if( nr < 0 )
5 kx {
5 kx if( errno == EINTR )
5 kx continue;
5 kx if( handle_nonblock(fd, POLLOUT, errno) )
5 kx continue;
5 kx }
5 kx return nr;
5 kx }
5 kx }
5 kx
5 kx ssize_t read_in_full( int fd, void *buf, size_t count )
5 kx {
5 kx char *p = buf;
5 kx ssize_t total = 0;
5 kx
5 kx while( count > 0 )
5 kx {
5 kx ssize_t loaded = xread( fd, p, count );
5 kx
5 kx if (loaded < 0)
5 kx return -1;
5 kx
5 kx if (loaded == 0)
5 kx return total;
5 kx
5 kx count -= loaded;
5 kx p += loaded;
5 kx total += loaded;
5 kx }
5 kx
5 kx return total;
5 kx }
5 kx
5 kx ssize_t write_in_full( int fd, const void *buf, size_t count )
5 kx {
5 kx const char *p = buf;
5 kx ssize_t total = 0;
5 kx
5 kx while( count > 0 )
5 kx {
5 kx ssize_t written = xwrite( fd, p, count );
5 kx if( written < 0 )
5 kx return -1;
5 kx
5 kx if( !written )
5 kx {
5 kx errno = ENOSPC;
5 kx return -1;
5 kx }
5 kx count -= written;
5 kx p += written;
5 kx total += written;
5 kx }
5 kx
5 kx return total;
5 kx }
5 kx
5 kx int xdup( int fd )
5 kx {
5 kx int ret = dup( fd );
5 kx if( ret < 0 )
5 kx wrapper_fatal( "dup failed" );
5 kx return ret;
5 kx }
5 kx
5 kx /*
5 kx xfopen() is the same as fopen(), but it die()s if the fopen() fails.
5 kx */
5 kx FILE *xfopen( const char *path, const char *mode )
5 kx {
5 kx for( ;; )
5 kx {
5 kx FILE *fp = fopen( path, mode );
5 kx if (fp)
5 kx return fp;
5 kx if (errno == EINTR)
5 kx continue;
5 kx
5 kx if( *mode && mode[1] == '+' )
5 kx wrapper_fatal( "could not open '%s' for reading and writing", path );
5 kx else if( *mode == 'w' || *mode == 'a' )
5 kx wrapper_fatal( "could not open '%s' for writing", path );
5 kx else
5 kx wrapper_fatal( "could not open '%s' for reading", path );
5 kx }
5 kx }
5 kx
5 kx FILE *xfdopen( int fd, const char *mode )
5 kx {
5 kx FILE *stream = fdopen( fd, mode );
5 kx if( stream == NULL )
5 kx wrapper_fatal( "Out of memory? fdopen failed" );
5 kx return stream;
5 kx }