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