#include <limits.h>
#include <string.h>
#include <errno.h>
-#include <ccan/tdb2/private.h>
+#include <ccan/tdb2/tdb1_private.h>
#include <ccan/tap/tap.h>
#include <stdio.h>
#include <stdarg.h>
static struct tdb_context *tdb;
+void (*external_agent_free)(void *) = free;
+
+static enum TDB_ERROR clear_if_first(int fd, void *arg)
+{
+/* We hold a lock offset 4 always, so we can tell if anyone is holding it.
+ * (This is compatible with tdb1's TDB_CLEAR_IF_FIRST flag). */
+ struct flock fl;
+
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 4;
+ 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;
}
} 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:
ecode = tdb_fetch(tdb, k, &data);
if (ecode == TDB_ERR_NOEXIST) {
ret = OTHER_FAILURE;
} else if (!tdb_deq(data, k)) {
ret = OTHER_FAILURE;
- free(data.dptr);
+ external_agent_free(data.dptr);
} else {
ret = SUCCESS;
- free(data.dptr);
+ external_agent_free(data.dptr);
}
break;
case STORE:
ret = tdb_transaction_commit(tdb)==0 ? SUCCESS : OTHER_FAILURE;
break;
case NEEDS_RECOVERY:
- ret = tdb_needs_recovery(tdb) ? SUCCESS : FAILED;
+ if (tdb->flags & TDB_VERSION1)
+ ret = tdb1_needs_recovery(tdb) ? SUCCESS : FAILED;
+ else
+ ret = tdb_needs_recovery(tdb) ? SUCCESS : FAILED;
break;
case CHECK:
ret = tdb_check(tdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
ret = tdb_close(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
tdb = NULL;
break;
+ case SEND_SIGNAL:
+ /* We do this async */
+ ret = SUCCESS;
+ break;
default:
ret = OTHER_FAILURE;
}
if (write(response[1], &result, sizeof(result))
!= sizeof(result))
err(1, "Writing response");
+ if (name[0] == SEND_SIGNAL) {
+ struct timeval ten_ms;
+ ten_ms.tv_sec = 0;
+ ten_ms.tv_usec = 10000;
+ select(0, NULL, NULL, NULL, &ten_ms);
+ kill(getppid(), SIGUSR1);
+ }
}
exit(0);
}
{
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";
case TRANSACTION_START: return "TRANSACTION_START";
case TRANSACTION_COMMIT: return "TRANSACTION_COMMIT";
case NEEDS_RECOVERY: return "NEEDS_RECOVERY";
+ case SEND_SIGNAL: return "SEND_SIGNAL";
case CLOSE: return "CLOSE";
}
return "**INVALID**";