zookeeper-ng 1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/.ctags_paths +1 -0
  3. data/.dotfiles/ruby-gemset +1 -0
  4. data/.dotfiles/ruby-version +1 -0
  5. data/.dotfiles/rvmrc +2 -0
  6. data/.gitignore +19 -0
  7. data/.gitmodules +3 -0
  8. data/.travis.yml +25 -0
  9. data/CHANGELOG +395 -0
  10. data/Gemfile +30 -0
  11. data/Guardfile +8 -0
  12. data/LICENSE +23 -0
  13. data/Manifest +29 -0
  14. data/README.markdown +85 -0
  15. data/Rakefile +121 -0
  16. data/cause-abort.rb +117 -0
  17. data/ext/.gitignore +6 -0
  18. data/ext/Rakefile +41 -0
  19. data/ext/c_zookeeper.rb +398 -0
  20. data/ext/common.h +17 -0
  21. data/ext/dbg.h +53 -0
  22. data/ext/depend +5 -0
  23. data/ext/event_lib.c +740 -0
  24. data/ext/event_lib.h +175 -0
  25. data/ext/extconf.rb +103 -0
  26. data/ext/generate_gvl_code.rb +321 -0
  27. data/ext/patches/zkc-3.3.5-network.patch +24 -0
  28. data/ext/patches/zkc-3.4.5-fetch-and-add.patch +16 -0
  29. data/ext/patches/zkc-3.4.5-logging.patch +41 -0
  30. data/ext/patches/zkc-3.4.5-out-of-order-ping.patch +163 -0
  31. data/ext/patches/zkc-3.4.5-overflow.patch +11 -0
  32. data/ext/patches/zkc-3.4.5-yosemite-htonl-fix.patch +102 -0
  33. data/ext/zkc-3.4.5.tar.gz +0 -0
  34. data/ext/zkrb.c +1075 -0
  35. data/ext/zkrb_wrapper.c +775 -0
  36. data/ext/zkrb_wrapper.h +350 -0
  37. data/ext/zkrb_wrapper_compat.c +15 -0
  38. data/ext/zkrb_wrapper_compat.h +11 -0
  39. data/ext/zookeeper_base.rb +256 -0
  40. data/java/java_base.rb +503 -0
  41. data/lib/zookeeper.rb +115 -0
  42. data/lib/zookeeper/acls.rb +44 -0
  43. data/lib/zookeeper/callbacks.rb +108 -0
  44. data/lib/zookeeper/client.rb +30 -0
  45. data/lib/zookeeper/client_methods.rb +282 -0
  46. data/lib/zookeeper/common.rb +122 -0
  47. data/lib/zookeeper/common/queue_with_pipe.rb +110 -0
  48. data/lib/zookeeper/compatibility.rb +138 -0
  49. data/lib/zookeeper/constants.rb +97 -0
  50. data/lib/zookeeper/continuation.rb +223 -0
  51. data/lib/zookeeper/core_ext.rb +58 -0
  52. data/lib/zookeeper/em_client.rb +55 -0
  53. data/lib/zookeeper/exceptions.rb +135 -0
  54. data/lib/zookeeper/forked.rb +19 -0
  55. data/lib/zookeeper/latch.rb +34 -0
  56. data/lib/zookeeper/logger.rb +39 -0
  57. data/lib/zookeeper/logger/forwarding_logger.rb +84 -0
  58. data/lib/zookeeper/monitor.rb +19 -0
  59. data/lib/zookeeper/rake_tasks.rb +165 -0
  60. data/lib/zookeeper/request_registry.rb +153 -0
  61. data/lib/zookeeper/stat.rb +21 -0
  62. data/lib/zookeeper/version.rb +4 -0
  63. data/notes.txt +14 -0
  64. data/scripts/upgrade-1.0-sed-alike.rb +46 -0
  65. data/spec/c_zookeeper_spec.rb +51 -0
  66. data/spec/chrooted_connection_spec.rb +83 -0
  67. data/spec/compatibilty_spec.rb +8 -0
  68. data/spec/default_watcher_spec.rb +41 -0
  69. data/spec/em_spec.rb +51 -0
  70. data/spec/ext/zookeeper_base_spec.rb +19 -0
  71. data/spec/forked_connection_spec.rb +124 -0
  72. data/spec/latch_spec.rb +24 -0
  73. data/spec/log4j.properties +17 -0
  74. data/spec/shared/all_success_return_values.rb +10 -0
  75. data/spec/shared/connection_examples.rb +1077 -0
  76. data/spec/spec_helper.rb +61 -0
  77. data/spec/support/00_logging.rb +38 -0
  78. data/spec/support/10_spawn_zookeeper.rb +24 -0
  79. data/spec/support/progress_formatter.rb +15 -0
  80. data/spec/support/zookeeper_spec_helpers.rb +96 -0
  81. data/spec/zookeeper_spec.rb +24 -0
  82. data/zookeeper.gemspec +38 -0
  83. data/zoomonkey/duplicates +3 -0
  84. data/zoomonkey/zoomonkey.rb +194 -0
  85. metadata +157 -0
