tdb: handle processes dying during transaction commit.
[ccan] / ccan / tdb / test / external-transaction.c
index 1211da9e76d5c1d54702f3725fc34e30eb8d9658..23d8471386413f39a2d7a7d311ad13c3e22b0021 100644 (file)
@@ -9,7 +9,12 @@
 #include <string.h>
 #include <errno.h>
 #include <ccan/tdb/tdb.h>
+#include <ccan/tdb/tdb_private.h>
 #include <ccan/tap/tap.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+static struct tdb_context *tdb;
 
 static volatile sig_atomic_t alarmed;
 static void do_alarm(int signum)
@@ -17,20 +22,61 @@ static void do_alarm(int signum)
        alarmed++;
 }
 
+static void taplog(struct tdb_context *tdb,
+                  enum tdb_debug_level level,
+                  const char *fmt, ...)
+{
+       va_list ap;
+       char line[200];
+
+       va_start(ap, fmt);
+       vsprintf(line, fmt, ap);
+       va_end(ap);
+
+       diag("external: %s", line);
+}
+
 static int do_operation(enum operation op, const char *name)
 {
+       struct tdb_logging_context logctx = { taplog, NULL };
+
        TDB_DATA k = { .dptr = (void *)"a", .dsize = 1 };
        TDB_DATA d = { .dptr = (void *)"b", .dsize = 1 };
-       struct tdb_context *tdb;
 
-       tdb = tdb_open(name, 0, op == OPEN_WITH_CLEAR_IF_FIRST ?
-                      TDB_CLEAR_IF_FIRST : TDB_DEFAULT, O_RDWR, 0);
-       if (!tdb)
-               return -1;
+       if (op <= KEEP_OPENED) {
+               tdb = tdb_open_ex(name, 0, op == OPEN_WITH_CLEAR_IF_FIRST ?
+                                 TDB_CLEAR_IF_FIRST : TDB_DEFAULT, O_RDWR, 0,
+                                 &logctx, NULL);
+               if (!tdb)
+                       return -1;
+       }
 
-       if (op == OPEN || op == OPEN_WITH_CLEAR_IF_FIRST) {
-               tdb_close(tdb);
+       if (op == KEEP_OPENED) {
                return 0;
+       } else if (op == OPEN || op == OPEN_WITH_CLEAR_IF_FIRST || op == CLOSE) {
+               tdb_close(tdb);
+               tdb = NULL;
+               return 1;
+       } else if (op == STORE_KEEP_OPENED) {
+               if (tdb_store(tdb, k, d, 0) != 0)
+                       return -2;
+               return 1;
+       } else if (op == FETCH_KEEP_OPENED) {
+               TDB_DATA ret;
+               ret = tdb_fetch(tdb, k);
+               if (ret.dptr == NULL) {
+                       if (tdb_error(tdb) == TDB_ERR_NOEXIST)
+                               return 1;
+                       return -3;
+               }
+               if (ret.dsize != 1 || *(char *)ret.dptr != 'b')
+                       return -4;
+               free(ret.dptr);
+               return 1;
+       } else if (op == CHECK_KEEP_OPENED) {
+               return tdb_check(tdb, NULL, 0) == 0;
+       } else if (op == NEEDS_RECOVERY_KEEP_OPENED) {
+               return tdb_needs_recovery(tdb);
        }
 
        alarmed = 0;
@@ -40,21 +86,29 @@ static int do_operation(enum operation op, const char *name)
        if (tdb_transaction_start(tdb) != 0)
                goto maybe_alarmed;
 
+       alarm(0);
        if (tdb_store(tdb, k, d, 0) != 0) {
                tdb_transaction_cancel(tdb);
                tdb_close(tdb);
+               tdb = NULL;
                return -2;
        }
 
        if (tdb_transaction_commit(tdb) == 0) {
                tdb_delete(tdb, k);
-               tdb_close(tdb);
+               if (op != TRANSACTION_KEEP_OPENED) {
+                       tdb_close(tdb);
+                       tdb = NULL;
+               }
                return 1;
        }
 
        tdb_delete(tdb, k);
 maybe_alarmed:
-       tdb_close(tdb);
+       if (op != TRANSACTION_KEEP_OPENED) {
+               tdb_close(tdb);
+               tdb = NULL;
+       }
        if (alarmed)
                return 0;
        return -3;
@@ -67,7 +121,7 @@ struct agent {
 /* Do this before doing any tdb stuff.  Return handle, or NULL. */
 struct agent *prepare_external_agent(void)
 {
-       int pid;
+       int pid, ret;
        int command[2], response[2];
        struct sigaction act = { .sa_handler = do_alarm };
        char name[1+PATH_MAX];
@@ -93,7 +147,7 @@ struct agent *prepare_external_agent(void)
        close(response[0]);
        sigaction(SIGALRM, &act, NULL);
 
-       while (read(command[0], name, sizeof(name)) != 0) {
+       while ((ret = read(command[0], name, sizeof(name))) > 0) {
                int result;
 
                result = do_operation(name[0], name+1);
@@ -101,11 +155,12 @@ struct agent *prepare_external_agent(void)
                    != sizeof(result))
                        err(1, "Writing response");
        }
+       diag("external: read %i: %s", ret, strerror(errno));
        exit(0);
 }
 
 /* Ask the external agent to try to do an operation. */
-bool external_agent_operation(struct agent *agent,
+int external_agent_operation(struct agent *agent,
                              enum operation op, const char *tdbname)
 {
        int res;