external-agent.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. #include "external-agent.h"
  2. #include "logging.h"
  3. #include "lock-tracking.h"
  4. #include <sys/types.h>
  5. #include <sys/wait.h>
  6. #include <unistd.h>
  7. #include <ccan/err/err.h>
  8. #include <fcntl.h>
  9. #include <stdlib.h>
  10. #include <limits.h>
  11. #include <string.h>
  12. #include <errno.h>
  13. #include "tap-interface.h"
  14. #include <stdio.h>
  15. #include <stdarg.h>
  16. static struct ntdb_context *ntdb;
  17. void (*external_agent_free)(void *) = free;
  18. static enum NTDB_ERROR clear_if_first(int fd, void *arg)
  19. {
  20. /* We hold a lock offset 4 always, so we can tell if anyone is holding it.
  21. * (This is compatible with tdb's TDB_CLEAR_IF_FIRST flag). */
  22. struct flock fl;
  23. fl.l_type = F_WRLCK;
  24. fl.l_whence = SEEK_SET;
  25. fl.l_start = 4;
  26. fl.l_len = 1;
  27. if (fcntl(fd, F_SETLK, &fl) == 0) {
  28. /* We must be first ones to open it! */
  29. diag("agent truncating file!");
  30. if (ftruncate(fd, 0) != 0) {
  31. return NTDB_ERR_IO;
  32. }
  33. }
  34. fl.l_type = F_RDLCK;
  35. if (fcntl(fd, F_SETLKW, &fl) != 0) {
  36. return NTDB_ERR_IO;
  37. }
  38. return NTDB_SUCCESS;
  39. }
  40. static enum agent_return do_operation(enum operation op, const char *name)
  41. {
  42. NTDB_DATA k, d;
  43. enum agent_return ret;
  44. NTDB_DATA data;
  45. enum NTDB_ERROR ecode;
  46. union ntdb_attribute cif;
  47. const char *eq;
  48. if (op != OPEN && op != OPEN_WITH_HOOK && !ntdb) {
  49. diag("external: No ntdb open!");
  50. return OTHER_FAILURE;
  51. }
  52. diag("external: %s", operation_name(op));
  53. eq = strchr(name, '=');
  54. if (eq) {
  55. k = ntdb_mkdata(name, eq - name);
  56. d = ntdb_mkdata(eq + 1, strlen(eq+1));
  57. } else {
  58. k = ntdb_mkdata(name, strlen(name));
  59. d.dsize = 0;
  60. d.dptr = NULL;
  61. }
  62. locking_would_block = 0;
  63. switch (op) {
  64. case OPEN:
  65. if (ntdb) {
  66. diag("Already have ntdb %s open", ntdb_name(ntdb));
  67. return OTHER_FAILURE;
  68. }
  69. ntdb = ntdb_open(name, MAYBE_NOSYNC, O_RDWR, 0, &tap_log_attr);
  70. if (!ntdb) {
  71. if (!locking_would_block)
  72. diag("Opening ntdb gave %s", strerror(errno));
  73. forget_locking();
  74. ret = OTHER_FAILURE;
  75. } else
  76. ret = SUCCESS;
  77. break;
  78. case OPEN_WITH_HOOK:
  79. if (ntdb) {
  80. diag("Already have ntdb %s open", ntdb_name(ntdb));
  81. return OTHER_FAILURE;
  82. }
  83. cif.openhook.base.attr = NTDB_ATTRIBUTE_OPENHOOK;
  84. cif.openhook.base.next = &tap_log_attr;
  85. cif.openhook.fn = clear_if_first;
  86. ntdb = ntdb_open(name, MAYBE_NOSYNC, O_RDWR, 0, &cif);
  87. if (!ntdb) {
  88. if (!locking_would_block)
  89. diag("Opening ntdb gave %s", strerror(errno));
  90. forget_locking();
  91. ret = OTHER_FAILURE;
  92. } else
  93. ret = SUCCESS;
  94. break;
  95. case FETCH:
  96. ecode = ntdb_fetch(ntdb, k, &data);
  97. if (ecode == NTDB_ERR_NOEXIST) {
  98. ret = FAILED;
  99. } else if (ecode < 0) {
  100. ret = OTHER_FAILURE;
  101. } else if (!ntdb_deq(data, d)) {
  102. ret = OTHER_FAILURE;
  103. external_agent_free(data.dptr);
  104. } else {
  105. ret = SUCCESS;
  106. external_agent_free(data.dptr);
  107. }
  108. break;
  109. case STORE:
  110. ret = ntdb_store(ntdb, k, d, 0) == 0 ? SUCCESS : OTHER_FAILURE;
  111. break;
  112. case TRANSACTION_START:
  113. ret = ntdb_transaction_start(ntdb) == 0 ? SUCCESS : OTHER_FAILURE;
  114. break;
  115. case TRANSACTION_COMMIT:
  116. ret = ntdb_transaction_commit(ntdb)==0 ? SUCCESS : OTHER_FAILURE;
  117. break;
  118. case NEEDS_RECOVERY:
  119. ret = external_agent_needs_rec(ntdb);
  120. break;
  121. case CHECK:
  122. ret = ntdb_check(ntdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
  123. break;
  124. case CLOSE:
  125. ret = ntdb_close(ntdb) == 0 ? SUCCESS : OTHER_FAILURE;
  126. ntdb = NULL;
  127. break;
  128. case SEND_SIGNAL:
  129. /* We do this async */
  130. ret = SUCCESS;
  131. break;
  132. default:
  133. ret = OTHER_FAILURE;
  134. }
  135. if (locking_would_block)
  136. ret = WOULD_HAVE_BLOCKED;
  137. return ret;
  138. }
  139. struct agent {
  140. int cmdfd, responsefd;
  141. };
  142. /* Do this before doing any ntdb stuff. Return handle, or NULL. */
  143. struct agent *prepare_external_agent(void)
  144. {
  145. int pid, ret;
  146. int command[2], response[2];
  147. char name[1+PATH_MAX];
  148. if (pipe(command) != 0 || pipe(response) != 0)
  149. return NULL;
  150. pid = fork();
  151. if (pid < 0)
  152. return NULL;
  153. if (pid != 0) {
  154. struct agent *agent = malloc(sizeof(*agent));
  155. close(command[0]);
  156. close(response[1]);
  157. agent->cmdfd = command[1];
  158. agent->responsefd = response[0];
  159. return agent;
  160. }
  161. close(command[1]);
  162. close(response[0]);
  163. /* We want to fail, not block. */
  164. nonblocking_locks = true;
  165. log_prefix = "external: ";
  166. while ((ret = read(command[0], name, sizeof(name))) > 0) {
  167. enum agent_return result;
  168. result = do_operation(name[0], name+1);
  169. if (write(response[1], &result, sizeof(result))
  170. != sizeof(result))
  171. err(1, "Writing response");
  172. if (name[0] == SEND_SIGNAL) {
  173. struct timeval ten_ms;
  174. ten_ms.tv_sec = 0;
  175. ten_ms.tv_usec = 10000;
  176. select(0, NULL, NULL, NULL, &ten_ms);
  177. kill(getppid(), SIGUSR1);
  178. }
  179. }
  180. exit(0);
  181. }
  182. /* Ask the external agent to try to do an operation. */
  183. enum agent_return external_agent_operation(struct agent *agent,
  184. enum operation op,
  185. const char *name)
  186. {
  187. enum agent_return res;
  188. unsigned int len;
  189. char *string;
  190. if (!name)
  191. name = "";
  192. len = 1 + strlen(name) + 1;
  193. string = malloc(len);
  194. string[0] = op;
  195. strcpy(string+1, name);
  196. if (write(agent->cmdfd, string, len) != len
  197. || read(agent->responsefd, &res, sizeof(res)) != sizeof(res))
  198. res = AGENT_DIED;
  199. free(string);
  200. return res;
  201. }
  202. const char *agent_return_name(enum agent_return ret)
  203. {
  204. return ret == SUCCESS ? "SUCCESS"
  205. : ret == WOULD_HAVE_BLOCKED ? "WOULD_HAVE_BLOCKED"
  206. : ret == AGENT_DIED ? "AGENT_DIED"
  207. : ret == FAILED ? "FAILED"
  208. : ret == OTHER_FAILURE ? "OTHER_FAILURE"
  209. : "**INVALID**";
  210. }
  211. const char *operation_name(enum operation op)
  212. {
  213. switch (op) {
  214. case OPEN: return "OPEN";
  215. case OPEN_WITH_HOOK: return "OPEN_WITH_HOOK";
  216. case FETCH: return "FETCH";
  217. case STORE: return "STORE";
  218. case CHECK: return "CHECK";
  219. case TRANSACTION_START: return "TRANSACTION_START";
  220. case TRANSACTION_COMMIT: return "TRANSACTION_COMMIT";
  221. case NEEDS_RECOVERY: return "NEEDS_RECOVERY";
  222. case SEND_SIGNAL: return "SEND_SIGNAL";
  223. case CLOSE: return "CLOSE";
  224. }
  225. return "**INVALID**";
  226. }
  227. void free_external_agent(struct agent *agent)
  228. {
  229. close(agent->cmdfd);
  230. close(agent->responsefd);
  231. free(agent);
  232. }