api-fork-test.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /* Test forking while holding lock.
  2. *
  3. * There are only five ways to do this currently:
  4. * (1) grab a tdb_chainlock, then fork.
  5. * (2) grab a tdb_lockall, then fork.
  6. * (3) grab a tdb_lockall_read, then fork.
  7. * (4) start a transaction, then fork.
  8. * (5) fork from inside a tdb_parse() callback.
  9. *
  10. * Note that we don't hold a lock across tdb_traverse callbacks, so
  11. * that doesn't matter.
  12. */
  13. #include <ccan/tdb2/tdb2.h>
  14. #include <ccan/tap/tap.h>
  15. #include <sys/types.h>
  16. #include <sys/stat.h>
  17. #include <fcntl.h>
  18. #include <sys/types.h>
  19. #include <sys/wait.h>
  20. #include <unistd.h>
  21. #include <stdlib.h>
  22. #include "logging.h"
  23. static enum TDB_ERROR fork_in_parse(TDB_DATA key, TDB_DATA data,
  24. struct tdb_context *tdb)
  25. {
  26. int status, extra_messages;
  27. if (tdb_get_flags(tdb) & TDB_VERSION1) {
  28. extra_messages = 1;
  29. } else {
  30. extra_messages = 0;
  31. }
  32. if (fork() == 0) {
  33. /* We expect this to fail. */
  34. if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
  35. exit(1);
  36. tap_log_messages -= extra_messages;
  37. if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
  38. exit(1);
  39. tap_log_messages -= extra_messages;
  40. if (tap_log_messages != 2)
  41. exit(2);
  42. tdb_close(tdb);
  43. if (tap_log_messages != 2)
  44. exit(3);
  45. exit(0);
  46. }
  47. wait(&status);
  48. ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
  49. return TDB_SUCCESS;
  50. }
  51. int main(int argc, char *argv[])
  52. {
  53. unsigned int i;
  54. struct tdb_context *tdb;
  55. int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
  56. TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT,
  57. TDB_VERSION1, TDB_NOMMAP|TDB_VERSION1,
  58. TDB_CONVERT|TDB_VERSION1,
  59. TDB_NOMMAP|TDB_CONVERT|TDB_VERSION1 };
  60. struct tdb_data key = tdb_mkdata("key", 3);
  61. struct tdb_data data = tdb_mkdata("data", 4);
  62. plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
  63. for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
  64. int status, extra_messages;
  65. if (flags[i] & TDB_VERSION1) {
  66. extra_messages = 1;
  67. } else {
  68. extra_messages = 0;
  69. }
  70. tap_log_messages = 0;
  71. tdb = tdb_open("run-fork-test.tdb", flags[i],
  72. O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
  73. if (!ok1(tdb))
  74. continue;
  75. /* Put a record in here. */
  76. ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_SUCCESS);
  77. ok1(tdb_chainlock(tdb, key) == TDB_SUCCESS);
  78. if (fork() == 0) {
  79. /* We expect this to fail. */
  80. if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
  81. return 1;
  82. tap_log_messages -= extra_messages;
  83. if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
  84. return 1;
  85. tap_log_messages -= extra_messages;
  86. if (tap_log_messages != 2)
  87. return 2;
  88. tdb_chainunlock(tdb, key);
  89. if (tap_log_messages != 3)
  90. return 3;
  91. tdb_close(tdb);
  92. if (tap_log_messages != 3)
  93. return 4;
  94. return 0;
  95. }
  96. wait(&status);
  97. ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
  98. tdb_chainunlock(tdb, key);
  99. ok1(tdb_lockall(tdb) == TDB_SUCCESS);
  100. if (fork() == 0) {
  101. /* We expect this to fail. */
  102. if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
  103. return 1;
  104. tap_log_messages -= extra_messages;
  105. if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
  106. return 1;
  107. tap_log_messages -= extra_messages;
  108. if (tap_log_messages != 2)
  109. return 2;
  110. tdb_unlockall(tdb);
  111. if (tap_log_messages != 2)
  112. return 3;
  113. tdb_close(tdb);
  114. if (tap_log_messages != 2)
  115. return 4;
  116. return 0;
  117. }
  118. wait(&status);
  119. ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
  120. tdb_unlockall(tdb);
  121. ok1(tdb_lockall_read(tdb) == TDB_SUCCESS);
  122. if (fork() == 0) {
  123. /* We expect this to fail. */
  124. /* This would always fail anyway... */
  125. if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
  126. return 1;
  127. tap_log_messages -= extra_messages;
  128. if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
  129. return 1;
  130. tap_log_messages -= extra_messages;
  131. if (tap_log_messages != 2)
  132. return 2;
  133. tdb_unlockall_read(tdb);
  134. if (tap_log_messages != 2)
  135. return 3;
  136. tdb_close(tdb);
  137. if (tap_log_messages != 2)
  138. return 4;
  139. return 0;
  140. }
  141. wait(&status);
  142. ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
  143. tdb_unlockall_read(tdb);
  144. ok1(tdb_transaction_start(tdb) == TDB_SUCCESS);
  145. /* If transactions is empty, noop "commit" succeeds. */
  146. ok1(tdb_delete(tdb, key) == TDB_SUCCESS);
  147. if (fork() == 0) {
  148. /* We expect this to fail. */
  149. if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
  150. return 1;
  151. tap_log_messages -= extra_messages;
  152. if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
  153. return 1;
  154. tap_log_messages -= extra_messages;
  155. if (tap_log_messages != 2)
  156. return 2;
  157. if (tdb_transaction_commit(tdb) != TDB_ERR_LOCK)
  158. return 3;
  159. tap_log_messages -= extra_messages;
  160. tdb_close(tdb);
  161. if (tap_log_messages < 3)
  162. return 4;
  163. return 0;
  164. }
  165. wait(&status);
  166. ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
  167. tdb_transaction_cancel(tdb);
  168. ok1(tdb_parse_record(tdb, key, fork_in_parse, tdb)
  169. == TDB_SUCCESS);
  170. tdb_close(tdb);
  171. ok1(tap_log_messages == 0);
  172. }
  173. return exit_status();
  174. }