]> git.ozlabs.org Git - ccan/blobdiff - ccan/tdb2/test/external-agent.c
tdb2: allow multiple chain locks.
[ccan] / ccan / tdb2 / test / external-agent.c
index bcf5d215a7a210baf3d8beafa23b0c173c22e556..081b8500a0fb206ab2ec239e94d9bb333f399516 100644 (file)
@@ -1,5 +1,6 @@
 #include "external-agent.h"
 #include "logging.h"
+#include "lock-tracking.h"
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
 static struct tdb_context *tdb;
 
-#if 1 /* FIXME */
-static unsigned int locking_would_block = 0;
-static bool nonblocking_locks = false;
-#endif
+static enum TDB_ERROR clear_if_first(int fd, void *arg)
+{
+/* We hold a lock offset 63 always, so we can tell if anyone is holding it. */
+       struct flock fl;
+
+       fl.l_type = F_WRLCK;
+       fl.l_whence = SEEK_SET;
+       fl.l_start = 63;
+       fl.l_len = 1;
+
+       if (fcntl(fd, F_SETLK, &fl) == 0) {
+               /* We must be first ones to open it! */
+               diag("agent truncating file!");
+               if (ftruncate(fd, 0) != 0) {
+                       return TDB_ERR_IO;
+               }
+       }
+       fl.l_type = F_RDLCK;
+       if (fcntl(fd, F_SETLKW, &fl) != 0) {
+               return TDB_ERR_IO;
+       }
+       return TDB_SUCCESS;
+}
 
 static enum agent_return do_operation(enum operation op, const char *name)
 {
        TDB_DATA k;
        enum agent_return ret;
        TDB_DATA data;
+       enum TDB_ERROR ecode;
+       union tdb_attribute cif;
 
-       if (op != OPEN && !tdb) {
+       if (op != OPEN && op != OPEN_WITH_HOOK && !tdb) {
                diag("external: No tdb open!");
                return OTHER_FAILURE;
        }
 
-       k.dptr = (void *)name;
-       k.dsize = strlen(name);
+       diag("external: %s", operation_name(op));
+
+       k = tdb_mkdata(name, strlen(name));
 
        locking_would_block = 0;
        switch (op) {
@@ -46,29 +69,45 @@ static enum agent_return do_operation(enum operation op, const char *name)
                if (!tdb) {
                        if (!locking_would_block)
                                diag("Opening tdb gave %s", strerror(errno));
+                       forget_locking();
+                       ret = OTHER_FAILURE;
+               } else
+                       ret = SUCCESS;
+               break;
+       case OPEN_WITH_HOOK:
+               if (tdb) {
+                       diag("Already have tdb %s open", tdb->name);
+                       return OTHER_FAILURE;
+               }
+               cif.openhook.base.attr = TDB_ATTRIBUTE_OPENHOOK;
+               cif.openhook.base.next = &tap_log_attr;
+               cif.openhook.fn = clear_if_first;
+               tdb = tdb_open(name, TDB_DEFAULT, O_RDWR, 0, &cif);
+               if (!tdb) {
+                       if (!locking_would_block)
+                               diag("Opening tdb gave %s", strerror(errno));
+                       forget_locking();
                        ret = OTHER_FAILURE;
                } else
                        ret = SUCCESS;
                break;
        case FETCH:
-               data = tdb_fetch(tdb, k);
-               if (data.dptr == NULL) {
-                       if (tdb_error(tdb) == TDB_ERR_NOEXIST)
-                               ret = FAILED;
-                       else
-                               ret = OTHER_FAILURE;
-               } else if (data.dsize != k.dsize
-                          || memcmp(data.dptr, k.dptr, k.dsize) != 0) {
+               ecode = tdb_fetch(tdb, k, &data);
+               if (ecode == TDB_ERR_NOEXIST) {
+                       ret = FAILED;
+               } else if (ecode < 0) {
+                       ret = OTHER_FAILURE;
+               } else if (!tdb_deq(data, k)) {
                        ret = OTHER_FAILURE;
+                       free(data.dptr);
                } else {
                        ret = SUCCESS;
+                       free(data.dptr);
                }
-               free(data.dptr);
                break;
        case STORE:
                ret = tdb_store(tdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE;
                break;
-#if 0 /* FIXME */
        case TRANSACTION_START:
                ret = tdb_transaction_start(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
                break;
@@ -78,7 +117,6 @@ static enum agent_return do_operation(enum operation op, const char *name)
        case NEEDS_RECOVERY:
                ret = tdb_needs_recovery(tdb) ? SUCCESS : FAILED;
                break;
-#endif
        case CHECK:
                ret = tdb_check(tdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
                break;
@@ -180,15 +218,21 @@ const char *operation_name(enum operation op)
 {
        switch (op) {
        case OPEN: return "OPEN";
+       case OPEN_WITH_HOOK: return "OPEN_WITH_HOOK";
        case FETCH: return "FETCH";
        case STORE: return "STORE";
        case CHECK: return "CHECK";
-#if 0
        case TRANSACTION_START: return "TRANSACTION_START";
        case TRANSACTION_COMMIT: return "TRANSACTION_COMMIT";
        case NEEDS_RECOVERY: return "NEEDS_RECOVERY";
-#endif
        case CLOSE: return "CLOSE";
        }
        return "**INVALID**";
 }
+
+void free_external_agent(struct agent *agent)
+{
+       close(agent->cmdfd);
+       close(agent->responsefd);
+       free(agent);
+}