tdb2: copy tdb1's changed expansion logic.
[ccan] / ccan / failtest / failtest.h
index 473f9e7950fa498e5d56a613353b049e69c82084..9af5669678f2295edccb5cbea4cb777016f40b8f 100644 (file)
@@ -1,9 +1,15 @@
+/* Licensed under LGPL - see LICENSE file for details */
 #ifndef CCAN_FAILTEST_H
 #define CCAN_FAILTEST_H
+#include "config.h"
+#if HAVE_FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
 #include <sys/types.h>
 #include <stdbool.h>
 #include <fcntl.h>
 #include <ccan/compiler/compiler.h>
+#include <ccan/tlist/tlist.h>
 
 /**
  * failtest_init - initialize the failtest module
@@ -36,10 +42,13 @@ enum failtest_call_type {
        FAILTEST_CALLOC,
        FAILTEST_REALLOC,
        FAILTEST_OPEN,
+       FAILTEST_CLOSE,
        FAILTEST_PIPE,
        FAILTEST_READ,
        FAILTEST_WRITE,
        FAILTEST_FCNTL,
+       FAILTEST_MMAP,
+       FAILTEST_LSEEK
 };
 
 struct calloc_call {
@@ -64,6 +73,14 @@ struct open_call {
        const char *pathname;
        int flags;
        mode_t mode;
+       bool always_save;
+       bool closed;
+       /* This is used for O_TRUNC opens on existing files. */
+       struct contents_saved *saved;
+};
+
+struct close_call {
+       int fd;
 };
 
 struct pipe_call {
@@ -86,6 +103,9 @@ struct write_call {
        const void *buf;
        size_t count;
        off_t off;
+       bool is_pwrite;
+       struct failtest_call *opener;
+       struct contents_saved *saved;
 };
 
 struct fcntl_call {
@@ -99,6 +119,26 @@ struct fcntl_call {
        } arg;
 };
 
+struct mmap_call {
+       void *ret;
+       void *addr;
+       size_t length;
+       int prot;
+       int flags;
+       int fd;
+       off_t offset;
+       struct failtest_call *opener;
+       struct contents_saved *saved;
+};
+
+struct lseek_call {
+       ssize_t ret;
+       int fd;
+       off_t offset;
+       int whence;
+       off_t old_off;
+};
+
 /**
  * struct failtest_call - description of a call redirected to failtest module
  * @type: the call type
@@ -114,6 +154,8 @@ struct fcntl_call {
  *     failtest_hook, failtest_exit_check
  */
 struct failtest_call {
+       /* We're in the history list. */
+       struct list_node list;
        enum failtest_call_type type;
        /* Where we were called from. */
        const char *file;
@@ -123,49 +165,70 @@ struct failtest_call {
        /* What we set errno to. */
        int error;
        /* How do we clean this up? */
-       void (*cleanup)(void *u);
+       void (*cleanup)(void *u, bool restore);
+       /* Should their program have cleaned up? */
+       bool can_leak;
+       /* Backtrace of call chain. */
+       void **backtrace;
+       unsigned int backtrace_num;
        /* The actual call data. */
        union {
                struct calloc_call calloc;
                struct malloc_call malloc;
                struct realloc_call realloc;
                struct open_call open;
+               struct close_call close;
                struct pipe_call pipe;
                struct read_call read;
                struct write_call write;
                struct fcntl_call fcntl;
+               struct mmap_call mmap;
+               struct lseek_call lseek;
        } u;
 };
 
+/* This defines struct tlist_calls. */
+TLIST_TYPE(calls, struct failtest_call);
+
+enum failtest_result {
+       /* Yes try failing this call. */
+       FAIL_OK,
+       /* No, don't try failing this call. */
+       FAIL_DONT_FAIL,
+       /* Try failing this call but don't go too far down that path. */
+       FAIL_PROBE,
+};
+
 /**
  * failtest_hook - whether a certain call should fail or not.
  * @history: the ordered history of all failtest calls.
- * @num: the number of elements in @history (greater than 0)
  *
  * The default value of this hook is failtest_default_hook(), which returns
- * true (ie. yes, fail the call).
+ * FAIL_OK (ie. yes, fail the call).
  *
  * You can override it, and avoid failing certain calls.  The parameters
  * of the call (but not the return value(s)) will be filled in for the last
  * call.
  *
  * Example:
- *     static bool dont_fail_allocations(struct failtest_call *history,
- *                                       unsigned num)
+ *     static enum failtest_result dont_fail_alloc(struct tlist_calls *history)
  *     {
- *             return history[num-1].type != FAILTEST_MALLOC
- *                     && history[num-1].type != FAILTEST_CALLOC
- *                     && history[num-1].type != FAILTEST_REALLOC;
+ *             struct failtest_call *call;
+ *             call = tlist_tail(history, list);
+ *             if (call->type == FAILTEST_MALLOC
+ *                     || call->type == FAILTEST_CALLOC
+ *                     || call->type == FAILTEST_REALLOC)
+ *                     return FAIL_DONT_FAIL;
+ *             return FAIL_OK;
  *     }
  *     ...
- *             failtest_hook = dont_fail_allocations;
+ *             failtest_hook = dont_fail_alloc;
  */
-extern bool (*failtest_hook)(struct failtest_call *history, unsigned num);
+extern enum failtest_result (*failtest_hook)(struct tlist_calls *history);
 
 /**
  * failtest_exit_check - hook for additional checks on a failed child.
  * @history: the ordered history of all failtest calls.
- * @num: the number of elements in @history (greater than 0)
  *
  * Your program might have additional checks to do on failure, such as
  * check that a file is not corrupted, or than an error message has been
@@ -174,11 +237,17 @@ extern bool (*failtest_hook)(struct failtest_call *history, unsigned num);
  * If this returns false, the path to this failure will be printed and the
  * overall test will fail.
  */
-extern bool (*failtest_exit_check)(struct failtest_call *history,
-                                  unsigned num);
+extern bool (*failtest_exit_check)(struct tlist_calls *history);
 
-/* This usually fails the call. */
-bool failtest_default_hook(struct failtest_call *history, unsigned num);
+/**
+ * failtest_has_failed - determine if a failure has occurred.
+ *
+ * Sometimes you want to exit immediately if you've experienced an
+ * injected failure.  This is useful when you have four separate tests
+ * in your test suite, and you don't want to do the next one if you've
+ * had a failure in a previous one.
+ */
+extern bool failtest_has_failed(void);
 
 /**
  * failtest_timeout_ms - how long to wait before killing child.