@@ -0,0 +1,24 @@
1
+ --- c/src/zookeeper.c.orig 2013-04-22 16:20:14.000000000 -0700
2
+ +++ c/src/zookeeper.c 2013-04-22 16:21:36.000000000 -0700
3
+ @@ -413,7 +413,9 @@
4
+ static int getaddrinfo_errno(int rc) {
5
+ switch(rc) {
6
+ case EAI_NONAME:
7
+ +#ifdef EAI_NODATA
8
+ case EAI_NODATA:
9
+ +#endif
10
+ return ENOENT;
11
+ case EAI_MEMORY:
12
+ return ENOMEM;
13
+ @@ -546,7 +548,11 @@
14
+ //EAI_BADFLAGS or EAI_ADDRFAMILY with AF_UNSPEC and
15
+ // ai_flags as AI_ADDRCONFIG
16
+ if ((hints.ai_flags == AI_ADDRCONFIG) &&
17
+ +#ifdef EAI_ADDRFAMILY
18
+ ((rc ==EAI_BADFLAGS) || (rc == EAI_ADDRFAMILY))) {
19
+ +#else
20
+ + (rc ==EAI_BADFLAGS)) {
21
+ +#endif
22
+ //reset ai_flags to null
23
+ hints.ai_flags = 0;
24
+ //retry getaddrinfo
@@ -0,0 +1,16 @@
1
+ diff -ur zkc-3.4.5-orig/c/src/mt_adaptor.c zkc-3.4.5/c/src/mt_adaptor.c
2
+ --- zkc-3.4.5-orig/c/src/mt_adaptor.c 2012-09-30 10:53:32.000000000 -0700
3
+ +++ zkc-3.4.5/c/src/mt_adaptor.c 2016-09-07 16:55:13.787553837 -0700
4
+ @@ -484,11 +484,7 @@
5
+ {
6
+ #ifndef WIN32
7
+ int32_t result;
8
+ - asm __volatile__(
9
+ - "lock xaddl %0,%1\n"
10
+ - : "=r"(result), "=m"(*(int *)operand)
11
+ - : "0"(incr)
12
+ - : "memory");
13
+ + result = __sync_fetch_and_add(operand, incr);
14
+ return result;
15
+ #else
16
+ volatile int32_t result;
@@ -0,0 +1,41 @@
1
+ diff -ur zkc-3.4.5-orig/c/src/zookeeper.c zkc-3.4.5/c/src/zookeeper.c
2
+ --- zkc-3.4.5-orig/c/src/zookeeper.c 2012-09-30 10:53:32.000000000 -0700
3
+ +++ zkc-3.4.5/c/src/zookeeper.c 2013-09-07 21:25:24.000000000 -0700
4
+ @@ -1650,14 +1650,16 @@
5
+ // a PING
6
+ if (zh->state==ZOO_CONNECTED_STATE) {
7
+ send_to = zh->recv_timeout/3 - idle_send;
8
+ - if (send_to <= 0 && zh->sent_requests.head==0) {
9
+ -// LOG_DEBUG(("Sending PING to %s (exceeded idle by %dms)",
10
+ -// format_current_endpoint_info(zh),-send_to));
11
+ - int rc=send_ping(zh);
12
+ - if (rc < 0){
13
+ - LOG_ERROR(("failed to send PING request (zk retcode=%d)",rc));
14
+ - return api_epilog(zh,rc);
15
+ - }
16
+ + if (send_to <= 0) {
17
+ + if (zh->sent_requests.head==0) {
18
+ + LOG_DEBUG(("Sending PING to %s (exceeded idle by %dms)",
19
+ + format_current_endpoint_info(zh),-send_to));
20
+ + int rc=send_ping(zh);
21
+ + if (rc < 0){
22
+ + LOG_ERROR(("failed to send PING request (zk retcode=%d)",rc));
23
+ + return api_epilog(zh,rc);
24
+ + }
25
+ + }
26
+ send_to = zh->recv_timeout/3;
27
+ }
28
+ }
29
+ @@ -1669,6 +1671,12 @@
30
+ zh->next_deadline.tv_sec += zh->next_deadline.tv_usec / 1000000;
31
+ zh->next_deadline.tv_usec = zh->next_deadline.tv_usec % 1000000;
32
+ }
33
+ +
34
+ + if (tv->tv_sec == 0 && tv->tv_usec == 0) {
35
+ + LOG_DEBUG(("Returning a 0.0 timeval: state=%d idle_recv=%d idle_send=%d recv_to=%d send_to=%d send_requests=%s",
36
+ + zh->state, idle_recv, idle_send, recv_to, send_to, zh->sent_requests.head==0 ? "false" : "true"));
37
+ + }
38
+ +
39
+ *interest = ZOOKEEPER_READ;
40
+ /* we are interested in a write if we are connected and have something
41
+ * to send, or we are waiting for a connect to finish. */
@@ -0,0 +1,163 @@
1
+ diff --git zkc-3.4.5-orig/c/src/zookeeper.c zkc-3.4.5/c/src/zookeeper.c
2
+ index de58c62..2347ff4 100644
3
+ --- zkc-3.4.5-orig/c/src/zookeeper.c
4
+ +++ zkc-3.4.5/c/src/zookeeper.c
5
+ @@ -1167,25 +1167,20 @@ void free_completions(zhandle_t *zh,int callCompletion,int reason)
6
+ zh->outstanding_sync--;
7
+ destroy_completion_entry(cptr);
8
+ } else if (callCompletion) {
9
+ - if(cptr->xid == PING_XID){
10
+ - // Nothing to do with a ping response
11
+ - destroy_completion_entry(cptr);
12
+ - } else {
13
+ - // Fake the response
14
+ - buffer_list_t *bptr;
15
+ - h.xid = cptr->xid;
16
+ - h.zxid = -1;
17
+ - h.err = reason;
18
+ - oa = create_buffer_oarchive();
19
+ - serialize_ReplyHeader(oa, "header", &h);
20
+ - bptr = calloc(sizeof(*bptr), 1);
21
+ - assert(bptr);
22
+ - bptr->len = get_buffer_len(oa);
23
+ - bptr->buffer = get_buffer(oa);
24
+ - close_buffer_oarchive(&oa, 0);
25
+ - cptr->buffer = bptr;
26
+ - queue_completion(&zh->completions_to_process, cptr, 0);
27
+ - }
28
+ + // Fake the response
29
+ + buffer_list_t *bptr;
30
+ + h.xid = cptr->xid;
31
+ + h.zxid = -1;
32
+ + h.err = reason;
33
+ + oa = create_buffer_oarchive();
34
+ + serialize_ReplyHeader(oa, "header", &h);
35
+ + bptr = calloc(sizeof(*bptr), 1);
36
+ + assert(bptr);
37
+ + bptr->len = get_buffer_len(oa);
38
+ + bptr->buffer = get_buffer(oa);
39
+ + close_buffer_oarchive(&oa, 0);
40
+ + cptr->buffer = bptr;
41
+ + queue_completion(&zh->completions_to_process, cptr, 0);
42
+ }
43
+ }
44
+ a_list.completion = NULL;
45
+ @@ -1526,7 +1521,6 @@ static struct timeval get_timeval(int interval)
46
+ rc = serialize_RequestHeader(oa, "header", &h);
47
+ enter_critical(zh);
48
+ gettimeofday(&zh->last_ping, 0);
49
+ - rc = rc < 0 ? rc : add_void_completion(zh, h.xid, 0, 0);
50
+ rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
51
+ get_buffer_len(oa));
52
+ leave_critical(zh);
53
+ @@ -2063,12 +2057,8 @@ static void deserialize_response(int type, int xid, int failed, int rc, completi
54
+ case COMPLETION_VOID:
55
+ LOG_DEBUG(("Calling COMPLETION_VOID for xid=%#x failed=%d rc=%d",
56
+ cptr->xid, failed, rc));
57
+ - if (xid == PING_XID) {
58
+ - // We want to skip the ping
59
+ - } else {
60
+ - assert(cptr->c.void_result);
61
+ - cptr->c.void_result(rc, cptr->data);
62
+ - }
63
+ + assert(cptr->c.void_result);
64
+ + cptr->c.void_result(rc, cptr->data);
65
+ break;
66
+ case COMPLETION_MULTI:
67
+ LOG_DEBUG(("Calling COMPLETION_MULTI for xid=%#x failed=%d rc=%d",
68
+ @@ -2184,7 +2174,15 @@ int zookeeper_process(zhandle_t *zh, int events)
69
+ // fprintf(stderr, "Got %#x for %#x\n", hdr.zxid, hdr.xid);
70
+ }
71
+
72
+ - if (hdr.xid == WATCHER_EVENT_XID) {
73
+ + if (hdr.xid == PING_XID) {
74
+ + // Ping replies can arrive out-of-order
75
+ + int elapsed = 0;
76
+ + struct timeval now;
77
+ + gettimeofday(&now, 0);
78
+ + elapsed = calculate_interval(&zh->last_ping, &now);
79
+ + LOG_DEBUG(("Got ping response in %d ms", elapsed));
80
+ + free_buffer(bptr);
81
+ + } else if (hdr.xid == WATCHER_EVENT_XID) {
82
+ struct WatcherEvent evt;
83
+ int type = 0;
84
+ char *path = NULL;
85
+ @@ -2250,22 +2248,9 @@ int zookeeper_process(zhandle_t *zh, int events)
86
+ activateWatcher(zh, cptr->watcher, rc);
87
+
88
+ if (cptr->c.void_result != SYNCHRONOUS_MARKER) {
89
+ - if(hdr.xid == PING_XID){
90
+ - int elapsed = 0;
91
+ - struct timeval now;
92
+ - gettimeofday(&now, 0);
93
+ - elapsed = calculate_interval(&zh->last_ping, &now);
94
+ - LOG_DEBUG(("Got ping response in %d ms", elapsed));
95
+ -
96
+ - // Nothing to do with a ping response
97
+ - free_buffer(bptr);
98
+ - destroy_completion_entry(cptr);
99
+ - } else {
100
+ - LOG_DEBUG(("Queueing asynchronous response"));
101
+ -
102
+ - cptr->buffer = bptr;
103
+ - queue_completion(&zh->completions_to_process, cptr, 0);
104
+ - }
105
+ + LOG_DEBUG(("Queueing asynchronous response"));
106
+ + cptr->buffer = bptr;
107
+ + queue_completion(&zh->completions_to_process, cptr, 0);
108
+ } else {
109
+ struct sync_completion
110
+ *sc = (struct sync_completion*)cptr->data;
111
+ diff --git zkc-3.4.5-orig/c/tests/TestOperations.cc zkc-3.4.5/c/tests/TestOperations.cc
112
+ index b0370e9..27d9270 100644
113
+ --- zkc-3.4.5-orig/c/tests/TestOperations.cc
114
+ +++ zkc-3.4.5/c/tests/TestOperations.cc
115
+ @@ -29,6 +29,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture
116
+ CPPUNIT_TEST_SUITE(Zookeeper_operations);
117
+ #ifndef THREADED
118
+ CPPUNIT_TEST(testPing);
119
+ + CPPUNIT_TEST(testUnsolicitedPing);
120
+ CPPUNIT_TEST(testTimeoutCausedByWatches1);
121
+ CPPUNIT_TEST(testTimeoutCausedByWatches2);
122
+ #else
123
+ @@ -305,6 +306,40 @@ public:
124
+ CPPUNIT_ASSERT_EQUAL(1,zkServer.pingCount_);
125
+ }
126
+
127
+ + // ZOOKEEPER-2253: Permit unsolicited pings
128
+ + void testUnsolicitedPing()
129
+ + {
130
+ + const int TIMEOUT=9; // timeout in secs
131
+ + Mock_gettimeofday timeMock;
132
+ + PingCountingServer zkServer;
133
+ + // must call zookeeper_close() while all the mocks are in scope
134
+ + CloseFinally guard(&zh);
135
+ +
136
+ + // receive timeout is in milliseconds
137
+ + zh=zookeeper_init("localhost:1234",watcher,TIMEOUT*1000,TEST_CLIENT_ID,0,0);
138
+ + CPPUNIT_ASSERT(zh!=0);
139
+ + // simulate connected state
140
+ + forceConnected(zh);
141
+ +
142
+ + int fd=0;
143
+ + int interest=0;
144
+ + timeval tv;
145
+ +
146
+ + int rc=zookeeper_interest(zh,&fd,&interest,&tv);
147
+ + CPPUNIT_ASSERT_EQUAL((int)ZOK,rc);
148
+ +
149
+ + // verify no ping sent
150
+ + CPPUNIT_ASSERT(zkServer.pingCount_==0);
151
+ +
152
+ + // we're going to receive a unsolicited PING response; ensure
153
+ + // that the client has updated its last_recv timestamp
154
+ + timeMock.tick(tv);
155
+ + zkServer.addRecvResponse(new PingResponse);
156
+ + rc=zookeeper_process(zh,interest);
157
+ + CPPUNIT_ASSERT_EQUAL((int)ZOK,rc);
158
+ + CPPUNIT_ASSERT(timeMock==zh->last_recv);
159
+ + }
160
+ +
161
+ // simulate a watch arriving right before a ping is due
162
+ // assert the ping is sent nevertheless
163
+ void testTimeoutCausedByWatches1()
@@ -0,0 +1,11 @@
1
+ --- zkc-3.4.5-orig/c/src/zookeeper.c 2020-12-01 14:16:08.000000000 +0100
2
+ +++ zkc-3.4.5/c/src/zookeeper.c 2020-12-01 14:16:08.000000000 +0100
3
+ @@ -3411,7 +3411,7 @@
4
+
5
+ static const char* format_endpoint_info(const struct sockaddr_storage* ep)
6
+ {
7
+ - static char buf[128];
8
+ + static char buf[134] = { 0 };
9
+ char addrstr[128];
10
+ void *inaddr;
11
+ #ifdef WIN32
@@ -0,0 +1,102 @@
1
+ diff -ur zkc-3.4.5-orig/c/include/recordio.h zkc-3.4.5/c/include/recordio.h
2
+ --- zkc-3.4.5-orig/c/include/recordio.h 2012-09-30 13:53:32.000000000 -0400
3
+ +++ zkc-3.4.5/c/include/recordio.h 2014-07-29 03:13:27.000000000 -0400
4
+ @@ -73,7 +73,7 @@
5
+ char *get_buffer(struct oarchive *);
6
+ int get_buffer_len(struct oarchive *);
7
+
8
+ -int64_t htonll(int64_t v);
9
+ +int64_t zk_htonll(int64_t v);
10
+
11
+ #ifdef __cplusplus
12
+ }
13
+ diff -ur zkc-3.4.5-orig/c/src/recordio.c zkc-3.4.5/c/src/recordio.c
14
+ --- zkc-3.4.5-orig/c/src/recordio.c 2012-09-30 13:53:32.000000000 -0400
15
+ +++ zkc-3.4.5/c/src/recordio.c 2014-07-29 03:13:35.000000000 -0400
16
+ @@ -80,7 +80,7 @@
17
+ priv->off+=sizeof(i);
18
+ return 0;
19
+ }
20
+ -int64_t htonll(int64_t v)
21
+ +int64_t zk_htonll(int64_t v)
22
+ {
23
+ int i = 0;
24
+ char *s = (char *)&v;
25
+ @@ -98,7 +98,7 @@
26
+
27
+ int oa_serialize_long(struct oarchive *oa, const char *tag, const int64_t *d)
28
+ {
29
+ - const int64_t i = htonll(*d);
30
+ + const int64_t i = zk_htonll(*d);
31
+ struct buff_struct *priv = oa->priv;
32
+ if ((priv->len - priv->off) < sizeof(i)) {
33
+ int rc = resize_buffer(priv, priv->len + sizeof(i));
34
+ @@ -207,7 +207,7 @@
35
+ }
36
+ memcpy(count, priv->buffer+priv->off, sizeof(*count));
37
+ priv->off+=sizeof(*count);
38
+ - v = htonll(*count); // htonll and ntohll do the same
39
+ + v = zk_htonll(*count); // zk_htonll and ntohll do the same
40
+ *count = v;
41
+ return 0;
42
+ }
43
+ diff -ur zkc-3.4.5-orig/c/src/zookeeper.c zkc-3.4.5/c/src/zookeeper.c
44
+ --- zkc-3.4.5-orig/c/src/zookeeper.c 2012-09-30 13:53:32.000000000 -0400
45
+ +++ zkc-3.4.5/c/src/zookeeper.c 2014-07-29 03:13:45.000000000 -0400
46
+ @@ -1408,7 +1408,7 @@
47
+ memcpy(buffer + offset, &req->protocolVersion, sizeof(req->protocolVersion));
48
+ offset = offset + sizeof(req->protocolVersion);
49
+
50
+ - req->lastZxidSeen = htonll(req->lastZxidSeen);
51
+ + req->lastZxidSeen = zk_htonll(req->lastZxidSeen);
52
+ memcpy(buffer + offset, &req->lastZxidSeen, sizeof(req->lastZxidSeen));
53
+ offset = offset + sizeof(req->lastZxidSeen);
54
+
55
+ @@ -1416,7 +1416,7 @@
56
+ memcpy(buffer + offset, &req->timeOut, sizeof(req->timeOut));
57
+ offset = offset + sizeof(req->timeOut);
58
+
59
+ - req->sessionId = htonll(req->sessionId);
60
+ + req->sessionId = zk_htonll(req->sessionId);
61
+ memcpy(buffer + offset, &req->sessionId, sizeof(req->sessionId));
62
+ offset = offset + sizeof(req->sessionId);
63
+
64
+ @@ -1447,7 +1447,7 @@
65
+ memcpy(&req->sessionId, buffer + offset, sizeof(req->sessionId));
66
+ offset = offset + sizeof(req->sessionId);
67
+
68
+ - req->sessionId = htonll(req->sessionId);
69
+ + req->sessionId = zk_htonll(req->sessionId);
70
+ memcpy(&req->passwd_len, buffer + offset, sizeof(req->passwd_len));
71
+ offset = offset + sizeof(req->passwd_len);
72
+
73
+ diff -ur zkc-3.4.5-orig/c/tests/ZKMocks.cc zkc-3.4.5/c/tests/ZKMocks.cc
74
+ --- zkc-3.4.5-orig/c/tests/ZKMocks.cc 2012-09-30 13:53:32.000000000 -0400
75
+ +++ zkc-3.4.5/c/tests/ZKMocks.cc 2014-07-29 03:13:59.000000000 -0400
76
+ @@ -41,7 +41,7 @@
77
+ int offset=sizeof(req->protocolVersion);
78
+
79
+ memcpy(&req->lastZxidSeen,buf.data()+offset,sizeof(req->lastZxidSeen));
80
+ - req->lastZxidSeen = htonll(req->lastZxidSeen);
81
+ + req->lastZxidSeen = zk_htonll(req->lastZxidSeen);
82
+ offset+=sizeof(req->lastZxidSeen);
83
+
84
+ memcpy(&req->timeOut,buf.data()+offset,sizeof(req->timeOut));
85
+ @@ -49,7 +49,7 @@
86
+ offset+=sizeof(req->timeOut);
87
+
88
+ memcpy(&req->sessionId,buf.data()+offset,sizeof(req->sessionId));
89
+ - req->sessionId = htonll(req->sessionId);
90
+ + req->sessionId = zk_htonll(req->sessionId);
91
+ offset+=sizeof(req->sessionId);
92
+
93
+ memcpy(&req->passwd_len,buf.data()+offset,sizeof(req->passwd_len));
94
+ @@ -322,7 +322,7 @@
95
+ buf.append((char*)&tmp,sizeof(tmp));
96
+ tmp=htonl(timeOut);
97
+ buf.append((char*)&tmp,sizeof(tmp));
98
+ - int64_t tmp64=htonll(sessionId);
99
+ + int64_t tmp64=zk_htonll(sessionId);
100
+ buf.append((char*)&tmp64,sizeof(sessionId));
101
+ tmp=htonl(passwd_len);
102
+ buf.append((char*)&tmp,sizeof(tmp));
Binary file
data/ext/zkrb.c ADDED
@@ -0,0 +1,1075 @@
1
+ /* Ruby wrapper for the Zookeeper C API
2
+ * Phillip Pearson <pp@myelin.co.nz>
3
+ * Eric Maland <eric@twitter.com>
4
+ * Brian Wickman <wickman@twitter.com>
5
+ * Jonathan D. Simms <slyphon@gmail.com>
6
+ *
7
+ * This fork is a 90% rewrite of the original. It takes a more evented
8
+ * approach to isolate the ZK state machine from the ruby interpreter via an
9
+ * event queue. It's similar to the ZookeeperFFI version except that it
10
+ * actually works on MRI 1.8.
11
+ *
12
+ *----------------
13
+ * (slyphon)
14
+ *
15
+ * Wickman's implementation was linked against the 'mt' version of the zookeeper
16
+ * library, which is multithreaded at the zookeeper level and is subsequently
17
+ * much more difficult to get to behave properly with the ruby runtime (which
18
+ * he did, and I could never have written).
19
+ *
20
+ * The current implementation has been converted to use the 'st' version of the
21
+ * zookeeper library, which is single threaded and requires a ruby-side event
22
+ * loop. This is essentially a ruby port of the code running in the 'mt'
23
+ * library, with one important difference: It's running in ruby-land. The
24
+ * reason this change is so important is that it's virtually impossible to
25
+ * provide a fork-safe library when you have native threads you don't own
26
+ * running around. If you fork when a thread holds a mutex, and that thread
27
+ * is not the fork-caller, that mutex can never be unlocked, and is therefore
28
+ * a ticking time-bomb in the child. The only way to guarantee safety is to
29
+ * either replace all of your mutexes and conditions and such after a fork
30
+ * (which is what we do on the ruby side), or avoid the problem altogether
31
+ * and not use a multithreaded library on the backend. Since we can't replace
32
+ * mutexes in the zookeeper code, we opt for the latter solution.
33
+ *
34
+ * The ruby code runs the event loop in a thread that will never cause a fork()
35
+ * to occur. This way, when fork() is called, the event thread will be dead
36
+ * in the child, guaranteeing that the child can safely be cleaned up.
37
+ *
38
+ * In that cleanup, there is a nasty (and brutishly effective) hack that makes
39
+ * the fork case work. We keep track of the pid that allocated the
40
+ * zkrb_instance_data_t, and if at destruction time we see that a fork has
41
+ * happened, we reach inside the zookeeper handle (zk->zh), and close the
42
+ * open socket it's got before calling zookeeper_close. This prevents
43
+ * corruption of the client/server state. Without this code, zookeeper_close
44
+ * in the child would actually send an "Ok, we're closing" message with the
45
+ * parent's session id, causing the parent to hit an assert() case in
46
+ * zookeeper_process, and cause a SIGABRT. With this code in place, we get back
47
+ * a ZCONNECTIONLOSS from zookeeper_close in the child (which we ignore), and
48
+ * the parent continues on.
49
+ *
50
+ * You will notice below we undef 'THREADED', which would be set if we were
51
+ * using the 'mt' library. We also conditionally include additional cases
52
+ * ('SYNC', 'SYNC_WATCH') inside of some of the methods defined here. These
53
+ * would be valid when running the 'mt' library, but since we have a ruby layer
54
+ * to provide a sync front-end to an async backend, these cases should never be
55
+ * hit, and instead will raise exceptions.
56
+ *
57
+ * NOTE: This file depends on exception classes defined in lib/zookeeper/exceptions.rb
58
+ *
59
+ * -------
60
+ *
61
+ * @rectalogic: any time you create a ruby value in C, and so there are no
62
+ * references to it in the VM except for your variable, and you then call into
63
+ * the VM (allowing a GC), and your reference is on the stack, then it needs to
64
+ * be volatile
65
+ *
66
+ */
67
+
68
+ #include "ruby.h"
69
+
70
+ #ifdef ZKRB_RUBY_187
71
+ #include "rubyio.h"
72
+ #else
73
+ #include "ruby/io.h"
74
+ #endif
75
+
76
+ #ifndef HAVE_RB_THREAD_FD_SELECT
77
+ #define rb_fdset_t fd_set
78
+ #define rb_fd_isset(n, f) FD_ISSET(n, f)
79
+ #define rb_fd_init(f) FD_ZERO(f)
80
+ #define rb_fd_zero(f) FD_ZERO(f)
81
+ #define rb_fd_set(n, f) FD_SET(n, f)
82
+ #define rb_fd_clr(n, f) FD_CLR(n, f)
83
+ #define rb_fd_term(f)
84
+ #define rb_thread_fd_select rb_thread_select
85
+ #endif
86
+
87
+ #include "zookeeper/zookeeper.h"
88
+ #include <errno.h>
89
+ #include <stdio.h>
90
+ #include <stdlib.h>
91
+ #include <unistd.h>
92
+ #include <sys/fcntl.h>
93
+ #include <pthread.h>
94
+ #include <inttypes.h>
95
+ #include <time.h>
96
+ #include <arpa/inet.h>
97
+ #include <netinet/in.h>
98
+
99
+ #include "common.h"
100
+ #include "event_lib.h"
101
+ #include "zkrb_wrapper.h"
102
+ #include "dbg.h"
103
+
104
+ static VALUE mZookeeper = Qnil; // the Zookeeper module
105
+ static VALUE CZookeeper = Qnil; // the Zookeeper::CZookeeper class
106
+ static VALUE ZookeeperClientId = Qnil;
107
+ static VALUE mZookeeperExceptions = Qnil; // the Zookeeper::Exceptions module
108
+ static VALUE eHandleClosedException = Qnil; // raised when we don't have a valid handle in FETCH_DATA_PTR
109
+
110
+ // slyphon: possibly add a lock to this for synchronizing during get_next_event
111
+
112
+ struct zkrb_instance_data {
113
+ zhandle_t *zh;
114
+ clientid_t myid;
115
+ zkrb_queue_t *queue;
116
+ long object_id; // the ruby object this instance data is associated with
117
+ pid_t orig_pid;
118
+ };
119
+
120
+ typedef struct zkrb_instance_data zkrb_instance_data_t;
121
+
122
+ typedef enum {
123
+ SYNC = 0,
124
+ ASYNC = 1,
125
+ SYNC_WATCH = 2,
126
+ ASYNC_WATCH = 3
127
+ } zkrb_call_type;
128
+
129
+ inline static const char* call_type_to_str(zkrb_call_type ct) {
130
+ const char *rv = NULL;
131
+ switch (ct) {
132
+ case SYNC:
133
+ rv="SYNC";
134
+ break;
135
+ case ASYNC:
136
+ rv="ASYNC";
137
+ break;
138
+ case SYNC_WATCH:
139
+ rv="SYNC_WATCH";
140
+ break;
141
+ case ASYNC_WATCH:
142
+ rv="ASYNC_WATCH";
143
+ break;
144
+ }
145
+ return rv;
146
+ }
147
+
148
+ inline static void assert_valid_params(VALUE reqid, VALUE path) {
149
+ switch (TYPE(reqid)) {
150
+ case T_FIXNUM:
151
+ case T_BIGNUM:
152
+ break;
153
+ default:
154
+ rb_raise(rb_eTypeError, "reqid must be Fixnum/Bignum");
155
+ }
156
+
157
+ Check_Type(path, T_STRING);
158
+ }
159
+
160
+ inline static zkrb_call_type get_call_type(VALUE async, VALUE watch) {
161
+ if (RTEST(async)) {
162
+ return RTEST(watch) ? ASYNC_WATCH : ASYNC;
163
+ } else {
164
+ return RTEST(watch) ? SYNC_WATCH : SYNC;
165
+ }
166
+ }
167
+
168
+ inline static void raise_invalid_call_type_err(zkrb_call_type call_type) {
169
+ rb_raise(rb_eRuntimeError, "hit the default case, call_type: %s", call_type_to_str(call_type));
170
+ }
171
+
172
+ #define IS_SYNC(zkrbcall) ((zkrbcall)==SYNC || (zkrbcall)==SYNC_WATCH)
173
+ #define IS_ASYNC(zkrbcall) ((zkrbcall)==ASYNC || (zkrbcall)==ASYNC_WATCH)
174
+
175
+ #define FETCH_DATA_PTR(SELF, ZK) \
176
+ zkrb_instance_data_t * ZK; \
177
+ Data_Get_Struct(rb_iv_get(SELF, "@_data"), zkrb_instance_data_t, ZK); \
178
+ if ((ZK)->zh == NULL) \
179
+ rb_raise(eHandleClosedException, "zookeeper handle is closed")
180
+
181
+ #define STANDARD_PREAMBLE(SELF, ZK, REQID, PATH, ASYNC, WATCH, CALL_TYPE) \
182
+ assert_valid_params(REQID, PATH); \
183
+ FETCH_DATA_PTR(SELF, ZK); \
184
+ zkrb_call_type CALL_TYPE = get_call_type(ASYNC, WATCH); \
185
+
186
+ #define CTX_ALLOC(ZK,REQID) zkrb_calling_context_alloc(NUM2LL(REQID), ZK->queue)
187
+
188
+ static void hexbufify(char *dest, const char *src, int len) {
189
+ int i=0;
190
+
191
+ for (i=0; i < len; i++) {
192
+ sprintf(&dest[i*2], "%x", src[i]);
193
+ }
194
+ }
195
+
196
+ inline static int we_are_forked(zkrb_instance_data_t *zk) {
197
+ int rv=0;
198
+
199
+ if ((!!zk) && (zk->orig_pid != getpid())) {
200
+ rv=1;
201
+ }
202
+
203
+ return rv;
204
+ }
205
+
206
+
207
+ static int destroy_zkrb_instance(zkrb_instance_data_t* zk) {
208
+ int rv = ZOK;
209
+
210
+ zkrb_debug("destroy_zkrb_instance, zk_local_ctx: %p, zh: %p, queue: %p", zk, zk->zh, zk->queue);
211
+
212
+ if (zk->zh) {
213
+ const void *ctx = zoo_get_context(zk->zh);
214
+ /* Note that after zookeeper_close() returns, ZK handle is invalid */
215
+ zkrb_debug("obj_id: %lx, calling zookeeper_close", zk->object_id);
216
+
217
+ if (we_are_forked(zk)) {
218
+ zkrb_debug("FORK DETECTED! orig_pid: %d, current pid: %d, "
219
+ "using socket-closing hack before zookeeper_close", zk->orig_pid, getpid());
220
+
221
+ int fd = ((int *)zk->zh)[0]; // nasty, brutish, and wonderfully effective hack (see above)
222
+ close(fd);
223
+
224
+ }
225
+
226
+ rv = zookeeper_close(zk->zh);
227
+
228
+ zkrb_debug("obj_id: %lx, zookeeper_close returned %d, calling context: %p", zk->object_id, rv, ctx);
229
+ zkrb_calling_context_free((zkrb_calling_context *) ctx);
230
+ }
231
+
232
+ zk->zh = NULL;
233
+
234
+ if (zk->queue) {
235
+ zkrb_debug("obj_id: %lx, freeing queue pointer: %p", zk->object_id, zk->queue);
236
+ zkrb_queue_free(zk->queue);
237
+ }
238
+
239
+ zk->queue = NULL;
240
+
241
+ return rv;
242
+ }
243
+
244
+ static void free_zkrb_instance_data(zkrb_instance_data_t* ptr) {
245
+ destroy_zkrb_instance(ptr);
246
+ }
247
+
248
+ static void print_zkrb_instance_data(zkrb_instance_data_t* ptr) {
249
+ fprintf(stderr, "zkrb_instance_data (%p) {\n", ptr);
250
+ fprintf(stderr, " zh = %p\n", ptr->zh);
251
+ fprintf(stderr, " { state = %d }\n", zoo_state(ptr->zh));
252
+ fprintf(stderr, " id = %"PRId64"\n", ptr->myid.client_id); // PRId64 defined in inttypes.h
253
+ fprintf(stderr, " q = %p\n", ptr->queue);
254
+ fprintf(stderr, " obj_id = %lx\n", ptr->object_id);
255
+ fprintf(stderr, "}\n");
256
+ }
257
+
258
+ #define receive_timeout_msec(self) rb_iv_get(self, "@_receive_timeout_msec")
259
+
260
+ inline static void zkrb_debug_clientid_t(const clientid_t *cid) {
261
+ int pass_len = sizeof(cid->passwd);
262
+ int hex_len = 2 * pass_len + 1;
263
+ char buf[hex_len];
264
+ hexbufify(buf, cid->passwd, pass_len);
265
+
266
+ zkrb_debug("myid, client_id: %"PRId64", passwd: %*s", cid->client_id, hex_len, buf);
267
+ }
268
+
269
+ static VALUE method_zkrb_init(int argc, VALUE* argv, VALUE self) {
270
+ VALUE hostPort=Qnil;
271
+ VALUE options=Qnil;
272
+
273
+ rb_scan_args(argc, argv, "11", &hostPort, &options);
274
+
275
+ if (NIL_P(options)) {
276
+ options = rb_hash_new();
277
+ } else {
278
+ Check_Type(options, T_HASH);
279
+ }
280
+
281
+ Check_Type(hostPort, T_STRING);
282
+
283
+ // Look up :zkc_log_level
284
+ // VALUE log_level = rb_hash_aref(options, ID2SYM(rb_intern("zkc_log_level")));
285
+ // if (NIL_P(log_level)) {
286
+ // zoo_set_debug_level(0); // no log messages
287
+ // } else {
288
+ // Check_Type(log_level, T_FIXNUM);
289
+ // zoo_set_debug_level(FIX2INT(log_level));
290
+ // }
291
+
292
+ volatile VALUE data;
293
+ zkrb_instance_data_t *zk_local_ctx;
294
+ data = Data_Make_Struct(CZookeeper, zkrb_instance_data_t, 0, free_zkrb_instance_data, zk_local_ctx);
295
+
296
+ // Look up :session_id and :session_passwd
297
+ VALUE session_id = rb_hash_aref(options, ID2SYM(rb_intern("session_id")));
298
+ VALUE password = rb_hash_aref(options, ID2SYM(rb_intern("session_passwd")));
299
+ if (!NIL_P(session_id) && !NIL_P(password)) {
300
+ Check_Type(password, T_STRING);
301
+
302
+ zk_local_ctx->myid.client_id = NUM2LL(session_id);
303
+ strncpy(zk_local_ctx->myid.passwd, RSTRING_PTR(password), 16);
304
+ }
305
+
306
+ zk_local_ctx->queue = zkrb_queue_alloc();
307
+
308
+ if (zk_local_ctx->queue == NULL)
309
+ rb_raise(rb_eRuntimeError, "could not allocate zkrb queue!");
310
+
311
+ zoo_deterministic_conn_order(0);
312
+
313
+ zkrb_calling_context *ctx =
314
+ zkrb_calling_context_alloc(ZKRB_GLOBAL_REQ, zk_local_ctx->queue);
315
+
316
+ zk_local_ctx->object_id = FIX2LONG(rb_obj_id(self));
317
+
318
+ zk_local_ctx->zh =
319
+ zookeeper_init(
320
+ RSTRING_PTR(hostPort), // const char *host
321
+ zkrb_state_callback, // watcher_fn
322
+ receive_timeout_msec(self), // recv_timeout
323
+ &zk_local_ctx->myid, // cilentid_t
324
+ ctx, // void *context
325
+ 0); // flags
326
+
327
+ zkrb_debug("method_zkrb_init, zk_local_ctx: %p, zh: %p, queue: %p, calling_ctx: %p",
328
+ zk_local_ctx, zk_local_ctx->zh, zk_local_ctx->queue, ctx);
329
+
330
+ if (!zk_local_ctx->zh) {
331
+ rb_raise(rb_eRuntimeError, "error connecting to zookeeper: %d", errno);
332
+ }
333
+
334
+ zk_local_ctx->orig_pid = getpid();
335
+
336
+ rb_iv_set(self, "@_data", data);
337
+ rb_funcall(self, rb_intern("zkc_set_running_and_notify!"), 0);
338
+
339
+ return Qnil;
340
+ }
341
+
342
+ static VALUE method_get_children(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE watch) {
343
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, call_type);
344
+
345
+ VALUE output = Qnil;
346
+ struct String_vector strings;
347
+ struct Stat stat;
348
+
349
+ int rc = 0;
350
+ switch (call_type) {
351
+
352
+ #ifdef THREADED
353
+ case SYNC:
354
+ rc = zkrb_call_zoo_get_children2(
355
+ zk->zh, RSTRING_PTR(path), 0, &strings, &stat);
356
+ break;
357
+
358
+ case SYNC_WATCH:
359
+ rc = zkrb_call_zoo_wget_children2(
360
+ zk->zh, RSTRING_PTR(path), zkrb_state_callback, CTX_ALLOC(zk, reqid), &strings, &stat);
361
+ break;
362
+ #endif
363
+
364
+ case ASYNC:
365
+ rc = zkrb_call_zoo_aget_children2(
366
+ zk->zh, RSTRING_PTR(path), 0, zkrb_strings_stat_callback, CTX_ALLOC(zk, reqid));
367
+ break;
368
+
369
+ case ASYNC_WATCH:
370
+ rc = zkrb_call_zoo_awget_children2(
371
+ zk->zh, RSTRING_PTR(path), zkrb_state_callback, CTX_ALLOC(zk, reqid), zkrb_strings_stat_callback, CTX_ALLOC(zk, reqid));
372
+ break;
373
+
374
+ default:
375
+ raise_invalid_call_type_err(call_type);
376
+ break;
377
+ }
378
+
379
+ output = rb_ary_new();
380
+ rb_ary_push(output, INT2FIX(rc));
381
+ if (IS_SYNC(call_type) && rc == ZOK) {
382
+ rb_ary_push(output, zkrb_string_vector_to_ruby(&strings));
383
+ rb_ary_push(output, zkrb_stat_to_rarray(&stat));
384
+ }
385
+ return output;
386
+ }
387
+
388
+ static VALUE method_exists(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE watch) {
389
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, call_type);
390
+
391
+ VALUE output = Qnil;
392
+ struct Stat stat;
393
+
394
+ int rc = 0;
395
+ switch (call_type) {
396
+
397
+ #ifdef THREADED
398
+ case SYNC:
399
+ rc = zkrb_call_zoo_exists(zk->zh, RSTRING_PTR(path), 0, &stat);
400
+ break;
401
+
402
+ case SYNC_WATCH:
403
+ rc = zkrb_call_zoo_wexists(zk->zh, RSTRING_PTR(path), zkrb_state_callback, CTX_ALLOC(zk, reqid), &stat);
404
+ break;
405
+ #endif
406
+
407
+ case ASYNC:
408
+ rc = zkrb_call_zoo_aexists(zk->zh, RSTRING_PTR(path), 0, zkrb_stat_callback, CTX_ALLOC(zk, reqid));
409
+ break;
410
+
411
+ case ASYNC_WATCH:
412
+ rc = zkrb_call_zoo_awexists(zk->zh, RSTRING_PTR(path), zkrb_state_callback, CTX_ALLOC(zk, reqid), zkrb_stat_callback, CTX_ALLOC(zk, reqid));
413
+ break;
414
+
415
+ default:
416
+ raise_invalid_call_type_err(call_type);
417
+ break;
418
+ }
419
+
420
+ output = rb_ary_new();
421
+ rb_ary_push(output, INT2FIX(rc));
422
+ if (IS_SYNC(call_type) && rc == ZOK) {
423
+ rb_ary_push(output, zkrb_stat_to_rarray(&stat));
424
+ }
425
+ return output;
426
+ }
427
+
428
+ // this method is *only* called asynchronously
429
+ static VALUE method_sync(VALUE self, VALUE reqid, VALUE path) {
430
+ int rc = ZOK;
431
+
432
+ // don't use STANDARD_PREAMBLE here b/c we don't need to determine call_type
433
+ assert_valid_params(reqid, path);
434
+ FETCH_DATA_PTR(self, zk);
435
+
436
+ rc = zkrb_call_zoo_async(zk->zh, RSTRING_PTR(path), zkrb_string_callback, CTX_ALLOC(zk, reqid));
437
+
438
+ return INT2FIX(rc);
439
+ }
440
+
441
+ static VALUE method_add_auth(VALUE self, VALUE reqid, VALUE scheme, VALUE cert) {
442
+ int rc = ZOK;
443
+
444
+ Check_Type(scheme, T_STRING);
445
+ Check_Type(cert, T_STRING);
446
+
447
+ FETCH_DATA_PTR(self, zk);
448
+
449
+ rc = zkrb_call_zoo_add_auth(zk->zh, RSTRING_PTR(scheme), RSTRING_PTR(cert), RSTRING_LEN(cert), zkrb_void_callback, CTX_ALLOC(zk, reqid));
450
+
451
+ return INT2FIX(rc);
452
+ }
453
+
454
+
455
+ static VALUE method_create(VALUE self, VALUE reqid, VALUE path, VALUE data, VALUE async, VALUE acls, VALUE flags) {
456
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, Qfalse, call_type);
457
+ VALUE output = Qnil;
458
+
459
+ if (data != Qnil) Check_Type(data, T_STRING);
460
+ Check_Type(flags, T_FIXNUM);
461
+ const char *data_ptr = (data == Qnil) ? NULL : RSTRING_PTR(data);
462
+ ssize_t data_len = (data == Qnil) ? -1 : RSTRING_LEN(data);
463
+
464
+ struct ACL_vector *aclptr = NULL;
465
+ if (acls != Qnil) { aclptr = zkrb_ruby_to_aclvector(acls); }
466
+ char realpath[16384];
467
+
468
+ int invalid_call_type=0;
469
+
470
+ int rc = 0;
471
+ switch (call_type) {
472
+
473
+ #ifdef THREADED
474
+ case SYNC:
475
+ // casting data_len to int is OK as you can only store 1MB in zookeeper
476
+ rc = zkrb_call_zoo_create(zk->zh, RSTRING_PTR(path), data_ptr, (int)data_len, aclptr, FIX2INT(flags), realpath, sizeof(realpath));
477
+ break;
478
+ #endif
479
+
480
+ case ASYNC:
481
+ rc = zkrb_call_zoo_acreate(zk->zh, RSTRING_PTR(path), data_ptr, (int)data_len, aclptr, FIX2INT(flags), zkrb_string_callback, CTX_ALLOC(zk, reqid));
482
+ break;
483
+
484
+ default:
485
+ invalid_call_type=1;
486
+ break;
487
+ }
488
+
489
+ if (aclptr) {
490
+ deallocate_ACL_vector(aclptr);
491
+ free(aclptr);
492
+ }
493
+
494
+ if (invalid_call_type) raise_invalid_call_type_err(call_type);
495
+
496
+ output = rb_ary_new();
497
+ rb_ary_push(output, INT2FIX(rc));
498
+ if (IS_SYNC(call_type) && rc == ZOK) {
499
+ return rb_ary_push(output, rb_str_new2(realpath));
500
+ }
501
+ return output;
502
+ }
503
+
504
+ static VALUE method_delete(VALUE self, VALUE reqid, VALUE path, VALUE version, VALUE async) {
505
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, Qfalse, call_type);
506
+ Check_Type(version, T_FIXNUM);
507
+
508
+ int rc = 0;
509
+ switch (call_type) {
510
+
511
+ #ifdef THREADED
512
+ case SYNC:
513
+ rc = zkrb_call_zoo_delete(zk->zh, RSTRING_PTR(path), FIX2INT(version));
514
+ break;
515
+ #endif
516
+
517
+ case ASYNC:
518
+ rc = zkrb_call_zoo_adelete(zk->zh, RSTRING_PTR(path), FIX2INT(version), zkrb_void_callback, CTX_ALLOC(zk, reqid));
519
+ break;
520
+
521
+ default:
522
+ raise_invalid_call_type_err(call_type);
523
+ break;
524
+ }
525
+
526
+ return INT2FIX(rc);
527
+ }
528
+
529
+ #define MAX_ZNODE_SIZE 1048576
530
+
531
+ static VALUE method_get(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE watch) {
532
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, call_type);
533
+
534
+ VALUE output = Qnil;
535
+
536
+ int data_len = MAX_ZNODE_SIZE;
537
+ struct Stat stat;
538
+
539
+ char * data = NULL;
540
+ if (IS_SYNC(call_type)) {
541
+ data = malloc(MAX_ZNODE_SIZE); /* ugh */
542
+ memset(data, 0, MAX_ZNODE_SIZE);
543
+ }
544
+
545
+ int rc, invalid_call_type=0;
546
+
547
+ switch (call_type) {
548
+
549
+ #ifdef THREADED
550
+ case SYNC:
551
+ rc = zkrb_call_zoo_get(zk->zh, RSTRING_PTR(path), 0, data, &data_len, &stat);
552
+ break;
553
+
554
+ case SYNC_WATCH:
555
+ rc = zkrb_call_zoo_wget(
556
+ zk->zh, RSTRING_PTR(path), zkrb_state_callback, CTX_ALLOC(zk, reqid), data, &data_len, &stat);
557
+ break;
558
+ #endif
559
+
560
+ case ASYNC:
561
+ rc = zkrb_call_zoo_aget(zk->zh, RSTRING_PTR(path), 0, zkrb_data_callback, CTX_ALLOC(zk, reqid));
562
+ break;
563
+
564
+ case ASYNC_WATCH:
565
+ // first ctx is a watch, second is the async callback
566
+ rc = zkrb_call_zoo_awget(
567
+ zk->zh, RSTRING_PTR(path), zkrb_state_callback, CTX_ALLOC(zk, reqid), zkrb_data_callback, CTX_ALLOC(zk, reqid));
568
+ break;
569
+
570
+ default:
571
+ invalid_call_type=1;
572
+ goto cleanup;
573
+ break;
574
+ }
575
+
576
+ output = rb_ary_new();
577
+ rb_ary_push(output, INT2FIX(rc));
578
+ if (IS_SYNC(call_type) && rc == ZOK) {
579
+ if (data_len == -1)
580
+ rb_ary_push(output, Qnil); /* No data associated with path */
581
+ else
582
+ rb_ary_push(output, rb_str_new(data, data_len));
583
+ rb_ary_push(output, zkrb_stat_to_rarray(&stat));
584
+ }
585
+
586
+ cleanup:
587
+ free(data);
588
+ if (invalid_call_type) raise_invalid_call_type_err(call_type);
589
+ return output;
590
+ }
591
+
592
+ static VALUE method_set(VALUE self, VALUE reqid, VALUE path, VALUE data, VALUE async, VALUE version) {
593
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, Qfalse, call_type);
594
+
595
+ VALUE output = Qnil;
596
+ struct Stat stat;
597
+
598
+ if (data != Qnil) Check_Type(data, T_STRING);
599
+
600
+ const char *data_ptr = (data == Qnil) ? NULL : RSTRING_PTR(data);
601
+ ssize_t data_len = (data == Qnil) ? -1 : RSTRING_LEN(data);
602
+
603
+ int rc=ZOK;
604
+ switch (call_type) {
605
+
606
+ #ifdef THREADED
607
+ case SYNC:
608
+ rc = zkrb_call_zoo_set2(zk->zh, RSTRING_PTR(path), data_ptr, (int)data_len, FIX2INT(version), &stat);
609
+ break;
610
+ #endif
611
+
612
+ case ASYNC:
613
+ rc = zkrb_call_zoo_aset(
614
+ zk->zh, RSTRING_PTR(path), data_ptr, (int)data_len, FIX2INT(version), zkrb_stat_callback, CTX_ALLOC(zk, reqid));
615
+ break;
616
+
617
+ default:
618
+ raise_invalid_call_type_err(call_type);
619
+ break;
620
+ }
621
+
622
+ output = rb_ary_new();
623
+ rb_ary_push(output, INT2FIX(rc));
624
+ if (IS_SYNC(call_type) && rc == ZOK) {
625
+ rb_ary_push(output, zkrb_stat_to_rarray(&stat));
626
+ }
627
+ return output;
628
+ }
629
+
630
+ static VALUE method_set_acl(VALUE self, VALUE reqid, VALUE path, VALUE acls, VALUE async, VALUE version) {
631
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, Qfalse, call_type);
632
+
633
+ struct ACL_vector * aclptr = zkrb_ruby_to_aclvector(acls);
634
+
635
+ int rc=ZOK, invalid_call_type=0;
636
+ switch (call_type) {
637
+
638
+ #ifdef THREADED
639
+ case SYNC:
640
+ rc = zkrb_call_zoo_set_acl(zk->zh, RSTRING_PTR(path), FIX2INT(version), aclptr);
641
+ break;
642
+ #endif
643
+
644
+ case ASYNC:
645
+ rc = zkrb_call_zoo_aset_acl(zk->zh, RSTRING_PTR(path), FIX2INT(version), aclptr, zkrb_void_callback, CTX_ALLOC(zk, reqid));
646
+ break;
647
+
648
+ default:
649
+ invalid_call_type=1;
650
+ break;
651
+ }
652
+
653
+ deallocate_ACL_vector(aclptr);
654
+ free(aclptr);
655
+
656
+ if (invalid_call_type) raise_invalid_call_type_err(call_type);
657
+
658
+ return INT2FIX(rc);
659
+ }
660
+
661
+ static VALUE method_get_acl(VALUE self, VALUE reqid, VALUE path, VALUE async) {
662
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, Qfalse, call_type);
663
+
664
+ VALUE output = Qnil;
665
+ struct ACL_vector acls;
666
+ struct Stat stat;
667
+
668
+ int rc=ZOK;
669
+ switch (call_type) {
670
+
671
+ #ifdef THREADED
672
+ case SYNC:
673
+ rc = zkrb_call_zoo_get_acl(zk->zh, RSTRING_PTR(path), &acls, &stat);
674
+ break;
675
+ #endif
676
+
677
+ case ASYNC:
678
+ rc = zkrb_call_zoo_aget_acl(zk->zh, RSTRING_PTR(path), zkrb_acl_callback, CTX_ALLOC(zk, reqid));
679
+ break;
680
+
681
+ default:
682
+ raise_invalid_call_type_err(call_type);
683
+ break;
684
+ }
685
+
686
+ output = rb_ary_new();
687
+ rb_ary_push(output, INT2FIX(rc));
688
+ if (IS_SYNC(call_type) && rc == ZOK) {
689
+ rb_ary_push(output, zkrb_acl_vector_to_ruby(&acls));
690
+ rb_ary_push(output, zkrb_stat_to_rarray(&stat));
691
+ deallocate_ACL_vector(&acls);
692
+ }
693
+ return output;
694
+ }
695
+
696
+ #define is_running(self) RTEST(rb_iv_get(self, "@_running"))
697
+ #define is_closed(self) RTEST(rb_iv_get(self, "@_closed"))
698
+ #define is_shutting_down(self) RTEST(rb_iv_get(self, "@_shutting_down"))
699
+
700
+ static VALUE method_zkrb_get_next_event(VALUE self, VALUE blocking) {
701
+ // dbg.h
702
+ check_debug(!is_closed(self), "we are closed, not trying to get event");
703
+
704
+ char buf[64];
705
+ FETCH_DATA_PTR(self, zk);
706
+
707
+ for (;;) {
708
+ check_debug(!is_closed(self), "we're closed in the middle of method_zkrb_get_next_event, bailing");
709
+
710
+ zkrb_event_t *event = zkrb_dequeue(zk->queue, 1);
711
+
712
+ if (event == NULL) {
713
+ if (NIL_P(blocking) || (blocking == Qfalse)) {
714
+ goto error;
715
+ }
716
+ else {
717
+ // if we're shutting down, don't enter this section, we don't want to block
718
+ check_debug(!is_shutting_down(self), "method_zkrb_get_next_event, we're shutting down, don't enter blocking section");
719
+
720
+ int fd = zk->queue->pipe_read;
721
+ ssize_t bytes_read = 0;
722
+
723
+ // wait for an fd to become readable, opposite of rb_thread_fd_writable
724
+ rb_thread_wait_fd(fd);
725
+
726
+ // clear all bytes here, we'll catch all the events on subsequent calls
727
+ // (until we run out of events)
728
+ bytes_read = read(fd, buf, sizeof(buf));
729
+
730
+ if (bytes_read == -1) {
731
+ rb_raise(rb_eRuntimeError, "read failed: %d", errno);
732
+ }
733
+
734
+ zkrb_debug_inst(self, "read %zd bytes from the queue (%p)'s pipe", bytes_read, zk->queue);
735
+
736
+ continue;
737
+ }
738
+ }
739
+
740
+ VALUE hash = zkrb_event_to_ruby(event);
741
+ zkrb_event_free(event);
742
+ return hash;
743
+ }
744
+
745
+ error:
746
+ return Qnil;
747
+ }
748
+
749
+ // the single threaded version of this call. will go away when we do direct
750
+ // event delivery (soon)
751
+ static VALUE method_zkrb_get_next_event_st(VALUE self) {
752
+ volatile VALUE rval = Qnil;
753
+
754
+ if (is_closed(self)) {
755
+ zkrb_debug("we are closed, not gonna try to get an event");
756
+ return Qnil;
757
+ }
758
+
759
+ FETCH_DATA_PTR(self, zk);
760
+
761
+ zkrb_event_t *event = zkrb_dequeue(zk->queue, 0);
762
+
763
+ if (event != NULL) {
764
+ rval = zkrb_event_to_ruby(event);
765
+ zkrb_event_free(event);
766
+
767
+ #if THREADED
768
+ int fd = zk->queue->pipe_read;
769
+
770
+ // we don't care in this case. this is just until i can remove the self
771
+ // pipe from the queue
772
+ char b[128];
773
+ while(read(fd, b, sizeof(b)) == sizeof(b)){}
774
+ #endif
775
+ }
776
+
777
+ return rval;
778
+ }
779
+
780
+ inline static int get_self_pipe_read_fd(VALUE self) {
781
+ rb_io_t *fptr;
782
+ VALUE pipe_read = rb_iv_get(self, "@pipe_read");
783
+
784
+ if (NIL_P(pipe_read))
785
+ rb_raise(rb_eRuntimeError, "@pipe_read was nil!");
786
+
787
+ GetOpenFile(pipe_read, fptr);
788
+
789
+ rb_io_check_readable(fptr);
790
+
791
+ #ifdef ZKRB_RUBY_187
792
+ return fileno(fptr->f);
793
+ #else
794
+ return fptr->fd;
795
+ #endif
796
+ }
797
+
798
+ static VALUE method_zkrb_iterate_event_loop(VALUE self) {
799
+ FETCH_DATA_PTR(self, zk);
800
+
801
+ rb_fdset_t rfds, wfds, efds;
802
+ rb_fd_init(&rfds); rb_fd_init(&wfds); rb_fd_init(&efds);
803
+
804
+ int fd = 0, interest = 0, events = 0, rc = 0, maxfd = 0, irc = 0, prc = 0;
805
+ struct timeval tv;
806
+
807
+ irc = zookeeper_interest(zk->zh, &fd, &interest, &tv);
808
+
809
+ if (fd != -1) {
810
+ if (interest & ZOOKEEPER_READ) {
811
+ rb_fd_set(fd, &rfds);
812
+ } else {
813
+ rb_fd_clr(fd, &rfds);
814
+ }
815
+ if (interest & ZOOKEEPER_WRITE) {
816
+ rb_fd_set(fd, &wfds);
817
+ } else {
818
+ rb_fd_clr(fd, &wfds);
819
+ }
820
+ } else {
821
+ fd = 0;
822
+ }
823
+
824
+ // add our self-pipe to the read set, allow us to wake up in case our attention is needed
825
+ int pipe_r_fd = get_self_pipe_read_fd(self);
826
+
827
+ rb_fd_set(pipe_r_fd, &rfds);
828
+
829
+ maxfd = (pipe_r_fd > fd) ? pipe_r_fd : fd;
830
+
831
+ rc = rb_thread_fd_select(maxfd+1, &rfds, &wfds, &efds, &tv);
832
+
833
+ if (rc > 0) {
834
+ if (rb_fd_isset(fd, &rfds)) {
835
+ events |= ZOOKEEPER_READ;
836
+ }
837
+ if (rb_fd_isset(fd, &wfds)) {
838
+ events |= ZOOKEEPER_WRITE;
839
+ }
840
+
841
+ // we got woken up by the self-pipe
842
+ if (rb_fd_isset(pipe_r_fd, &rfds)) {
843
+ // one event has awoken us, so we clear one event from the pipe
844
+ char b[1];
845
+
846
+ if (read(pipe_r_fd, b, 1) < 0) {
847
+ rb_raise(rb_eRuntimeError, "read from pipe failed: %s", clean_errno());
848
+ }
849
+ }
850
+ }
851
+ else if (rc == 0) {
852
+ // zkrb_debug("timed out waiting for descriptor to be ready. interest=%d fd=%d pipe_r_fd=%d maxfd=%d irc=%d timeout=%f",
853
+ // interest, fd, pipe_r_fd, maxfd, irc, tv.tv_sec + (tv.tv_usec/ 1000.0 / 1000.0));
854
+ }
855
+ else {
856
+ log_err("select returned an error: rc=%d interest=%d fd=%d pipe_r_fd=%d maxfd=%d irc=%d timeout=%f",
857
+ rc, interest, fd, pipe_r_fd, maxfd, irc, tv.tv_sec + (tv.tv_usec/ 1000.0 / 1000.0));
858
+ }
859
+
860
+ prc = zookeeper_process(zk->zh, events);
861
+
862
+ if (rc == 0) {
863
+ zkrb_debug("timed out waiting for descriptor to be ready. prc=%d interest=%d fd=%d pipe_r_fd=%d maxfd=%d irc=%d timeout=%f",
864
+ prc, interest, fd, pipe_r_fd, maxfd, irc, tv.tv_sec + (tv.tv_usec/ 1000.0 / 1000.0));
865
+ }
866
+
867
+ rb_fd_term(&rfds);
868
+ rb_fd_term(&wfds);
869
+ rb_fd_term(&efds);
870
+ return INT2FIX(prc);
871
+ }
872
+
873
+ static VALUE method_has_events(VALUE self) {
874
+ VALUE rb_event;
875
+ FETCH_DATA_PTR(self, zk);
876
+
877
+ rb_event = zkrb_peek(zk->queue) != NULL ? Qtrue : Qfalse;
878
+ return rb_event;
879
+ }
880
+
881
+ static VALUE method_zoo_set_log_level(VALUE self, VALUE level) {
882
+ Check_Type(level, T_FIXNUM);
883
+ zoo_set_debug_level(FIX2INT(level));
884
+ return Qnil;
885
+ }
886
+
887
+ static VALUE method_close_handle(VALUE self) {
888
+ FETCH_DATA_PTR(self, zk);
889
+
890
+ if (ZKRBDebugging) {
891
+ zkrb_debug_inst(self, "CLOSING_ZK_INSTANCE");
892
+ print_zkrb_instance_data(zk);
893
+ }
894
+
895
+ // this is a value on the ruby side we can check to see if destroy_zkrb_instance
896
+ // has been called
897
+ rb_iv_set(self, "@_closed", Qtrue);
898
+
899
+ /* Note that after zookeeper_close() returns, ZK handle is invalid */
900
+ int rc = destroy_zkrb_instance(zk);
901
+
902
+ zkrb_debug("destroy_zkrb_instance returned: %d", rc);
903
+
904
+ return INT2FIX(rc);
905
+ }
906
+
907
+ static VALUE method_deterministic_conn_order(VALUE self, VALUE yn) {
908
+ zoo_deterministic_conn_order(yn == Qtrue);
909
+ return Qnil;
910
+ }
911
+
912
+ static VALUE method_is_unrecoverable(VALUE self) {
913
+ FETCH_DATA_PTR(self, zk);
914
+ return is_unrecoverable(zk->zh) == ZINVALIDSTATE ? Qtrue : Qfalse;
915
+ }
916
+
917
+ static VALUE method_zkrb_state(VALUE self) {
918
+ FETCH_DATA_PTR(self, zk);
919
+ return INT2NUM(zoo_state(zk->zh));
920
+ }
921
+
922
+ static VALUE method_recv_timeout(VALUE self) {
923
+ FETCH_DATA_PTR(self, zk);
924
+ return INT2NUM(zoo_recv_timeout(zk->zh));
925
+ }
926
+
927
+ // returns a CZookeeper::ClientId object with the values set for session_id and passwd
928
+ static VALUE method_client_id(VALUE self) {
929
+ FETCH_DATA_PTR(self, zk);
930
+ const clientid_t *cid = zoo_client_id(zk->zh);
931
+
932
+ VALUE session_id = LL2NUM(cid->client_id);
933
+ VALUE passwd = rb_str_new(cid->passwd, 16);
934
+
935
+ VALUE client_id_obj = rb_class_new_instance(0, RARRAY_PTR(rb_ary_new()), ZookeeperClientId);
936
+
937
+ rb_funcall(client_id_obj, rb_intern("session_id="), 1, session_id);
938
+ rb_funcall(client_id_obj, rb_intern("passwd="), 1, passwd);
939
+
940
+ return client_id_obj;
941
+ }
942
+
943
+ static VALUE klass_method_zkrb_set_debug_level(VALUE klass, VALUE level) {
944
+ Check_Type(level, T_FIXNUM);
945
+ ZKRBDebugging = (FIX2INT(level) == ZOO_LOG_LEVEL_DEBUG);
946
+ zoo_set_debug_level(FIX2INT(level));
947
+ return Qnil;
948
+ }
949
+
950
+ static VALUE method_zerror(VALUE self, VALUE errc) {
951
+ return rb_str_new2(zerror(FIX2INT(errc)));
952
+ }
953
+
954
+ static VALUE method_connected_host(VALUE self) {
955
+ FETCH_DATA_PTR(self, zk);
956
+
957
+ struct sockaddr addr;
958
+ socklen_t addr_len = sizeof(addr);
959
+
960
+ if (zookeeper_get_connected_host(zk->zh, &addr, &addr_len) != NULL) {
961
+ char buf[255];
962
+ char addrstr[128];
963
+ void *inaddr;
964
+ int port;
965
+
966
+ #if defined(AF_INET6)
967
+ if(addr.sa_family==AF_INET6){
968
+ inaddr = &((struct sockaddr_in6 *) &addr)->sin6_addr;
969
+ port = ((struct sockaddr_in6 *) &addr)->sin6_port;
970
+ } else {
971
+ #endif
972
+ inaddr = &((struct sockaddr_in *) &addr)->sin_addr;
973
+ port = ((struct sockaddr_in *) &addr)->sin_port;
974
+ #if defined(AF_INET6)
975
+ }
976
+ #endif
977
+
978
+ inet_ntop(addr.sa_family, inaddr, addrstr, sizeof(addrstr)-1);
979
+ snprintf(buf, sizeof(buf), "%s:%d", addrstr, ntohs(port));
980
+ return rb_str_new2(buf);
981
+ }
982
+
983
+ return Qnil;
984
+ }
985
+
986
+ static void zkrb_define_methods(void) {
987
+ #define DEFINE_METHOD(M, ARGS) { \
988
+ rb_define_method(CZookeeper, #M, method_ ## M, ARGS); }
989
+
990
+ #define DEFINE_CLASS_METHOD(M, ARGS) { \
991
+ rb_define_singleton_method(CZookeeper, #M, method_ ## M, ARGS); }
992
+
993
+ // defines a method with a zkrb_ prefix, the actual C method does not have this prefix
994
+ #define DEFINE_ZKRB_METHOD(M, ARGS) { \
995
+ rb_define_method(CZookeeper, zkrb_ ## M, method_ ## M, ARGS); }
996
+
997
+ // the number after the method name should be actual arity of C function - 1
998
+ DEFINE_METHOD(zkrb_init, -1);
999
+
1000
+ rb_define_method(CZookeeper, "zkrb_get_children", method_get_children, 4);
1001
+ rb_define_method(CZookeeper, "zkrb_exists", method_exists, 4);
1002
+ rb_define_method(CZookeeper, "zkrb_create", method_create, 6);
1003
+ rb_define_method(CZookeeper, "zkrb_delete", method_delete, 4);
1004
+ rb_define_method(CZookeeper, "zkrb_get", method_get, 4);
1005
+ rb_define_method(CZookeeper, "zkrb_set", method_set, 5);
1006
+ rb_define_method(CZookeeper, "zkrb_set_acl", method_set_acl, 5);
1007
+ rb_define_method(CZookeeper, "zkrb_get_acl", method_get_acl, 3);
1008
+ rb_define_method(CZookeeper, "zkrb_add_auth", method_add_auth, 3);
1009
+
1010
+ rb_define_singleton_method(CZookeeper, "zoo_set_log_level", method_zoo_set_log_level, 1);
1011
+
1012
+ DEFINE_METHOD(client_id, 0);
1013
+ DEFINE_METHOD(close_handle, 0);
1014
+ DEFINE_METHOD(deterministic_conn_order, 1);
1015
+ DEFINE_METHOD(is_unrecoverable, 0);
1016
+ DEFINE_METHOD(recv_timeout, 1);
1017
+ DEFINE_METHOD(zkrb_state, 0);
1018
+ DEFINE_METHOD(sync, 2);
1019
+ DEFINE_METHOD(zkrb_iterate_event_loop, 0);
1020
+ DEFINE_METHOD(zkrb_get_next_event_st, 0);
1021
+ DEFINE_METHOD(connected_host, 0);
1022
+
1023
+ // methods for the ruby-side event manager
1024
+ DEFINE_METHOD(zkrb_get_next_event, 1);
1025
+ DEFINE_METHOD(zkrb_get_next_event_st, 0);
1026
+ DEFINE_METHOD(has_events, 0);
1027
+
1028
+ // Make these class methods?
1029
+ DEFINE_METHOD(zerror, 1);
1030
+
1031
+ rb_define_singleton_method(CZookeeper, "set_zkrb_debug_level", klass_method_zkrb_set_debug_level, 1);
1032
+
1033
+ rb_attr(CZookeeper, rb_intern("selectable_io"), 1, 0, Qtrue);
1034
+
1035
+ }
1036
+
1037
+ // class CZookeeper::ClientId
1038
+ // attr_accessor :session_id, :passwd
1039
+ //
1040
+ // def initialize(session_id, passwd)
1041
+ // @session_id = session_id
1042
+ // @passwd = passwd
1043
+ // end
1044
+ // end
1045
+
1046
+ static VALUE zkrb_client_id_method_initialize(VALUE self) {
1047
+ rb_iv_set(self, "@session_id", Qnil);
1048
+ rb_iv_set(self, "@passwd", Qnil);
1049
+ return Qnil;
1050
+ }
1051
+
1052
+
1053
+ void Init_zookeeper_c() {
1054
+ // Don't debug by default
1055
+ ZKRBDebugging = 0;
1056
+ zoo_set_debug_level(0);
1057
+
1058
+ mZookeeper = rb_define_module("Zookeeper");
1059
+ mZookeeperExceptions = rb_define_module_under(mZookeeper, "Exceptions");
1060
+
1061
+ // this will likely fail if the load order is screwed up
1062
+ eHandleClosedException = rb_const_get(mZookeeperExceptions, rb_intern("HandleClosedException"));
1063
+
1064
+ /* initialize CZookeeper class */
1065
+ CZookeeper = rb_define_class_under(mZookeeper, "CZookeeper", rb_cObject);
1066
+ zkrb_define_methods();
1067
+
1068
+ ZookeeperClientId = rb_define_class_under(CZookeeper, "ClientId", rb_cObject);
1069
+ rb_define_method(ZookeeperClientId, "initialize", zkrb_client_id_method_initialize, 0);
1070
+ rb_define_attr(ZookeeperClientId, "session_id", 1, 1);
1071
+ rb_define_attr(ZookeeperClientId, "passwd", 1, 1);
1072
+
1073
+ }
1074
+
1075
+ // vim:ts=2:sw=2:sts=2:et