| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- #include "external-transaction.h"
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include <err.h>
- #include <fcntl.h>
- #include <stdlib.h>
- #include <limits.h>
- #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)
- {
- 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 };
- 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 == 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;
- tdb_setalarm_sigptr(tdb, &alarmed);
- alarm(1);
- 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);
- if (op != TRANSACTION_KEEP_OPENED) {
- tdb_close(tdb);
- tdb = NULL;
- }
- return 1;
- }
- tdb_delete(tdb, k);
- maybe_alarmed:
- if (op != TRANSACTION_KEEP_OPENED) {
- tdb_close(tdb);
- tdb = NULL;
- }
- if (alarmed)
- return 0;
- return -3;
- }
- struct agent {
- int cmdfd, responsefd;
- };
- /* Do this before doing any tdb stuff. Return handle, or NULL. */
- struct agent *prepare_external_agent(void)
- {
- int pid, ret;
- int command[2], response[2];
- struct sigaction act = { .sa_handler = do_alarm };
- char name[1+PATH_MAX];
- if (pipe(command) != 0 || pipe(response) != 0)
- return NULL;
- pid = fork();
- if (pid < 0)
- return NULL;
- if (pid != 0) {
- struct agent *agent = malloc(sizeof(*agent));
- close(command[0]);
- close(response[1]);
- agent->cmdfd = command[1];
- agent->responsefd = response[0];
- return agent;
- }
- close(command[1]);
- close(response[0]);
- sigaction(SIGALRM, &act, NULL);
- while ((ret = read(command[0], name, sizeof(name))) > 0) {
- int result;
- result = do_operation(name[0], name+1);
- if (write(response[1], &result, sizeof(result))
- != 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. */
- int external_agent_operation(struct agent *agent,
- enum operation op, const char *tdbname)
- {
- int res;
- char string[1 + strlen(tdbname) + 1];
- string[0] = op;
- strcpy(string+1, tdbname);
- if (write(agent->cmdfd, string, sizeof(string)) != sizeof(string))
- err(1, "Writing to agent");
- if (read(agent->responsefd, &res, sizeof(res)) != sizeof(res))
- err(1, "Reading from agent");
- if (res > 1)
- errx(1, "Agent returned %u\n", res);
- return res;
- }
- void external_agent_operation_start(struct agent *agent,
- enum operation op, const char *tdbname)
- {
- char string[1 + strlen(tdbname) + 1];
- string[0] = op;
- strcpy(string+1, tdbname);
- if (write(agent->cmdfd, string, sizeof(string)) != sizeof(string))
- err(1, "Writing to agent");
- }
- bool external_agent_operation_check(struct agent *agent, bool block, int *res)
- {
- int flags = fcntl(agent->responsefd, F_GETFL);
- if (block)
- fcntl(agent->responsefd, F_SETFL, flags & ~O_NONBLOCK);
- else
- fcntl(agent->responsefd, F_SETFL, flags | O_NONBLOCK);
- switch (read(agent->responsefd, res, sizeof(*res))) {
- case sizeof(*res):
- break;
- case 0:
- errx(1, "Agent died?");
- default:
- if (!block && (errno == EAGAIN || errno == EWOULDBLOCK))
- return false;
- err(1, "%slocking reading from agent", block ? "B" : "Non-b");
- }
- if (*res > 1)
- errx(1, "Agent returned %u\n", *res);
- return true;
- }
|