zookeeper-ng 1.5

Sign up to get free protection for your applications and to get access to all the features.
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