Browse Source

Initial release

Suren A. Chilingaryan 12 years ago
commit
a77e7147c1
11 changed files with 869 additions and 0 deletions
  1. 10 0
      .bzrignore
  2. 60 0
      CMakeLists.txt
  3. 1 0
      config.h.in
  4. 172 0
      default.c
  5. 20 0
      default.h
  6. 317 0
      fastwriter.c
  7. 44 0
      fastwriter.h
  8. 10 0
      fastwriter.pc.in
  9. 55 0
      private.h
  10. 173 0
      sysinfo.c
  11. 7 0
      sysinfo.h

+ 10 - 0
.bzrignore

@@ -0,0 +1,10 @@
+CMakeFiles
+config.h
+fastwriter.pc
+install_manifest.txt
+libfastwriter.so
+libfastwriter.so.0
+libfastwriter.so.0.0.1
+Makefile
+cmake_install.cmake
+CMakeCache.txt

+ 60 - 0
CMakeLists.txt

@@ -0,0 +1,60 @@
+project(fastwriter)
+
+set(FASTWRITER_VERSION "0.0.1")
+set(FASTWRITER_ABI_VERSION "0")
+
+cmake_minimum_required(VERSION 2.8)
+
+
+include(CheckIncludeFiles)
+check_include_files("linux/falloc.h" HAVE_LINUX_FALLOC_H)
+
+set(HEADERS fastwriter.h)
+add_definitions("-fPIC --std=c99 -Wall -O2 -pthread")
+
+add_library(fastwriter SHARED fastwriter.c sysinfo.c default.c) 
+
+set_target_properties(fastwriter PROPERTIES
+    VERSION ${FASTWRITER_VERSION}
+    SOVERSION ${FASTWRITER_ABI_VERSION}
+    LINK_FLAGS "-pthread"
+)
+
+set(TARNAME "fastwriter")
+set(PACKAGE_VERSION ${FASTWRITER_VERSION})
+set(PACKAGE_NAME "${TARNAME}")
+set(PACKAGE_TARNAME "${TARNAME}")
+set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
+set(PACKAGE_BUGREPORT "http://ufo.kit.edu/ufo/newticket")
+
+if(NOT DEFINED BIN_INSTALL_DIR)
+    set(BIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/bin")
+endif(NOT DEFINED BIN_INSTALL_DIR)
+
+if(NOT DEFINED LIB_INSTALL_DIR)
+    set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+endif(NOT DEFINED LIB_INSTALL_DIR)
+
+if(NOT DEFINED INCLUDE_INSTALL_DIR)
+    set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include/${PACKAGE_TARNAME}")
+endif(NOT DEFINED INCLUDE_INSTALL_DIR)
+
+if(NOT DEFINED LOCALE_INSTALL_DIR)
+    set(LOCALE_INSTALL_DIR "${DATA_INSTALL_DIR}/locale/")
+endif(NOT DEFINED LOCALE_INSTALL_DIR)
+
+configure_file(fastwriter.pc.in ${CMAKE_CURRENT_BINARY_DIR}/fastwriter.pc)
+configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+
+install(TARGETS fastwriter
+    LIBRARY DESTINATION lib${LIB_SUFFIX}
+)
+
+install(FILES fastwriter.h
+    DESTINATION include
+)
+
+install(FILES 
+    ${CMAKE_CURRENT_BINARY_DIR}/fastwriter.pc 
+    DESTINATION ${LIB_INSTALL_DIR}/pkgconfig
+)

+ 1 - 0
config.h.in

@@ -0,0 +1 @@
+#cmakedefine HAVE_LINUX_FALLOC_H

+ 172 - 0
default.c

@@ -0,0 +1,172 @@
+#define _FASTWRITER_DEFAULT_C
+
+#define _GNU_SOURCE
+#define _XOPEN_SOURCE 600
+#define _POSIX_C_SOURCE 200112L
+#define _LARGEFILE64_SOURCE
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
+
+
+#ifdef HAVE_LINUX_FALLOC_H
+# include <linux/falloc.h>
+#endif /* HAVE_LINUX_FALLOC_H */
+
+#include "fastwriter.h"
+#include "private.h"
+#include "sysinfo.h"
+
+#define SYNC_MODE
+#define HAVE_FALLOCATE
+#define EXT4_WRITEBLOCK 4194304
+#define EXT4_PREALLOCATE 1073741824
+
+
+typedef struct {
+    int fd;
+    
+    size_t prior_size;		/**< original size of file */
+    size_t preallocated;	/**< preallocated bytes */
+    
+    size_t wr_block;		/**< minimal block of data to write */
+    size_t pa_block;		/**< preallocation setp */
+} fastwriter_default_t;
+
+
+int fastwriter_open_default(fastwriter_t *fw, const char *name, fastwriter_flags_t flags) {
+    int err;
+    char fs[16];
+
+    int open_flags = (O_CREAT|O_WRONLY|O_NOATIME|O_LARGEFILE);
+    int open_mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+
+#ifdef SYNC_MODE
+    open_flags |= O_DIRECT;//|O_SYNC;
+#endif /* SYNC_MODE */
+    
+    fastwriter_default_t *ctx;
+
+    err = get_file_fs(name, sizeof(fs) - 1, fs);
+    if (err) return err;
+    
+    ctx = (fastwriter_default_t*)malloc(sizeof(fastwriter_default_t));
+    if (!ctx) return ENOMEM;
+
+    memset(ctx, 0, sizeof(fastwriter_default_t));
+
+    fw->ctx = ctx;
+
+    if (!strcmp(fs, "raw")) {
+	ctx->wr_block = EXT4_WRITEBLOCK;
+	ctx->pa_block = 0;
+    } else if (!strcmp(fs, "ext4")) {
+	ctx->wr_block = EXT4_WRITEBLOCK;
+	ctx->pa_block = EXT4_PREALLOCATE;
+    } else if (!strcmp(fs, "btrfs")) {
+	ctx->wr_block = EXT4_WRITEBLOCK;
+	ctx->pa_block = EXT4_PREALLOCATE;
+    } else if (!strcmp(fs, "xfs")) {
+	ctx->wr_block = EXT4_WRITEBLOCK;
+	ctx->pa_block = EXT4_PREALLOCATE;
+    } else {
+	ctx->wr_block = EXT4_WRITEBLOCK;
+	ctx->pa_block = 0;
+    }
+    
+    if (flags&FASTWRITER_FLAGS_OVERWRITE)
+	open_flags |= O_TRUNC;
+
+    ctx->fd = open(name, open_flags, open_mode);
+    if (ctx->fd < 0) return errno;
+
+    ctx->prior_size = 0;
+    
+#ifndef HAVE_LINUX_FALLOC_H
+    if (((open_flags&FASTWRITER_FLAGS_OVERWRITE)==0)&&(strcmp(fs, "raw"))) {
+	ctx->prior_size = lseek(ctx->fd, 0, SEEK_END);
+    }
+#endif /* HAVE_LINUX_FALLOC_H */
+
+    ctx->preallocated = 0;
+
+    return 0;
+}
+
+
+void fastwriter_close_default(fastwriter_t *fw) {
+    if (fw->ctx) {
+	fastwriter_default_t *ctx = (fastwriter_default_t*)fw->ctx;
+
+	if (ctx->fd >= 0) {
+#ifndef HAVE_LINUX_FALLOC_H
+	    if (ctx->prior_size) {
+		ftrucate(ctx->fd, ctx->prior_size + fw->written);
+	    }
+#endif /* HAVE_LINUX_FALLOC_H */
+	    close(ctx->fd);
+	}
+	
+	free(ctx);
+	fw->ctx = NULL;
+    }
+}
+
+
+int fastwriter_write_default(fastwriter_t *fw, fastwriter_write_flags_t flags, size_t size, void *data, size_t *written) {
+    size_t sum = 0;
+    ssize_t res;
+    fastwriter_default_t *ctx = (fastwriter_default_t*)fw->ctx;
+    
+    if ((flags&FASTWRITER_WRITE_FLAG_FORCE)==0) {
+	if (size < ctx->wr_block) {
+	    *written = 0;
+	    return 0;
+	}
+    
+        size -= size % ctx->wr_block;
+    }
+
+    if ((ctx->pa_block)&&((fw->written + size) > ctx->preallocated)) {
+#ifdef HAVE_LINUX_FALLOC_H
+    	if (fallocate(ctx->fd, FALLOC_FL_KEEP_SIZE, ctx->preallocated, ctx->pa_block)) {
+#else /* HAVE_LINUX_FALLOC_H */
+    	if (posix_fallocate(ctx->fd, ctx->preallocated, ctx->pa_block)) {
+#endif /* HAVE_LINUX_FALLOC_H */
+	    ctx->pa_block = 0;
+	} else {
+	    ctx->preallocated += ctx->pa_block;
+	}
+    }
+    
+    do {
+	res = write(ctx->fd, data, size);
+	if (res < 0) {
+	    *written = sum;
+	    return errno;
+	}
+	
+	sum += res;
+    } while (sum < size);
+
+#ifdef SYNC_MODE    
+    posix_fadvise(ctx->fd, fw->written, size, POSIX_FADV_DONTNEED);
+#endif /* SYNC_MODE */
+    
+    *written = size;
+    return 0;
+}

+ 20 - 0
default.h

@@ -0,0 +1,20 @@
+#ifndef _FASTWRITER_DEFAULT_H
+#define _FASTWRITER_DEFAULT_H
+
+#include "private.h"
+
+int fastwriter_open_default(fastwriter_t *ctx, const char *name, fastwriter_flags_t flags);
+void fastwriter_close_default(fastwriter_t *ctx);
+int fastwriter_write_default(fastwriter_t *ctx, fastwriter_write_flags_t flags, size_t size, void *data, size_t *written);
+
+#ifdef _FASTWRITER_DEFAULT_C
+fastwriter_api_t fastwriter_default_api = {
+    fastwriter_default_open,
+    fastwriter_default_close,
+    fastwriter_default_write
+};
+#else /* _FASTWRITER_DEFAULT_C */
+extern fastwriter_api_t fastwriter_default_api;
+#endif /* _FASTWRITER_DEFAULT_C */
+
+#endif /* _FASTWRITER_DEFAULT_H */

+ 317 - 0
fastwriter.c

@@ -0,0 +1,317 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
+
+
+#include "private.h"
+#include "default.h"
+#include "sysinfo.h"
+
+fastwriter_t *fastwriter_init(const char *fs, fastwriter_flags_t flags) {
+    fastwriter_t *ctx;
+    
+    ctx = (fastwriter_t*)malloc(sizeof(fastwriter_t));
+    if (!ctx) return ctx;
+    
+    memset(ctx, 0, sizeof(fastwriter_t));
+    ctx->params.flags = flags;
+    ctx->api = &fastwriter_default_api;
+    
+    return ctx;
+}
+
+void fastwriter_destroy(fastwriter_t *ctx) {
+    free(ctx);
+}
+
+int fastwriter_set_buffer_size(fastwriter_t *ctx, size_t buffer_size) {
+    ctx->params.buffer_size = buffer_size;
+    
+    return 0;
+}
+
+static void *fastwriter_writer_thread(void *user);
+
+int fastwriter_open(fastwriter_t *ctx, const char *name, fastwriter_flags_t flags) {
+    int i;
+    int err;
+    int e[4];
+    
+    ctx->flags = flags | ctx->params.flags;
+    
+    switch (ctx->params.buffer_size) {
+     case FASTWRITER_BUFFER_DEFAULT:
+        ctx->size = FASTWRITER_DEFAULT_BUFFER_SIZE;
+	break;
+     case FASTWRITER_BUFFER_MAX:
+        ctx->size = get_free_memory();
+
+	if ((ctx->size - FASTWRITER_RESERVE_MEMORY) < FASTWRITER_DEFAULT_BUFFER_SIZE)
+    	    ctx->size = FASTWRITER_DEFAULT_BUFFER_SIZE;
+	else
+	    ctx->size -= FASTWRITER_RESERVE_MEMORY;
+
+        break;
+     default:
+	ctx->size = ctx->params.buffer_size;
+    }
+    
+    ctx->buffer = malloc(ctx->size);
+    if (!ctx->buffer) {
+	fastwriter_close(ctx);
+	return ENOMEM;
+    }
+    ctx->err = 0;
+    ctx->written = 0;
+    ctx->commited = 0;
+    ctx->chunked = 0;
+    
+    ctx->tail = 0;
+    ctx->head = 0;
+    ctx->pos = 0;
+    
+    err = ctx->api->open(ctx, name, ctx->flags);
+    if (err) {
+	fastwriter_close(ctx);
+	return err;
+    }
+
+    e[0] = pthread_mutex_init(&ctx->data_cond_mutex, NULL);
+    e[1] = pthread_mutex_init(&ctx->space_cond_mutex, NULL);
+    e[2] = pthread_cond_init(&ctx->data_cond, NULL);
+    e[3] = pthread_cond_init(&ctx->space_cond, NULL);
+    
+    if (e[0]|e[1]|e[2]|e[3]) {
+	if (!e[3]) pthread_cond_destroy(&ctx->space_cond);
+	if (!e[2]) pthread_cond_destroy(&ctx->data_cond);
+	if (!e[1]) pthread_mutex_destroy(&ctx->space_cond_mutex);
+	if (!e[0]) pthread_mutex_destroy(&ctx->data_cond_mutex);
+	
+	fastwriter_close(ctx);
+	
+	for (i = 0; i < 4; i++) 
+	    if (e[i]) return e[i];
+    }
+    
+    ctx->clean_locks = 1;
+    ctx->run_flag = 1;
+    
+    err = pthread_create(&ctx->wthread, NULL, &fastwriter_writer_thread, ctx);
+    if (err) {
+	ctx->run_flag = 0;
+	fastwriter_close(ctx);
+	return err;
+    }
+    
+    return 0;
+}
+
+int fastwriter_close(fastwriter_t *ctx) {
+    if ((!ctx->err)&&(ctx->pos != ctx->head))
+	return EBADFD;
+
+    if (ctx->run_flag) {
+	ctx->run_flag = 0;
+
+	pthread_mutex_lock(&ctx->data_cond_mutex);
+	pthread_cond_broadcast(&ctx->data_cond);
+	pthread_mutex_unlock(&ctx->data_cond_mutex);
+	
+	pthread_join(ctx->wthread, NULL);
+    }
+    
+    if (ctx->clean_locks) {
+	pthread_cond_destroy(&ctx->space_cond);
+	pthread_cond_destroy(&ctx->data_cond);
+	pthread_mutex_destroy(&ctx->space_cond_mutex);
+	pthread_mutex_destroy(&ctx->data_cond_mutex);
+    
+	ctx->clean_locks = 0;
+    }
+    
+    ctx->api->close(ctx);
+
+    if (ctx->buffer) {
+	free(ctx->buffer);
+	ctx->buffer = NULL;
+    }
+    
+    return 0;
+    
+}
+
+
+static inline size_t fastwriter_compute_free_space(fastwriter_t *ctx) {
+    if (ctx->pos < ctx->tail) return ctx->tail - ctx->pos;
+    return ctx->tail + ctx->size - ctx->pos - 1;
+}
+
+int fastwriter_get_stats(fastwriter_t *ctx, fastwriter_stats_t *stats) {
+    stats->buffer_size = ctx->size;
+    stats->buffer_used = ctx->size - fastwriter_compute_free_space(ctx);
+    stats->buffer_max = ctx->max_usage;
+    stats->commited = ctx->commited;
+    stats->written = ctx->written;
+    return 0;
+}
+
+
+static void *fastwriter_writer_thread(void *user) {
+    int err = 0;
+    fastwriter_write_flags_t flags;
+    size_t size;
+    size_t head;
+
+    fastwriter_t *ctx = (fastwriter_t*)user;
+
+    while ((ctx->run_flag)||(ctx->head != ctx->tail)) {
+	if (ctx->head != ctx->tail) {
+	    head = ctx->head;
+
+	    if (head > ctx->tail) {
+		size = head - ctx->tail;
+		flags = FASTWRITER_WRITE_FLAGS_DEFAULT;
+	    } else { 
+		size = ctx->size - ctx->tail;
+		flags = FASTWRITER_WRITE_FLAG_FORCE;
+	    }
+	    
+	    if (!ctx->run_flag) 
+		flags |= FASTWRITER_WRITE_FLAG_FORCE;
+
+	    err = ctx->api->write(ctx, flags, size, ctx->buffer + ctx->tail, &size);
+	    if (err) {
+		ctx->err = err;
+		ctx->run_flag = 0;
+
+		pthread_mutex_lock(&ctx->space_cond_mutex);
+		pthread_cond_broadcast(&ctx->space_cond);
+		pthread_mutex_unlock(&ctx->space_cond_mutex);
+		
+		return NULL;
+	    }
+	    
+	    if (size > 0) {
+		ctx->written += size;
+		
+		size += ctx->tail;
+		if (size == ctx->size) ctx->tail = 0;
+		else ctx->tail = size;
+	    
+		pthread_mutex_lock(&ctx->space_cond_mutex);
+		pthread_cond_broadcast(&ctx->space_cond);
+		pthread_mutex_unlock(&ctx->space_cond_mutex);
+	    } else {
+		pthread_mutex_lock(&ctx->data_cond_mutex);
+		while ((ctx->run_flag)&&(ctx->head == head)) {
+		    pthread_cond_wait(&ctx->data_cond, &ctx->data_cond_mutex);
+		}
+	    }
+	} else {
+	    pthread_mutex_lock(&ctx->data_cond_mutex);
+	    while ((ctx->run_flag)&&(ctx->head == ctx->tail)) {
+		pthread_cond_wait(&ctx->data_cond, &ctx->data_cond_mutex);
+	    }
+	}
+    }
+    
+    return NULL;
+}
+
+
+int fastwriter_push_chunk(fastwriter_t *ctx, size_t size, const void *data) {
+    size_t part1, end;
+    size_t free = fastwriter_compute_free_space(ctx);
+
+    if (free < size) {
+	ctx->max_usage = ctx->size;
+	
+	if ((ctx->flags&FASTWRITER_FLAGS_BLOCK)==0)
+	    return EWOULDBLOCK;
+	
+	pthread_mutex_lock(&ctx->space_cond_mutex);
+	while ((ctx->run_flag)&&(fastwriter_compute_free_space(ctx) < size)) {
+	    pthread_cond_wait(&ctx->space_cond, &ctx->space_cond_mutex);
+	}
+	pthread_mutex_unlock(&ctx->space_cond_mutex);
+    } else {
+	end = ctx->size - (free - size);
+	if (end > ctx->max_usage) ctx->max_usage = end;
+    }
+    
+    if (!ctx->run_flag) {
+	if (ctx->err) return ctx->err;
+	return EBADFD;
+    }
+    
+    if (ctx->pos < ctx->tail) end = ctx->tail;
+    else end = ctx->size;
+    
+
+    part1 = end - ctx->pos;
+    
+    if (part1 > size) {
+	    // tail < pos (we have checked for free space)
+	end = size - part1;
+	memcpy(ctx->buffer + ctx->pos, data, part1);
+	memcpy(ctx->buffer, data + part1, end);
+	ctx->pos = end;
+    } else {
+	memcpy(ctx->buffer + ctx->pos, data, size);
+	ctx->pos += size;
+	
+	if (ctx->pos == ctx->size) ctx->pos = 0;
+    }
+    
+    ctx->chunked += size;
+    
+    return 0;
+}
+
+
+int fastwriter_commit(fastwriter_t *ctx) {
+    ctx->head = ctx->pos;
+
+    pthread_mutex_lock(&ctx->data_cond_mutex);
+    pthread_cond_broadcast(&ctx->data_cond);
+    pthread_mutex_unlock(&ctx->data_cond_mutex);
+    
+    ctx->commited += ctx->chunked;
+    ctx->chunked = 0;
+
+    return 0;
+}
+
+
+int fastwriter_cancel(fastwriter_t *ctx) {
+    ctx->pos = ctx->head;
+    
+    ctx->chunked = 0;
+    
+    return 0;
+}
+
+
+int fastwriter_push_data(fastwriter_t *ctx, size_t size, const void *buf) {
+    int err;
+    err = fastwriter_push_chunk(ctx, size, buf);
+    if (err) return err;
+    
+    err = fastwriter_commit(ctx);
+    if (err) fastwriter_cancel(ctx);
+
+    return err;
+}

+ 44 - 0
fastwriter.h

@@ -0,0 +1,44 @@
+#ifndef _FASTWRITER_H
+#define _FASTWRITER_H
+
+typedef struct fastwrtier_s fastwriter_t;
+
+typedef enum {
+    FASTWRITER_FLAGS_DEFAULT = 0,
+    FASTWRITER_FLAGS_BLOCK = 1,		/**< by default the error will be returned if there is no space in the buffer to accomodate the data */
+    FASTWRITER_FLAGS_OVERWRITE = 2	/**< overwrite the data currently in the storage */
+} fastwriter_flags_t;
+
+typedef struct {
+    size_t buffer_size;			/**< buffer size in bytes */
+    size_t buffer_used;			/**< amount of data currently in the buffer */
+    size_t buffer_max;			/**< maximal amount of data in the buffer */
+    size_t commited;			/**< total commited data for current file */
+    size_t written;			/**< total written data for currrent file */
+} fastwriter_stats_t;
+
+#define FASTWRITER_BUFFER_DEFAULT	0
+#define FASTWRITER_BUFFER_MAX		((size_t)-1)
+
+/*
+ * @fs - defines which writter implementation will be actually used. One can
+ * pass just a file name, then a type of partition will be autodetected.
+ * Otherwise, it is possible to pass the name of storage device. In this 
+ * case either RingFS will be used or data will be pushed to the RAW device.
+ */
+fastwriter_t *fastwriter_init(const char *fs, fastwriter_flags_t flags);
+void fastwriter_destroy(fastwriter_t *ctx);
+
+int fastwriter_set_buffer_size(fastwriter_t *ctx, size_t buffer_size);
+int fastwriter_get_stats(fastwriter_t *ctx, fastwriter_stats_t *stats);
+
+int fastwriter_open(fastwriter_t *ctx, const char *name, fastwriter_flags_t flags);
+int fastwriter_close(fastwriter_t *ctx);
+
+int fastwriter_push_chunk(fastwriter_t *ctx, size_t size, const void *buf);
+int fastwriter_commit(fastwriter_t *ctx);
+int fastwriter_cancel(fastwriter_t *ctx);
+
+int fastwriter_push_data(fastwriter_t *ctx, size_t size, const void *buf);
+
+#endif /* _FASTWRITER_H */

+ 10 - 0
fastwriter.pc.in

@@ -0,0 +1,10 @@
+prefix=${CMAKE_INSTALL_PREFIX}
+exec_prefix=${BIN_INSTALL_DIR}
+libdir=${LIB_INSTALL_DIR}
+includedir=${INCLUDE_INSTALL_DIR}
+
+Name: ${TARNAME}
+Description: Fast Streaming Storage Library
+Version: ${PACKAGE_VERSION}
+Libs: -L${LIB_INSTALL_DIR} -lfastwriter
+Cflags: -I${INCLUDE_INSTALL_DIR}

+ 55 - 0
private.h

@@ -0,0 +1,55 @@
+#ifndef _FASTWRITER_PRIVATE_H
+#define _FASTWRITER_PRIVATE_H
+
+#define FASTWRITER_DEFAULT_BUFFER_SIZE 134217728 /* 128 MB */
+#define FASTWRITER_RESERVE_MEMORY 536870912 /* 512 MB */
+
+#include <pthread.h>
+#include "fastwriter.h"
+
+typedef struct {
+    fastwriter_flags_t flags;
+    size_t buffer_size;
+} fastwriter_parameters_t;
+
+typedef enum {
+    FASTWRITER_WRITE_FLAGS_DEFAULT = 0,
+    FASTWRITER_WRITE_FLAG_FORCE = 1		/**< Force writting all passed data */
+} fastwriter_write_flags_t;
+
+typedef struct {
+    int (*open)(fastwriter_t *ctx, const char *name, fastwriter_flags_t flags);
+    void (*close)(fastwriter_t *ctx);
+    int (*write)(fastwriter_t *ctx, fastwriter_write_flags_t flags, size_t size, void *data, size_t *written);
+} fastwriter_api_t;
+
+struct fastwrtier_s {
+    fastwriter_api_t *api;		/**< Writer API */
+    void *ctx;				/**< Writer Context */
+    
+    fastwriter_flags_t flags;
+
+    int clean_locks;
+    volatile int err;		        /**< indicates error reported by the writter backend */
+    volatile int run_flag;
+    pthread_t wthread;
+    pthread_cond_t data_cond;		/**< broadcasted when new data arrives */
+    pthread_mutex_t data_cond_mutex;
+    pthread_cond_t space_cond;		/**< broadcasted when some space is freed */
+    pthread_mutex_t space_cond_mutex;
+
+    void *buffer;
+    size_t size;		/**< size of the ring buffer in bytes */
+    size_t max_usage;		/**< maximal number of bytes used in ring buffer */
+    volatile size_t tail;	/**< first unwritten data in the ring buffer */
+    volatile size_t head;	/**< last commited data in the ring buffer */
+    size_t pos;			/**< last pushed data in the ring buffer */
+
+    size_t written;		/**< number of bytes written */
+    size_t commited;		/**< number of bytes commited */
+    size_t chunked;		/**< number of bytes chunked */
+
+    fastwriter_parameters_t params;
+};
+
+#endif /* _FASTWRITER_PRIVATE_H */

+ 173 - 0
sysinfo.c

@@ -0,0 +1,173 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+
+#define MEMINFO_FILE "/proc/meminfo"
+#define MTAB_FILE "/etc/mtab"
+
+#define BAD_OPEN_MESSAGE					\
+"Error: /proc must be mounted\n"				\
+"  To mount /proc at boot you need an /etc/fstab line like:\n"	\
+"      /proc   /proc   proc    defaults\n"			\
+"  In the meantime, run \"mount /proc /proc -t proc\"\n"
+
+/* This macro opens filename only if necessary and seeks to 0 so
+ * that successive calls to the functions are more efficient.
+ * It also reads the current contents of the file into the global buf.
+ */
+#define FILE_TO_BUF(filename) do{				\
+    static int fd, local_n;					\
+    if ((fd = open(filename, O_RDONLY)) == -1) {		\
+	fputs(BAD_OPEN_MESSAGE, stderr);			\
+	fflush(NULL);						\
+	_exit(102);						\
+    }								\
+    lseek(fd, 0L, SEEK_SET);					\
+    if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) {	\
+	perror(filename);					\
+	fflush(NULL);						\
+	_exit(103);						\
+    }								\
+    buf[local_n] = '\0';					\
+    close(fd);							\
+}while(0)
+
+
+typedef struct mem_table_struct {
+  const char *name;     /* memory type name */
+  unsigned long *slot; /* slot in return struct */
+} mem_table_struct;
+
+static int compare_mem_table_structs(const void *a, const void *b){
+  return strcmp(((const mem_table_struct*)a)->name,((const mem_table_struct*)b)->name);
+}
+
+size_t get_free_memory(void){
+  char buf[4096];
+  unsigned long kb_main_buffers, kb_main_cached, kb_main_free;
+  char namebuf[16]; /* big enough to hold any row name */
+  mem_table_struct findme = { namebuf, NULL};
+  mem_table_struct *found;
+  char *head;
+  char *tail;
+
+  const mem_table_struct mem_table[] = {
+    {"Buffers",      &kb_main_buffers}, // important
+    {"Cached",       &kb_main_cached},  // important
+    {"MemFree",      &kb_main_free},    // important
+  };
+  const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct);
+
+  FILE_TO_BUF(MEMINFO_FILE);
+
+  head = buf;
+  for(;;){
+    tail = strchr(head, ':');
+    if(!tail) break;
+    *tail = '\0';
+    if(strlen(head) >= sizeof(namebuf)){
+      head = tail+1;
+      goto nextline;
+    }
+    strcpy(namebuf,head);
+    found = bsearch(&findme, mem_table, mem_table_count,
+        sizeof(mem_table_struct), compare_mem_table_structs
+    );
+    head = tail+1;
+    if(!found) goto nextline;
+    *(found->slot) = strtoul(head,&tail,10);
+nextline:
+    tail = strchr(head, '\n');
+    if(!tail) break;
+    head = tail+1;
+  }
+  
+  return (kb_main_buffers + kb_main_cached + kb_main_free) * 1024;
+}
+
+
+int get_file_fs(const char *fname, size_t size, char *fs) {
+  int err = 0;
+  char buf[4096];
+  char *fn;
+
+  char *head;
+  char *tail;
+
+  size_t len, max = 0;
+  struct stat st;
+  
+  if ((!fname)||(!fs)||(size < 3)) return EINVAL;
+  
+  if (*fname == '/') {
+    fn = (char*)fname;
+  } else {
+    if (!getcwd(buf, 4095)) return errno;
+    fn = malloc(strlen(fname) + strlen(buf) + 2);
+    if (!fn) return ENOMEM;
+    sprintf(fn, "%s/%s", buf, fname);
+  }
+  
+  if (!stat(fn, &st)) {
+    if (S_ISBLK(st.st_mode)) {
+	strcpy(fs, "raw");
+	goto clean;
+    }
+  }
+  
+  FILE_TO_BUF(MTAB_FILE);
+
+  head = buf;
+  for(;;){
+    head = strchr(head, ' ');
+    if(!head) break;
+
+    head += 1;
+    tail = strchr(head, ' ');
+    if(!tail) break;
+    
+    *tail = '\0';
+
+    len = strlen(head);
+    if((len <= max)||(strncmp(head, fn, len))) {
+      head = tail+1;
+      goto nextline;
+    }
+    
+    head = tail + 1;
+    tail = strchr(head, ' ');
+    if(!tail) break;
+
+    *tail = '\0';
+
+    if (!strncasecmp(head,"root",4)) {
+	head = tail+1;
+	goto nextline;
+    }
+    
+    max = len;
+
+    if (strlen(head) >= size) err = EFAULT;
+    else {
+	err = 0;
+	strcpy(fs, head);
+    }
+    
+    head = tail+1;
+nextline:
+    tail = strchr(head, '\n');
+    if(!tail) break;
+    head = tail+1;
+  }
+
+clean:  
+  if (fn != fname) free(fn);
+
+  return err;
+}

+ 7 - 0
sysinfo.h

@@ -0,0 +1,7 @@
+#ifndef _PCITOOL_SYSINFO_H
+#define _PCITOOL_SYSINFO_H
+
+size_t get_free_memory();
+int get_file_fs(const char *fname, size_t size, char *fs);
+
+#endif /* _PCITOOL_SYSINFO_H */