external-transaction.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #include "external-transaction.h"
  2. #include <sys/types.h>
  3. #include <sys/wait.h>
  4. #include <unistd.h>
  5. #include <err.h>
  6. #include <fcntl.h>
  7. #include <stdlib.h>
  8. #include <limits.h>
  9. #include <string.h>
  10. #include <errno.h>
  11. #include <ccan/tdb/tdb.h>
  12. #include <ccan/tdb/tdb_private.h>
  13. #include <ccan/tap/tap.h>
  14. #include <stdio.h>
  15. #include <stdarg.h>
  16. static struct tdb_context *tdb;
  17. static volatile sig_atomic_t alarmed;
  18. static void do_alarm(int signum)
  19. {
  20. alarmed++;
  21. }
  22. static void taplog(struct tdb_context *tdb,
  23. enum tdb_debug_level level,
  24. const char *fmt, ...)
  25. {
  26. va_list ap;
  27. char line[200];
  28. va_start(ap, fmt);
  29. vsprintf(line, fmt, ap);
  30. va_end(ap);
  31. diag("external: %s", line);
  32. }
  33. static int do_operation(enum operation op, const char *name)
  34. {
  35. struct tdb_logging_context logctx = { taplog, NULL };
  36. TDB_DATA k = { .dptr = (void *)"a", .dsize = 1 };
  37. TDB_DATA d = { .dptr = (void *)"b", .dsize = 1 };
  38. if (op <= KEEP_OPENED) {
  39. tdb = tdb_open_ex(name, 0, op == OPEN_WITH_CLEAR_IF_FIRST ?
  40. TDB_CLEAR_IF_FIRST : TDB_DEFAULT, O_RDWR, 0,
  41. &logctx, NULL);
  42. if (!tdb)
  43. return -1;
  44. }
  45. if (op == KEEP_OPENED) {
  46. return 0;
  47. } else if (op == OPEN || op == OPEN_WITH_CLEAR_IF_FIRST || op == CLOSE) {
  48. tdb_close(tdb);
  49. tdb = NULL;
  50. return 1;
  51. } else if (op == STORE_KEEP_OPENED) {
  52. if (tdb_store(tdb, k, d, 0) != 0)
  53. return -2;
  54. return 1;
  55. } else if (op == FETCH_KEEP_OPENED) {
  56. TDB_DATA ret;
  57. ret = tdb_fetch(tdb, k);
  58. if (ret.dptr == NULL) {
  59. if (tdb_error(tdb) == TDB_ERR_NOEXIST)
  60. return 1;
  61. return -3;
  62. }
  63. if (ret.dsize != 1 || *(char *)ret.dptr != 'b')
  64. return -4;
  65. free(ret.dptr);
  66. return 1;
  67. } else if (op == CHECK_KEEP_OPENED) {
  68. return tdb_check(tdb, NULL, 0) == 0;
  69. } else if (op == NEEDS_RECOVERY_KEEP_OPENED) {
  70. return tdb_needs_recovery(tdb);
  71. }
  72. alarmed = 0;
  73. tdb_setalarm_sigptr(tdb, &alarmed);
  74. alarm(1);
  75. if (tdb_transaction_start(tdb) != 0)
  76. goto maybe_alarmed;
  77. alarm(0);
  78. if (tdb_store(tdb, k, d, 0) != 0) {
  79. tdb_transaction_cancel(tdb);
  80. tdb_close(tdb);
  81. tdb = NULL;
  82. return -2;
  83. }
  84. if (tdb_transaction_commit(tdb) == 0) {
  85. tdb_delete(tdb, k);
  86. if (op != TRANSACTION_KEEP_OPENED) {
  87. tdb_close(tdb);
  88. tdb = NULL;
  89. }
  90. return 1;
  91. }
  92. tdb_delete(tdb, k);
  93. maybe_alarmed:
  94. if (op != TRANSACTION_KEEP_OPENED) {
  95. tdb_close(tdb);
  96. tdb = NULL;
  97. }
  98. if (alarmed)
  99. return 0;
  100. return -3;
  101. }
  102. struct agent {
  103. int cmdfd, responsefd;
  104. };
  105. /* Do this before doing any tdb stuff. Return handle, or NULL. */
  106. struct agent *prepare_external_agent(void)
  107. {
  108. int pid, ret;
  109. int command[2], response[2];
  110. struct sigaction act = { .sa_handler = do_alarm };
  111. char name[1+PATH_MAX];
  112. if (pipe(command) != 0 || pipe(response) != 0)
  113. return NULL;
  114. pid = fork();
  115. if (pid < 0)
  116. return NULL;
  117. if (pid != 0) {
  118. struct agent *agent = malloc(sizeof(*agent));
  119. close(command[0]);
  120. close(response[1]);
  121. agent->cmdfd = command[1];
  122. agent->responsefd = response[0];
  123. return agent;
  124. }
  125. close(command[1]);
  126. close(response[0]);
  127. sigaction(SIGALRM, &act, NULL);
  128. while ((ret = read(command[0], name, sizeof(name))) > 0) {
  129. int result;
  130. result = do_operation(name[0], name+1);
  131. if (write(response[1], &result, sizeof(result))
  132. != sizeof(result))
  133. err(1, "Writing response");
  134. }
  135. diag("external: read %i: %s", ret, strerror(errno));
  136. exit(0);
  137. }
  138. /* Ask the external agent to try to do an operation. */
  139. int external_agent_operation(struct agent *agent,
  140. enum operation op, const char *tdbname)
  141. {
  142. int res;
  143. char string[1 + strlen(tdbname) + 1];
  144. string[0] = op;
  145. strcpy(string+1, tdbname);
  146. if (write(agent->cmdfd, string, sizeof(string)) != sizeof(string))
  147. err(1, "Writing to agent");
  148. if (read(agent->responsefd, &res, sizeof(res)) != sizeof(res))
  149. err(1, "Reading from agent");
  150. if (res > 1)
  151. errx(1, "Agent returned %u\n", res);
  152. return res;
  153. }
  154. void external_agent_operation_start(struct agent *agent,
  155. enum operation op, const char *tdbname)
  156. {
  157. char string[1 + strlen(tdbname) + 1];
  158. string[0] = op;
  159. strcpy(string+1, tdbname);
  160. if (write(agent->cmdfd, string, sizeof(string)) != sizeof(string))
  161. err(1, "Writing to agent");
  162. }
  163. bool external_agent_operation_check(struct agent *agent, bool block, int *res)
  164. {
  165. int flags = fcntl(agent->responsefd, F_GETFL);
  166. if (block)
  167. fcntl(agent->responsefd, F_SETFL, flags & ~O_NONBLOCK);
  168. else
  169. fcntl(agent->responsefd, F_SETFL, flags | O_NONBLOCK);
  170. switch (read(agent->responsefd, res, sizeof(*res))) {
  171. case sizeof(*res):
  172. break;
  173. case 0:
  174. errx(1, "Agent died?");
  175. default:
  176. if (!block && (errno == EAGAIN || errno == EWOULDBLOCK))
  177. return false;
  178. err(1, "%slocking reading from agent", block ? "B" : "Non-b");
  179. }
  180. if (*res > 1)
  181. errx(1, "Agent returned %u\n", *res);
  182. return true;
  183. }