summaryrefslogtreecommitdiff
path: root/csvncgi/wrapper.c
diff options
context:
space:
mode:
authorkx <kx@radix.pro>2023-03-24 03:55:33 +0300
committerkx <kx@radix.pro>2023-03-24 03:55:33 +0300
commitbfc1508d26c89c9a36d2d9a827fe2c4ed128884d (patch)
tree8d41298a7072a3e289e4912f77ece75cbea1bd54 /csvncgi/wrapper.c
parentc836ae3775cf72f17e0b7e3792d156fdb389bee3 (diff)
downloadcsvn-ui-bfc1508d26c89c9a36d2d9a827fe2c4ed128884d.tar.xz
Version 0.1.4
Diffstat (limited to 'csvncgi/wrapper.c')
-rw-r--r--csvncgi/wrapper.c305
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;
+}