nutcracker 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/README.md +22 -0
  2. data/Rakefile +55 -0
  3. data/bin/nutcracker +2 -0
  4. data/ext/nutcracker/ChangeLog +66 -0
  5. data/ext/nutcracker/LICENSE +177 -0
  6. data/ext/nutcracker/Makefile.am +7 -0
  7. data/ext/nutcracker/Makefile.in +726 -0
  8. data/ext/nutcracker/NOTICE +124 -0
  9. data/ext/nutcracker/README.md +240 -0
  10. data/ext/nutcracker/aclocal.m4 +956 -0
  11. data/ext/nutcracker/conf/nutcracker.leaf.yml +10 -0
  12. data/ext/nutcracker/conf/nutcracker.root.yml +8 -0
  13. data/ext/nutcracker/conf/nutcracker.yml +67 -0
  14. data/ext/nutcracker/config.h.in +316 -0
  15. data/ext/nutcracker/config/config.guess +1561 -0
  16. data/ext/nutcracker/config/config.sub +1686 -0
  17. data/ext/nutcracker/config/depcomp +630 -0
  18. data/ext/nutcracker/config/install-sh +520 -0
  19. data/ext/nutcracker/config/ltmain.sh +8413 -0
  20. data/ext/nutcracker/config/missing +376 -0
  21. data/ext/nutcracker/configure +18862 -0
  22. data/ext/nutcracker/configure.ac +155 -0
  23. data/ext/nutcracker/contrib/Makefile.am +3 -0
  24. data/ext/nutcracker/contrib/Makefile.in +560 -0
  25. data/ext/nutcracker/contrib/yaml-0.1.4.tar.gz +0 -0
  26. data/ext/nutcracker/contrib/yaml-0.1.4/LICENSE +19 -0
  27. data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.am +20 -0
  28. data/ext/nutcracker/contrib/yaml-0.1.4/Makefile.in +736 -0
  29. data/ext/nutcracker/contrib/yaml-0.1.4/README +27 -0
  30. data/ext/nutcracker/contrib/yaml-0.1.4/aclocal.m4 +956 -0
  31. data/ext/nutcracker/contrib/yaml-0.1.4/config.h.in +80 -0
  32. data/ext/nutcracker/contrib/yaml-0.1.4/config/config.guess +1561 -0
  33. data/ext/nutcracker/contrib/yaml-0.1.4/config/config.sub +1686 -0
  34. data/ext/nutcracker/contrib/yaml-0.1.4/config/depcomp +630 -0
  35. data/ext/nutcracker/contrib/yaml-0.1.4/config/install-sh +520 -0
  36. data/ext/nutcracker/contrib/yaml-0.1.4/config/ltmain.sh +8406 -0
  37. data/ext/nutcracker/contrib/yaml-0.1.4/config/missing +376 -0
  38. data/ext/nutcracker/contrib/yaml-0.1.4/configure +13085 -0
  39. data/ext/nutcracker/contrib/yaml-0.1.4/configure.ac +75 -0
  40. data/ext/nutcracker/contrib/yaml-0.1.4/doc/doxygen.cfg +222 -0
  41. data/ext/nutcracker/contrib/yaml-0.1.4/include/yaml.h +1971 -0
  42. data/ext/nutcracker/contrib/yaml-0.1.4/m4/libtool.m4 +7357 -0
  43. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltoptions.m4 +368 -0
  44. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltsugar.m4 +123 -0
  45. data/ext/nutcracker/contrib/yaml-0.1.4/m4/ltversion.m4 +23 -0
  46. data/ext/nutcracker/contrib/yaml-0.1.4/m4/lt~obsolete.m4 +92 -0
  47. data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.am +4 -0
  48. data/ext/nutcracker/contrib/yaml-0.1.4/src/Makefile.in +484 -0
  49. data/ext/nutcracker/contrib/yaml-0.1.4/src/api.c +1392 -0
  50. data/ext/nutcracker/contrib/yaml-0.1.4/src/dumper.c +394 -0
  51. data/ext/nutcracker/contrib/yaml-0.1.4/src/emitter.c +2329 -0
  52. data/ext/nutcracker/contrib/yaml-0.1.4/src/loader.c +432 -0
  53. data/ext/nutcracker/contrib/yaml-0.1.4/src/parser.c +1374 -0
  54. data/ext/nutcracker/contrib/yaml-0.1.4/src/reader.c +465 -0
  55. data/ext/nutcracker/contrib/yaml-0.1.4/src/scanner.c +3570 -0
  56. data/ext/nutcracker/contrib/yaml-0.1.4/src/writer.c +141 -0
  57. data/ext/nutcracker/contrib/yaml-0.1.4/src/yaml_private.h +640 -0
  58. data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.am +8 -0
  59. data/ext/nutcracker/contrib/yaml-0.1.4/tests/Makefile.in +675 -0
  60. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor-alt.c +800 -0
  61. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-deconstructor.c +1130 -0
  62. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter-alt.c +217 -0
  63. data/ext/nutcracker/contrib/yaml-0.1.4/tests/example-reformatter.c +202 -0
  64. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-dumper.c +311 -0
  65. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-emitter.c +327 -0
  66. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-loader.c +63 -0
  67. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-parser.c +63 -0
  68. data/ext/nutcracker/contrib/yaml-0.1.4/tests/run-scanner.c +63 -0
  69. data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-reader.c +354 -0
  70. data/ext/nutcracker/contrib/yaml-0.1.4/tests/test-version.c +29 -0
  71. data/ext/nutcracker/extconf.rb +5 -0
  72. data/ext/nutcracker/m4/libtool.m4 +7376 -0
  73. data/ext/nutcracker/m4/ltoptions.m4 +368 -0
  74. data/ext/nutcracker/m4/ltsugar.m4 +123 -0
  75. data/ext/nutcracker/m4/ltversion.m4 +23 -0
  76. data/ext/nutcracker/m4/lt~obsolete.m4 +92 -0
  77. data/ext/nutcracker/notes/c-styleguide.txt +425 -0
  78. data/ext/nutcracker/notes/debug.txt +96 -0
  79. data/ext/nutcracker/notes/memcache.txt +123 -0
  80. data/ext/nutcracker/notes/recommendation.md +118 -0
  81. data/ext/nutcracker/notes/redis.md +415 -0
  82. data/ext/nutcracker/notes/socket.txt +131 -0
  83. data/ext/nutcracker/scripts/multi_get.sh +26 -0
  84. data/ext/nutcracker/scripts/nutcracker.init +73 -0
  85. data/ext/nutcracker/scripts/nutcracker.spec +52 -0
  86. data/ext/nutcracker/scripts/pipelined_read.sh +23 -0
  87. data/ext/nutcracker/scripts/pipelined_write.sh +29 -0
  88. data/ext/nutcracker/scripts/populate_memcached.sh +24 -0
  89. data/ext/nutcracker/scripts/redis-check.py +23 -0
  90. data/ext/nutcracker/scripts/redis-check.sh +564 -0
  91. data/ext/nutcracker/src/Makefile.am +46 -0
  92. data/ext/nutcracker/src/Makefile.in +726 -0
  93. data/ext/nutcracker/src/hashkit/Makefile.am +22 -0
  94. data/ext/nutcracker/src/hashkit/Makefile.in +501 -0
  95. data/ext/nutcracker/src/hashkit/nc_crc32.c +105 -0
  96. data/ext/nutcracker/src/hashkit/nc_fnv.c +82 -0
  97. data/ext/nutcracker/src/hashkit/nc_hashkit.h +74 -0
  98. data/ext/nutcracker/src/hashkit/nc_hsieh.c +93 -0
  99. data/ext/nutcracker/src/hashkit/nc_jenkins.c +230 -0
  100. data/ext/nutcracker/src/hashkit/nc_ketama.c +240 -0
  101. data/ext/nutcracker/src/hashkit/nc_md5.c +379 -0
  102. data/ext/nutcracker/src/hashkit/nc_modula.c +144 -0
  103. data/ext/nutcracker/src/hashkit/nc_murmur.c +99 -0
  104. data/ext/nutcracker/src/hashkit/nc_one_at_a_time.c +51 -0
  105. data/ext/nutcracker/src/hashkit/nc_random.c +146 -0
  106. data/ext/nutcracker/src/nc.c +573 -0
  107. data/ext/nutcracker/src/nc_array.c +204 -0
  108. data/ext/nutcracker/src/nc_array.h +73 -0
  109. data/ext/nutcracker/src/nc_client.c +189 -0
  110. data/ext/nutcracker/src/nc_client.h +28 -0
  111. data/ext/nutcracker/src/nc_conf.c +1766 -0
  112. data/ext/nutcracker/src/nc_conf.h +134 -0
  113. data/ext/nutcracker/src/nc_connection.c +392 -0
  114. data/ext/nutcracker/src/nc_connection.h +99 -0
  115. data/ext/nutcracker/src/nc_core.c +334 -0
  116. data/ext/nutcracker/src/nc_core.h +131 -0
  117. data/ext/nutcracker/src/nc_event.c +214 -0
  118. data/ext/nutcracker/src/nc_event.h +39 -0
  119. data/ext/nutcracker/src/nc_log.c +254 -0
  120. data/ext/nutcracker/src/nc_log.h +120 -0
  121. data/ext/nutcracker/src/nc_mbuf.c +285 -0
  122. data/ext/nutcracker/src/nc_mbuf.h +67 -0
  123. data/ext/nutcracker/src/nc_message.c +828 -0
  124. data/ext/nutcracker/src/nc_message.h +253 -0
  125. data/ext/nutcracker/src/nc_proxy.c +359 -0
  126. data/ext/nutcracker/src/nc_proxy.h +34 -0
  127. data/ext/nutcracker/src/nc_queue.h +788 -0
  128. data/ext/nutcracker/src/nc_rbtree.c +348 -0
  129. data/ext/nutcracker/src/nc_rbtree.h +47 -0
  130. data/ext/nutcracker/src/nc_request.c +588 -0
  131. data/ext/nutcracker/src/nc_response.c +332 -0
  132. data/ext/nutcracker/src/nc_server.c +841 -0
  133. data/ext/nutcracker/src/nc_server.h +143 -0
  134. data/ext/nutcracker/src/nc_signal.c +131 -0
  135. data/ext/nutcracker/src/nc_signal.h +34 -0
  136. data/ext/nutcracker/src/nc_stats.c +1188 -0
  137. data/ext/nutcracker/src/nc_stats.h +206 -0
  138. data/ext/nutcracker/src/nc_string.c +109 -0
  139. data/ext/nutcracker/src/nc_string.h +112 -0
  140. data/ext/nutcracker/src/nc_util.c +619 -0
  141. data/ext/nutcracker/src/nc_util.h +214 -0
  142. data/ext/nutcracker/src/proto/Makefile.am +14 -0
  143. data/ext/nutcracker/src/proto/Makefile.in +482 -0
  144. data/ext/nutcracker/src/proto/nc_memcache.c +1306 -0
  145. data/ext/nutcracker/src/proto/nc_proto.h +155 -0
  146. data/ext/nutcracker/src/proto/nc_redis.c +2102 -0
  147. data/lib/nutcracker.rb +7 -0
  148. data/lib/nutcracker/version.rb +3 -0
  149. metadata +194 -0
@@ -0,0 +1,28 @@
1
+ /*
2
+ * twemproxy - A fast and lightweight proxy for memcached protocol.
3
+ * Copyright (C) 2011 Twitter, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #ifndef _NC_CLIENT_H_
19
+ #define _NC_CLIENT_H_
20
+
21
+ #include <nc_core.h>
22
+
23
+ bool client_active(struct conn *conn);
24
+ void client_ref(struct conn *conn, void *owner);
25
+ void client_unref(struct conn *conn);
26
+ void client_close(struct context *ctx, struct conn *conn);
27
+
28
+ #endif
@@ -0,0 +1,1766 @@
1
+ /*
2
+ * twemproxy - A fast and lightweight proxy for memcached protocol.
3
+ * Copyright (C) 2011 Twitter, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include <nc_core.h>
19
+ #include <nc_conf.h>
20
+ #include <nc_server.h>
21
+ #include <proto/nc_proto.h>
22
+
23
+ #define DEFINE_ACTION(_hash, _name) string(#_name),
24
+ static struct string hash_strings[] = {
25
+ HASH_CODEC( DEFINE_ACTION )
26
+ null_string
27
+ };
28
+ #undef DEFINE_ACTION
29
+
30
+ #define DEFINE_ACTION(_hash, _name) hash_##_name,
31
+ static hash_t hash_algos[] = {
32
+ HASH_CODEC( DEFINE_ACTION )
33
+ NULL
34
+ };
35
+ #undef DEFINE_ACTION
36
+
37
+ #define DEFINE_ACTION(_dist, _name) string(#_name),
38
+ static struct string dist_strings[] = {
39
+ DIST_CODEC( DEFINE_ACTION )
40
+ null_string
41
+ };
42
+ #undef DEFINE_ACTION
43
+
44
+ static struct command conf_commands[] = {
45
+ { string("listen"),
46
+ conf_set_listen,
47
+ offsetof(struct conf_pool, listen) },
48
+
49
+ { string("hash"),
50
+ conf_set_hash,
51
+ offsetof(struct conf_pool, hash) },
52
+
53
+ { string("hash_tag"),
54
+ conf_set_hashtag,
55
+ offsetof(struct conf_pool, hash_tag) },
56
+
57
+ { string("distribution"),
58
+ conf_set_distribution,
59
+ offsetof(struct conf_pool, distribution) },
60
+
61
+ { string("timeout"),
62
+ conf_set_num,
63
+ offsetof(struct conf_pool, timeout) },
64
+
65
+ { string("backlog"),
66
+ conf_set_num,
67
+ offsetof(struct conf_pool, backlog) },
68
+
69
+ { string("client_connections"),
70
+ conf_set_num,
71
+ offsetof(struct conf_pool, client_connections) },
72
+
73
+ { string("redis"),
74
+ conf_set_bool,
75
+ offsetof(struct conf_pool, redis) },
76
+
77
+ { string("preconnect"),
78
+ conf_set_bool,
79
+ offsetof(struct conf_pool, preconnect) },
80
+
81
+ { string("auto_eject_hosts"),
82
+ conf_set_bool,
83
+ offsetof(struct conf_pool, auto_eject_hosts) },
84
+
85
+ { string("server_connections"),
86
+ conf_set_num,
87
+ offsetof(struct conf_pool, server_connections) },
88
+
89
+ { string("server_retry_timeout"),
90
+ conf_set_num,
91
+ offsetof(struct conf_pool, server_retry_timeout) },
92
+
93
+ { string("server_failure_limit"),
94
+ conf_set_num,
95
+ offsetof(struct conf_pool, server_failure_limit) },
96
+
97
+ { string("servers"),
98
+ conf_add_server,
99
+ offsetof(struct conf_pool, server) },
100
+
101
+ null_command
102
+ };
103
+
104
+ static void
105
+ conf_server_init(struct conf_server *cs)
106
+ {
107
+ string_init(&cs->pname);
108
+ string_init(&cs->name);
109
+ cs->port = 0;
110
+ cs->weight = 0;
111
+
112
+ memset(&cs->info, 0, sizeof(cs->info));
113
+
114
+ cs->valid = 0;
115
+
116
+ log_debug(LOG_VVERB, "init conf server %p", cs);
117
+ }
118
+
119
+ static void
120
+ conf_server_deinit(struct conf_server *cs)
121
+ {
122
+ string_deinit(&cs->pname);
123
+ string_deinit(&cs->name);
124
+ cs->valid = 0;
125
+ log_debug(LOG_VVERB, "deinit conf server %p", cs);
126
+ }
127
+
128
+ rstatus_t
129
+ conf_server_each_transform(void *elem, void *data)
130
+ {
131
+ struct conf_server *cs = elem;
132
+ struct array *server = data;
133
+ struct server *s;
134
+
135
+ ASSERT(cs->valid);
136
+
137
+ s = array_push(server);
138
+ ASSERT(s != NULL);
139
+
140
+ s->idx = array_idx(server, s);
141
+ s->owner = NULL;
142
+
143
+ s->pname = cs->pname;
144
+ s->name = cs->name;
145
+ s->port = (uint16_t)cs->port;
146
+ s->weight = (uint32_t)cs->weight;
147
+
148
+ s->family = cs->info.family;
149
+ s->addrlen = cs->info.addrlen;
150
+ s->addr = (struct sockaddr *)&cs->info.addr;
151
+
152
+ s->ns_conn_q = 0;
153
+ TAILQ_INIT(&s->s_conn_q);
154
+
155
+ s->next_retry = 0LL;
156
+ s->failure_count = 0;
157
+
158
+ log_debug(LOG_VERB, "transform to server %"PRIu32" '%.*s'",
159
+ s->idx, s->pname.len, s->pname.data);
160
+
161
+ return NC_OK;
162
+ }
163
+
164
+ static rstatus_t
165
+ conf_pool_init(struct conf_pool *cp, struct string *name)
166
+ {
167
+ rstatus_t status;
168
+
169
+ string_init(&cp->name);
170
+
171
+ string_init(&cp->listen.pname);
172
+ string_init(&cp->listen.name);
173
+ cp->listen.port = 0;
174
+ memset(&cp->listen.info, 0, sizeof(cp->listen.info));
175
+ cp->listen.valid = 0;
176
+
177
+ cp->hash = CONF_UNSET_HASH;
178
+ string_init(&cp->hash_tag);
179
+ cp->distribution = CONF_UNSET_DIST;
180
+
181
+ cp->timeout = CONF_UNSET_NUM;
182
+ cp->backlog = CONF_UNSET_NUM;
183
+
184
+ cp->client_connections = CONF_UNSET_NUM;
185
+
186
+ cp->redis = CONF_UNSET_NUM;
187
+ cp->preconnect = CONF_UNSET_NUM;
188
+ cp->auto_eject_hosts = CONF_UNSET_NUM;
189
+ cp->server_connections = CONF_UNSET_NUM;
190
+ cp->server_retry_timeout = CONF_UNSET_NUM;
191
+ cp->server_failure_limit = CONF_UNSET_NUM;
192
+
193
+ array_null(&cp->server);
194
+
195
+ cp->valid = 0;
196
+
197
+ status = string_duplicate(&cp->name, name);
198
+ if (status != NC_OK) {
199
+ return status;
200
+ }
201
+
202
+ status = array_init(&cp->server, CONF_DEFAULT_SERVERS,
203
+ sizeof(struct conf_server));
204
+ if (status != NC_OK) {
205
+ string_deinit(&cp->name);
206
+ return status;
207
+ }
208
+
209
+ log_debug(LOG_VVERB, "init conf pool %p, '%.*s'", cp, name->len, name->data);
210
+
211
+ return NC_OK;
212
+ }
213
+
214
+ static void
215
+ conf_pool_deinit(struct conf_pool *cp)
216
+ {
217
+ string_deinit(&cp->name);
218
+
219
+ string_deinit(&cp->listen.pname);
220
+ string_deinit(&cp->listen.name);
221
+
222
+ while (array_n(&cp->server) != 0) {
223
+ conf_server_deinit(array_pop(&cp->server));
224
+ }
225
+ array_deinit(&cp->server);
226
+
227
+ log_debug(LOG_VVERB, "deinit conf pool %p", cp);
228
+ }
229
+
230
+ rstatus_t
231
+ conf_pool_each_transform(void *elem, void *data)
232
+ {
233
+ rstatus_t status;
234
+ struct conf_pool *cp = elem;
235
+ struct array *server_pool = data;
236
+ struct server_pool *sp;
237
+
238
+ ASSERT(cp->valid);
239
+
240
+ sp = array_push(server_pool);
241
+ ASSERT(sp != NULL);
242
+
243
+ sp->idx = array_idx(server_pool, sp);
244
+ sp->ctx = NULL;
245
+
246
+ sp->p_conn = NULL;
247
+ sp->nc_conn_q = 0;
248
+ TAILQ_INIT(&sp->c_conn_q);
249
+
250
+ array_null(&sp->server);
251
+ sp->ncontinuum = 0;
252
+ sp->nserver_continuum = 0;
253
+ sp->continuum = NULL;
254
+ sp->nlive_server = 0;
255
+ sp->next_rebuild = 0LL;
256
+
257
+ sp->name = cp->name;
258
+ sp->addrstr = cp->listen.pname;
259
+ sp->port = (uint16_t)cp->listen.port;
260
+
261
+ sp->family = cp->listen.info.family;
262
+ sp->addrlen = cp->listen.info.addrlen;
263
+ sp->addr = (struct sockaddr *)&cp->listen.info.addr;
264
+
265
+ sp->key_hash_type = cp->hash;
266
+ sp->key_hash = hash_algos[cp->hash];
267
+ sp->dist_type = cp->distribution;
268
+ sp->hash_tag = cp->hash_tag;
269
+
270
+ sp->redis = cp->redis ? 1 : 0;
271
+ sp->timeout = cp->timeout;
272
+ sp->backlog = cp->backlog;
273
+
274
+ sp->client_connections = (uint32_t)cp->client_connections;
275
+
276
+ sp->server_connections = (uint32_t)cp->server_connections;
277
+ sp->server_retry_timeout = (int64_t)cp->server_retry_timeout * 1000LL;
278
+ sp->server_failure_limit = (uint32_t)cp->server_failure_limit;
279
+ sp->auto_eject_hosts = cp->auto_eject_hosts ? 1 : 0;
280
+ sp->preconnect = cp->preconnect ? 1 : 0;
281
+
282
+ status = server_init(&sp->server, &cp->server, sp);
283
+ if (status != NC_OK) {
284
+ return status;
285
+ }
286
+
287
+ log_debug(LOG_VERB, "transform to pool %"PRIu32" '%.*s'", sp->idx,
288
+ sp->name.len, sp->name.data);
289
+
290
+ return NC_OK;
291
+ }
292
+
293
+ static void
294
+ conf_dump(struct conf *cf)
295
+ {
296
+ uint32_t i, j, npool, nserver;
297
+ struct conf_pool *cp;
298
+ struct string *s;
299
+
300
+ npool = array_n(&cf->pool);
301
+ if (npool == 0) {
302
+ return;
303
+ }
304
+
305
+ log_debug(LOG_VVERB, "%"PRIu32" pools in configuration file '%s'", npool,
306
+ cf->fname);
307
+
308
+ for (i = 0; i < npool; i++) {
309
+ cp = array_get(&cf->pool, i);
310
+
311
+ log_debug(LOG_VVERB, "%.*s", cp->name.len, cp->name.data);
312
+ log_debug(LOG_VVERB, " listen: %.*s",
313
+ cp->listen.pname.len, cp->listen.pname.data);
314
+ log_debug(LOG_VVERB, " timeout: %d", cp->timeout);
315
+ log_debug(LOG_VVERB, " backlog: %d", cp->backlog);
316
+ log_debug(LOG_VVERB, " hash: %d", cp->hash);
317
+ log_debug(LOG_VVERB, " hash_tag: \"%.*s\"", cp->hash_tag.len,
318
+ cp->hash_tag.data);
319
+ log_debug(LOG_VVERB, " distribution: %d", cp->distribution);
320
+ log_debug(LOG_VVERB, " client_connections: %d",
321
+ cp->client_connections);
322
+ log_debug(LOG_VVERB, " redis: %d", cp->redis);
323
+ log_debug(LOG_VVERB, " preconnect: %d", cp->preconnect);
324
+ log_debug(LOG_VVERB, " auto_eject_hosts: %d", cp->auto_eject_hosts);
325
+ log_debug(LOG_VVERB, " server_connections: %d",
326
+ cp->server_connections);
327
+ log_debug(LOG_VVERB, " server_retry_timeout: %d",
328
+ cp->server_retry_timeout);
329
+ log_debug(LOG_VVERB, " server_failure_limit: %d",
330
+ cp->server_failure_limit);
331
+
332
+ nserver = array_n(&cp->server);
333
+ log_debug(LOG_VVERB, " servers: %"PRIu32"", nserver);
334
+
335
+ for (j = 0; j < nserver; j++) {
336
+ s = array_get(&cp->server, j);
337
+ log_debug(LOG_VVERB, " %.*s", s->len, s->data);
338
+ }
339
+ }
340
+ }
341
+
342
+ static rstatus_t
343
+ conf_yaml_init(struct conf *cf)
344
+ {
345
+ int rv;
346
+
347
+ ASSERT(!cf->valid_parser);
348
+
349
+ rv = fseek(cf->fh, 0L, SEEK_SET);
350
+ if (rv < 0) {
351
+ log_error("conf: failed to seek to the beginning of file '%s': %s",
352
+ cf->fname, strerror(errno));
353
+ return NC_ERROR;
354
+ }
355
+
356
+ rv = yaml_parser_initialize(&cf->parser);
357
+ if (!rv) {
358
+ log_error("conf: failed (err %d) to initialize yaml parser",
359
+ cf->parser.error);
360
+ return NC_ERROR;
361
+ }
362
+
363
+ yaml_parser_set_input_file(&cf->parser, cf->fh);
364
+ cf->valid_parser = 1;
365
+
366
+ return NC_OK;
367
+ }
368
+
369
+ static void
370
+ conf_yaml_deinit(struct conf *cf)
371
+ {
372
+ if (cf->valid_parser) {
373
+ yaml_parser_delete(&cf->parser);
374
+ cf->valid_parser = 0;
375
+ }
376
+ }
377
+
378
+ static rstatus_t
379
+ conf_token_next(struct conf *cf)
380
+ {
381
+ int rv;
382
+
383
+ ASSERT(cf->valid_parser && !cf->valid_token);
384
+
385
+ rv = yaml_parser_scan(&cf->parser, &cf->token);
386
+ if (!rv) {
387
+ log_error("conf: failed (err %d) to scan next token", cf->parser.error);
388
+ return NC_ERROR;
389
+ }
390
+ cf->valid_token = 1;
391
+
392
+ return NC_OK;
393
+ }
394
+
395
+ static void
396
+ conf_token_done(struct conf *cf)
397
+ {
398
+ ASSERT(cf->valid_parser);
399
+
400
+ if (cf->valid_token) {
401
+ yaml_token_delete(&cf->token);
402
+ cf->valid_token = 0;
403
+ }
404
+ }
405
+
406
+ static rstatus_t
407
+ conf_event_next(struct conf *cf)
408
+ {
409
+ int rv;
410
+
411
+ ASSERT(cf->valid_parser && !cf->valid_event);
412
+
413
+ rv = yaml_parser_parse(&cf->parser, &cf->event);
414
+ if (!rv) {
415
+ log_error("conf: failed (err %d) to get next event", cf->parser.error);
416
+ return NC_ERROR;
417
+ }
418
+ cf->valid_event = 1;
419
+
420
+ return NC_OK;
421
+ }
422
+
423
+ static void
424
+ conf_event_done(struct conf *cf)
425
+ {
426
+ if (cf->valid_event) {
427
+ yaml_event_delete(&cf->event);
428
+ cf->valid_event = 0;
429
+ }
430
+ }
431
+
432
+ static rstatus_t
433
+ conf_push_scalar(struct conf *cf)
434
+ {
435
+ rstatus_t status;
436
+ struct string *value;
437
+ uint8_t *scalar;
438
+ uint32_t scalar_len;
439
+
440
+ scalar = cf->event.data.scalar.value;
441
+ scalar_len = (uint32_t)cf->event.data.scalar.length;
442
+
443
+ log_debug(LOG_VVERB, "push '%.*s'", scalar_len, scalar);
444
+
445
+ value = array_push(&cf->arg);
446
+ if (value == NULL) {
447
+ return NC_ENOMEM;
448
+ }
449
+ string_init(value);
450
+
451
+ status = string_copy(value, scalar, scalar_len);
452
+ if (status != NC_OK) {
453
+ array_pop(&cf->arg);
454
+ return status;
455
+ }
456
+
457
+ return NC_OK;
458
+ }
459
+
460
+ static void
461
+ conf_pop_scalar(struct conf *cf)
462
+ {
463
+ struct string *value;
464
+
465
+ value = array_pop(&cf->arg);
466
+ log_debug(LOG_VVERB, "pop '%.*s'", value->len, value->data);
467
+ string_deinit(value);
468
+ }
469
+
470
+ static rstatus_t
471
+ conf_handler(struct conf *cf, void *data)
472
+ {
473
+ struct command *cmd;
474
+ struct string *key, *value;
475
+ uint32_t narg;
476
+
477
+ if (array_n(&cf->arg) == 1) {
478
+ value = array_top(&cf->arg);
479
+ log_debug(LOG_VVERB, "conf handler on '%.*s'", value->len, value->data);
480
+ return conf_pool_init(data, value);
481
+ }
482
+
483
+ narg = array_n(&cf->arg);
484
+ value = array_get(&cf->arg, narg - 1);
485
+ key = array_get(&cf->arg, narg - 2);
486
+
487
+ log_debug(LOG_VVERB, "conf handler on %.*s: %.*s", key->len, key->data,
488
+ value->len, value->data);
489
+
490
+ for (cmd = conf_commands; cmd->name.len != 0; cmd++) {
491
+ char *rv;
492
+
493
+ if (string_compare(key, &cmd->name) != 0) {
494
+ continue;
495
+ }
496
+
497
+ rv = cmd->set(cf, cmd, data);
498
+ if (rv != CONF_OK) {
499
+ log_error("conf: directive \"%.*s\" %s", key->len, key->data, rv);
500
+ return NC_ERROR;
501
+ }
502
+
503
+ return NC_OK;
504
+ }
505
+
506
+ log_error("conf: directive \"%.*s\" is unknown", key->len, key->data);
507
+
508
+ return NC_ERROR;
509
+ }
510
+
511
+ static rstatus_t
512
+ conf_begin_parse(struct conf *cf)
513
+ {
514
+ rstatus_t status;
515
+ bool done;
516
+
517
+ ASSERT(cf->sound && !cf->parsed);
518
+ ASSERT(cf->depth == 0);
519
+
520
+ status = conf_yaml_init(cf);
521
+ if (status != NC_OK) {
522
+ return status;
523
+ }
524
+
525
+ done = false;
526
+ do {
527
+ status = conf_event_next(cf);
528
+ if (status != NC_OK) {
529
+ return status;
530
+ }
531
+
532
+ log_debug(LOG_VVERB, "next begin event %d", cf->event.type);
533
+
534
+ switch (cf->event.type) {
535
+ case YAML_STREAM_START_EVENT:
536
+ case YAML_DOCUMENT_START_EVENT:
537
+ break;
538
+
539
+ case YAML_MAPPING_START_EVENT:
540
+ ASSERT(cf->depth < CONF_MAX_DEPTH);
541
+ cf->depth++;
542
+ done = true;
543
+ break;
544
+
545
+ default:
546
+ NOT_REACHED();
547
+ }
548
+
549
+ conf_event_done(cf);
550
+
551
+ } while (!done);
552
+
553
+ return NC_OK;
554
+ }
555
+
556
+ static rstatus_t
557
+ conf_end_parse(struct conf *cf)
558
+ {
559
+ rstatus_t status;
560
+ bool done;
561
+
562
+ ASSERT(cf->sound && !cf->parsed);
563
+ ASSERT(cf->depth == 0);
564
+
565
+ done = false;
566
+ do {
567
+ status = conf_event_next(cf);
568
+ if (status != NC_OK) {
569
+ return status;
570
+ }
571
+
572
+ log_debug(LOG_VVERB, "next end event %d", cf->event.type);
573
+
574
+ switch (cf->event.type) {
575
+ case YAML_STREAM_END_EVENT:
576
+ done = true;
577
+ break;
578
+
579
+ case YAML_DOCUMENT_END_EVENT:
580
+ break;
581
+
582
+ default:
583
+ NOT_REACHED();
584
+ }
585
+
586
+ conf_event_done(cf);
587
+ } while (!done);
588
+
589
+ conf_yaml_deinit(cf);
590
+
591
+ return NC_OK;
592
+ }
593
+
594
+ static rstatus_t
595
+ conf_parse_core(struct conf *cf, void *data)
596
+ {
597
+ rstatus_t status;
598
+ bool done, leaf, new_pool;
599
+
600
+ ASSERT(cf->sound);
601
+
602
+ status = conf_event_next(cf);
603
+ if (status != NC_OK) {
604
+ return status;
605
+ }
606
+
607
+ log_debug(LOG_VVERB, "next event %d depth %"PRIu32" seq %d", cf->event.type,
608
+ cf->depth, cf->seq);
609
+
610
+ done = false;
611
+ leaf = false;
612
+ new_pool = false;
613
+
614
+ switch (cf->event.type) {
615
+ case YAML_MAPPING_END_EVENT:
616
+ cf->depth--;
617
+ if (cf->depth == 1) {
618
+ conf_pop_scalar(cf);
619
+ } else if (cf->depth == 0) {
620
+ done = true;
621
+ }
622
+ break;
623
+
624
+ case YAML_MAPPING_START_EVENT:
625
+ cf->depth++;
626
+ break;
627
+
628
+ case YAML_SEQUENCE_START_EVENT:
629
+ cf->seq = 1;
630
+ break;
631
+
632
+ case YAML_SEQUENCE_END_EVENT:
633
+ conf_pop_scalar(cf);
634
+ cf->seq = 0;
635
+ break;
636
+
637
+ case YAML_SCALAR_EVENT:
638
+ status = conf_push_scalar(cf);
639
+ if (status != NC_OK) {
640
+ break;
641
+ }
642
+
643
+ /* take appropriate action */
644
+ if (cf->seq) {
645
+ /* for a sequence, leaf is at CONF_MAX_DEPTH */
646
+ ASSERT(cf->depth == CONF_MAX_DEPTH);
647
+ leaf = true;
648
+ } else if (cf->depth == CONF_ROOT_DEPTH) {
649
+ /* create new conf_pool */
650
+ data = array_push(&cf->pool);
651
+ if (data == NULL) {
652
+ status = NC_ENOMEM;
653
+ break;
654
+ }
655
+ new_pool = true;
656
+ } else if (array_n(&cf->arg) == cf->depth + 1) {
657
+ /* for {key: value}, leaf is at CONF_MAX_DEPTH */
658
+ ASSERT(cf->depth == CONF_MAX_DEPTH);
659
+ leaf = true;
660
+ }
661
+ break;
662
+
663
+ default:
664
+ NOT_REACHED();
665
+ break;
666
+ }
667
+
668
+ conf_event_done(cf);
669
+
670
+ if (status != NC_OK) {
671
+ return status;
672
+ }
673
+
674
+ if (done) {
675
+ /* terminating condition */
676
+ return NC_OK;
677
+ }
678
+
679
+ if (leaf || new_pool) {
680
+ status = conf_handler(cf, data);
681
+
682
+ if (leaf) {
683
+ conf_pop_scalar(cf);
684
+ if (!cf->seq) {
685
+ conf_pop_scalar(cf);
686
+ }
687
+ }
688
+
689
+ if (status != NC_OK) {
690
+ return status;
691
+ }
692
+ }
693
+
694
+ return conf_parse_core(cf, data);
695
+ }
696
+
697
+ static rstatus_t
698
+ conf_parse(struct conf *cf)
699
+ {
700
+ rstatus_t status;
701
+
702
+ ASSERT(cf->sound && !cf->parsed);
703
+ ASSERT(array_n(&cf->arg) == 0);
704
+
705
+ status = conf_begin_parse(cf);
706
+ if (status != NC_OK) {
707
+ return status;
708
+ }
709
+
710
+ status = conf_parse_core(cf, NULL);
711
+ if (status != NC_OK) {
712
+ return status;
713
+ }
714
+
715
+ status = conf_end_parse(cf);
716
+ if (status != NC_OK) {
717
+ return status;
718
+ }
719
+
720
+ cf->parsed = 1;
721
+
722
+ return NC_OK;
723
+ }
724
+
725
+ static struct conf *
726
+ conf_open(char *filename)
727
+ {
728
+ rstatus_t status;
729
+ struct conf *cf;
730
+ FILE *fh;
731
+
732
+ fh = fopen(filename, "r");
733
+ if (fh == NULL) {
734
+ log_error("conf: failed to open configuration '%s': %s", filename,
735
+ strerror(errno));
736
+ return NULL;
737
+ }
738
+
739
+ cf = nc_alloc(sizeof(*cf));
740
+ if (cf == NULL) {
741
+ fclose(fh);
742
+ return NULL;
743
+ }
744
+
745
+ status = array_init(&cf->arg, CONF_DEFAULT_ARGS, sizeof(struct string));
746
+ if (status != NC_OK) {
747
+ nc_free(cf);
748
+ fclose(fh);
749
+ return NULL;
750
+ }
751
+
752
+ status = array_init(&cf->pool, CONF_DEFAULT_POOL, sizeof(struct conf_pool));
753
+ if (status != NC_OK) {
754
+ array_deinit(&cf->arg);
755
+ nc_free(cf);
756
+ fclose(fh);
757
+ return NULL;
758
+ }
759
+
760
+ cf->fname = filename;
761
+ cf->fh = fh;
762
+ cf->depth = 0;
763
+ /* parser, event, and token are initialized later */
764
+ cf->seq = 0;
765
+ cf->valid_parser = 0;
766
+ cf->valid_event = 0;
767
+ cf->valid_token = 0;
768
+ cf->sound = 0;
769
+ cf->parsed = 0;
770
+ cf->valid = 0;
771
+
772
+ log_debug(LOG_VVERB, "opened conf '%s'", filename);
773
+
774
+ return cf;
775
+ }
776
+
777
+ static rstatus_t
778
+ conf_validate_document(struct conf *cf)
779
+ {
780
+ rstatus_t status;
781
+ uint32_t count;
782
+ bool done;
783
+
784
+ status = conf_yaml_init(cf);
785
+ if (status != NC_OK) {
786
+ return status;
787
+ }
788
+
789
+ count = 0;
790
+ done = false;
791
+ do {
792
+ yaml_document_t document;
793
+ yaml_node_t *node;
794
+ int rv;
795
+
796
+ rv = yaml_parser_load(&cf->parser, &document);
797
+ if (!rv) {
798
+ log_error("conf: failed (err %d) to get the next yaml document",
799
+ cf->parser.error);
800
+ conf_yaml_deinit(cf);
801
+ return NC_ERROR;
802
+ }
803
+
804
+ node = yaml_document_get_root_node(&document);
805
+ if (node == NULL) {
806
+ done = true;
807
+ } else {
808
+ count++;
809
+ }
810
+
811
+ yaml_document_delete(&document);
812
+ } while (!done);
813
+
814
+ conf_yaml_deinit(cf);
815
+
816
+ if (count != 1) {
817
+ log_error("conf: '%s' must contain only 1 document; found %"PRIu32" "
818
+ "documents", cf->fname, count);
819
+ return NC_ERROR;
820
+ }
821
+
822
+ return NC_OK;
823
+ }
824
+
825
+ static rstatus_t
826
+ conf_validate_tokens(struct conf *cf)
827
+ {
828
+ rstatus_t status;
829
+ bool done, error;
830
+ int type;
831
+
832
+ status = conf_yaml_init(cf);
833
+ if (status != NC_OK) {
834
+ return status;
835
+ }
836
+
837
+ done = false;
838
+ error = false;
839
+ do {
840
+ status = conf_token_next(cf);
841
+ if (status != NC_OK) {
842
+ return status;
843
+ }
844
+ type = cf->token.type;
845
+
846
+ switch (type) {
847
+ case YAML_NO_TOKEN:
848
+ error = true;
849
+ log_error("conf: no token (%d) is disallowed", type);
850
+ break;
851
+
852
+ case YAML_VERSION_DIRECTIVE_TOKEN:
853
+ error = true;
854
+ log_error("conf: version directive token (%d) is disallowed", type);
855
+ break;
856
+
857
+ case YAML_TAG_DIRECTIVE_TOKEN:
858
+ error = true;
859
+ log_error("conf: tag directive token (%d) is disallowed", type);
860
+ break;
861
+
862
+ case YAML_DOCUMENT_START_TOKEN:
863
+ error = true;
864
+ log_error("conf: document start token (%d) is disallowed", type);
865
+ break;
866
+
867
+ case YAML_DOCUMENT_END_TOKEN:
868
+ error = true;
869
+ log_error("conf: document end token (%d) is disallowed", type);
870
+ break;
871
+
872
+ case YAML_FLOW_SEQUENCE_START_TOKEN:
873
+ error = true;
874
+ log_error("conf: flow sequence start token (%d) is disallowed", type);
875
+ break;
876
+
877
+ case YAML_FLOW_SEQUENCE_END_TOKEN:
878
+ error = true;
879
+ log_error("conf: flow sequence end token (%d) is disallowed", type);
880
+ break;
881
+
882
+ case YAML_FLOW_MAPPING_START_TOKEN:
883
+ error = true;
884
+ log_error("conf: flow mapping start token (%d) is disallowed", type);
885
+ break;
886
+
887
+ case YAML_FLOW_MAPPING_END_TOKEN:
888
+ error = true;
889
+ log_error("conf: flow mapping end token (%d) is disallowed", type);
890
+ break;
891
+
892
+ case YAML_FLOW_ENTRY_TOKEN:
893
+ error = true;
894
+ log_error("conf: flow entry token (%d) is disallowed", type);
895
+ break;
896
+
897
+ case YAML_ALIAS_TOKEN:
898
+ error = true;
899
+ log_error("conf: alias token (%d) is disallowed", type);
900
+ break;
901
+
902
+ case YAML_ANCHOR_TOKEN:
903
+ error = true;
904
+ log_error("conf: anchor token (%d) is disallowed", type);
905
+ break;
906
+
907
+ case YAML_TAG_TOKEN:
908
+ error = true;
909
+ log_error("conf: tag token (%d) is disallowed", type);
910
+ break;
911
+
912
+ case YAML_BLOCK_SEQUENCE_START_TOKEN:
913
+ case YAML_BLOCK_MAPPING_START_TOKEN:
914
+ case YAML_BLOCK_END_TOKEN:
915
+ case YAML_BLOCK_ENTRY_TOKEN:
916
+ break;
917
+
918
+ case YAML_KEY_TOKEN:
919
+ case YAML_VALUE_TOKEN:
920
+ case YAML_SCALAR_TOKEN:
921
+ break;
922
+
923
+ case YAML_STREAM_START_TOKEN:
924
+ break;
925
+
926
+ case YAML_STREAM_END_TOKEN:
927
+ done = true;
928
+ log_debug(LOG_VVERB, "conf '%s' has valid tokens", cf->fname);
929
+ break;
930
+
931
+ default:
932
+ error = true;
933
+ log_error("conf: unknown token (%d) is disallowed", type);
934
+ break;
935
+ }
936
+
937
+ conf_token_done(cf);
938
+ } while (!done && !error);
939
+
940
+ conf_yaml_deinit(cf);
941
+
942
+ return !error ? NC_OK : NC_ERROR;
943
+ }
944
+
945
+ static rstatus_t
946
+ conf_validate_structure(struct conf *cf)
947
+ {
948
+ rstatus_t status;
949
+ int type, depth;
950
+ uint32_t i, count[CONF_MAX_DEPTH + 1];
951
+ bool done, error, seq;
952
+
953
+ status = conf_yaml_init(cf);
954
+ if (status != NC_OK) {
955
+ return status;
956
+ }
957
+
958
+ done = false;
959
+ error = false;
960
+ seq = false;
961
+ depth = 0;
962
+ for (i = 0; i < CONF_MAX_DEPTH + 1; i++) {
963
+ count[i] = 0;
964
+ }
965
+
966
+ /*
967
+ * Validate that the configuration conforms roughly to the following
968
+ * yaml tree structure:
969
+ *
970
+ * keyx:
971
+ * key1: value1
972
+ * key2: value2
973
+ * seq:
974
+ * - elem1
975
+ * - elem2
976
+ * - elem3
977
+ * key3: value3
978
+ *
979
+ * keyy:
980
+ * key1: value1
981
+ * key2: value2
982
+ * seq:
983
+ * - elem1
984
+ * - elem2
985
+ * - elem3
986
+ * key3: value3
987
+ */
988
+ do {
989
+ status = conf_event_next(cf);
990
+ if (status != NC_OK) {
991
+ return status;
992
+ }
993
+
994
+ type = cf->event.type;
995
+
996
+ log_debug(LOG_VVERB, "next event %d depth %d seq %d", type, depth, seq);
997
+
998
+ switch (type) {
999
+ case YAML_STREAM_START_EVENT:
1000
+ case YAML_DOCUMENT_START_EVENT:
1001
+ break;
1002
+
1003
+ case YAML_DOCUMENT_END_EVENT:
1004
+ break;
1005
+
1006
+ case YAML_STREAM_END_EVENT:
1007
+ done = true;
1008
+ break;
1009
+
1010
+ case YAML_MAPPING_START_EVENT:
1011
+ if (depth == CONF_ROOT_DEPTH && count[depth] != 1) {
1012
+ error = true;
1013
+ log_error("conf: '%s' has more than one \"key:value\" at depth"
1014
+ " %d", cf->fname, depth);
1015
+ } else if (depth >= CONF_MAX_DEPTH) {
1016
+ error = true;
1017
+ log_error("conf: '%s' has a depth greater than %d", cf->fname,
1018
+ CONF_MAX_DEPTH);
1019
+ }
1020
+ depth++;
1021
+ break;
1022
+
1023
+ case YAML_MAPPING_END_EVENT:
1024
+ if (depth == CONF_MAX_DEPTH) {
1025
+ if (seq) {
1026
+ seq = false;
1027
+ } else {
1028
+ error = true;
1029
+ log_error("conf: '%s' missing sequence directive at depth "
1030
+ "%d", cf->fname, depth);
1031
+ }
1032
+ }
1033
+ depth--;
1034
+ count[depth] = 0;
1035
+ break;
1036
+
1037
+ case YAML_SEQUENCE_START_EVENT:
1038
+ if (seq) {
1039
+ error = true;
1040
+ log_error("conf: '%s' has more than one sequence directive",
1041
+ cf->fname);
1042
+ } else if (depth != CONF_MAX_DEPTH) {
1043
+ error = true;
1044
+ log_error("conf: '%s' has sequence at depth %d instead of %d",
1045
+ cf->fname, depth, CONF_MAX_DEPTH);
1046
+ } else if (count[depth] != 1) {
1047
+ error = true;
1048
+ log_error("conf: '%s' has invalid \"key:value\" at depth %d",
1049
+ cf->fname, depth);
1050
+ }
1051
+ seq = true;
1052
+ break;
1053
+
1054
+ case YAML_SEQUENCE_END_EVENT:
1055
+ ASSERT(depth == CONF_MAX_DEPTH);
1056
+ count[depth] = 0;
1057
+ break;
1058
+
1059
+ case YAML_SCALAR_EVENT:
1060
+ if (depth == 0) {
1061
+ error = true;
1062
+ log_error("conf: '%s' has invalid empty \"key:\" at depth %d",
1063
+ cf->fname, depth);
1064
+ } else if (depth == CONF_ROOT_DEPTH && count[depth] != 0) {
1065
+ error = true;
1066
+ log_error("conf: '%s' has invalid mapping \"key:\" at depth %d",
1067
+ cf->fname, depth);
1068
+ } else if (depth == CONF_MAX_DEPTH && count[depth] == 2) {
1069
+ /* found a "key: value", resetting! */
1070
+ count[depth] = 0;
1071
+ }
1072
+ count[depth]++;
1073
+ break;
1074
+
1075
+ default:
1076
+ NOT_REACHED();
1077
+ }
1078
+
1079
+ conf_event_done(cf);
1080
+ } while (!done && !error);
1081
+
1082
+ conf_yaml_deinit(cf);
1083
+
1084
+ return !error ? NC_OK : NC_ERROR;
1085
+ }
1086
+
1087
+ static rstatus_t
1088
+ conf_pre_validate(struct conf *cf)
1089
+ {
1090
+ rstatus_t status;
1091
+
1092
+ status = conf_validate_document(cf);
1093
+ if (status != NC_OK) {
1094
+ return status;
1095
+ }
1096
+
1097
+ status = conf_validate_tokens(cf);
1098
+ if (status != NC_OK) {
1099
+ return status;
1100
+ }
1101
+
1102
+ status = conf_validate_structure(cf);
1103
+ if (status != NC_OK) {
1104
+ return status;
1105
+ }
1106
+
1107
+ cf->sound = 1;
1108
+
1109
+ return NC_OK;
1110
+ }
1111
+
1112
+ static int
1113
+ conf_server_pname_cmp(const void *t1, const void *t2)
1114
+ {
1115
+ const struct conf_server *s1 = t1, *s2 = t2;
1116
+
1117
+ return string_compare(&s1->pname, &s2->pname);
1118
+ }
1119
+
1120
+ static int
1121
+ conf_server_name_cmp(const void *t1, const void *t2)
1122
+ {
1123
+ const struct conf_server *s1 = t1, *s2 = t2;
1124
+
1125
+ return string_compare(&s1->name, &s2->name);
1126
+ }
1127
+
1128
+ static int
1129
+ conf_pool_name_cmp(const void *t1, const void *t2)
1130
+ {
1131
+ const struct conf_pool *p1 = t1, *p2 = t2;
1132
+
1133
+ return string_compare(&p1->name, &p2->name);
1134
+ }
1135
+
1136
+ static int
1137
+ conf_pool_listen_cmp(const void *t1, const void *t2)
1138
+ {
1139
+ const struct conf_pool *p1 = t1, *p2 = t2;
1140
+
1141
+ return string_compare(&p1->listen.pname, &p2->listen.pname);
1142
+ }
1143
+
1144
+ static rstatus_t
1145
+ conf_validate_server(struct conf *cf, struct conf_pool *cp)
1146
+ {
1147
+ uint32_t i, nserver;
1148
+ bool valid;
1149
+
1150
+ nserver = array_n(&cp->server);
1151
+ if (nserver == 0) {
1152
+ log_error("conf: pool '%.*s' has no servers", cp->name.len,
1153
+ cp->name.data);
1154
+ return NC_ERROR;
1155
+ }
1156
+
1157
+ /*
1158
+ * Disallow duplicate servers - servers with identical "host:port:weight"
1159
+ * or "name" combination are considered as duplicates
1160
+ */
1161
+ array_sort(&cp->server, conf_server_pname_cmp);
1162
+ for (valid = true, i = 0; i < nserver - 1; i++) {
1163
+ struct conf_server *cs1, *cs2;
1164
+
1165
+ cs1 = array_get(&cp->server, i);
1166
+ cs2 = array_get(&cp->server, i + 1);
1167
+
1168
+ if (string_compare(&cs1->pname, &cs2->pname) == 0) {
1169
+ log_error("conf: pool '%.*s' has servers with same name '%.*s'",
1170
+ cp->name.len, cp->name.data, cs1->pname.len,
1171
+ cs1->pname.data);
1172
+ valid = false;
1173
+ break;
1174
+ }
1175
+ }
1176
+ if (!valid) {
1177
+ return NC_ERROR;
1178
+ }
1179
+
1180
+ array_sort(&cp->server, conf_server_name_cmp);
1181
+ for (valid = true, i = 0; i < nserver - 1; i++) {
1182
+ struct conf_server *cs1, *cs2;
1183
+
1184
+ cs1 = array_get(&cp->server, i);
1185
+ cs2 = array_get(&cp->server, i + 1);
1186
+
1187
+ if (string_compare(&cs1->name, &cs2->name) == 0) {
1188
+ log_error("conf: pool '%.*s' has servers with same name '%.*s'",
1189
+ cp->name.len, cp->name.data, cs1->name.len,
1190
+ cs1->name.data);
1191
+ valid = false;
1192
+ break;
1193
+ }
1194
+ }
1195
+ if (!valid) {
1196
+ return NC_ERROR;
1197
+ }
1198
+
1199
+ return NC_OK;
1200
+ }
1201
+
1202
+ static rstatus_t
1203
+ conf_validate_pool(struct conf *cf, struct conf_pool *cp)
1204
+ {
1205
+ rstatus_t status;
1206
+
1207
+ ASSERT(!cp->valid);
1208
+ ASSERT(!string_empty(&cp->name));
1209
+
1210
+ if (!cp->listen.valid) {
1211
+ log_error("conf: directive \"listen:\" is missing");
1212
+ return NC_ERROR;
1213
+ }
1214
+
1215
+ /* set default values for unset directives */
1216
+
1217
+ if (cp->distribution == CONF_UNSET_DIST) {
1218
+ cp->distribution = CONF_DEFAULT_DIST;
1219
+ }
1220
+
1221
+ if (cp->hash == CONF_UNSET_HASH) {
1222
+ cp->hash = CONF_DEFAULT_HASH;
1223
+ }
1224
+
1225
+ if (cp->timeout == CONF_UNSET_NUM) {
1226
+ cp->timeout = CONF_DEFAULT_TIMEOUT;
1227
+ }
1228
+
1229
+ if (cp->backlog == CONF_UNSET_NUM) {
1230
+ cp->backlog = CONF_DEFAULT_LISTEN_BACKLOG;
1231
+ }
1232
+
1233
+ cp->client_connections = CONF_DEFAULT_CLIENT_CONNECTIONS;
1234
+
1235
+ if (cp->redis == CONF_UNSET_NUM) {
1236
+ cp->redis = CONF_DEFAULT_REDIS;
1237
+ }
1238
+
1239
+ if (cp->preconnect == CONF_UNSET_NUM) {
1240
+ cp->preconnect = CONF_DEFAULT_PRECONNECT;
1241
+ }
1242
+
1243
+ if (cp->auto_eject_hosts == CONF_UNSET_NUM) {
1244
+ cp->auto_eject_hosts = CONF_DEFAULT_AUTO_EJECT_HOSTS;
1245
+ }
1246
+
1247
+ if (cp->server_connections == CONF_UNSET_NUM) {
1248
+ cp->server_connections = CONF_DEFAULT_SERVER_CONNECTIONS;
1249
+ } else if (cp->server_connections == 0) {
1250
+ log_error("conf: directive \"server_connections:\" cannot be 0");
1251
+ return NC_ERROR;
1252
+ }
1253
+
1254
+ if (cp->server_retry_timeout == CONF_UNSET_NUM) {
1255
+ cp->server_retry_timeout = CONF_DEFAULT_SERVER_RETRY_TIMEOUT;
1256
+ }
1257
+
1258
+ if (cp->server_failure_limit == CONF_UNSET_NUM) {
1259
+ cp->server_failure_limit = CONF_DEFAULT_SERVER_FAILURE_LIMIT;
1260
+ }
1261
+
1262
+ status = conf_validate_server(cf, cp);
1263
+ if (status != NC_OK) {
1264
+ return status;
1265
+ }
1266
+
1267
+ cp->valid = 1;
1268
+
1269
+ return NC_OK;
1270
+ }
1271
+
1272
+ static rstatus_t
1273
+ conf_post_validate(struct conf *cf)
1274
+ {
1275
+ rstatus_t status;
1276
+ uint32_t i, npool;
1277
+ bool valid;
1278
+
1279
+ ASSERT(cf->sound && cf->parsed);
1280
+ ASSERT(!cf->valid);
1281
+
1282
+ npool = array_n(&cf->pool);
1283
+ if (npool == 0) {
1284
+ log_error("conf: '%.*s' has no pools", cf->fname);
1285
+ return NC_ERROR;
1286
+ }
1287
+
1288
+ /* validate pool */
1289
+ for (i = 0; i < npool; i++) {
1290
+ struct conf_pool *cp = array_get(&cf->pool, i);
1291
+
1292
+ status = conf_validate_pool(cf, cp);
1293
+ if (status != NC_OK) {
1294
+ return status;
1295
+ }
1296
+ }
1297
+
1298
+ /* disallow pools with duplicate listen: key values */
1299
+ array_sort(&cf->pool, conf_pool_listen_cmp);
1300
+ for (valid = true, i = 0; i < npool - 1; i++) {
1301
+ struct conf_pool *p1, *p2;
1302
+
1303
+ p1 = array_get(&cf->pool, i);
1304
+ p2 = array_get(&cf->pool, i + 1);
1305
+
1306
+ if (string_compare(&p1->listen.pname, &p2->listen.pname) == 0) {
1307
+ log_error("conf: pools '%.*s' and '%.*s' have the same listen "
1308
+ "address '%.*s'", p1->name.len, p1->name.data,
1309
+ p2->name.len, p2->name.data, p1->listen.pname.len,
1310
+ p1->listen.pname.data);
1311
+ valid = false;
1312
+ break;
1313
+ }
1314
+ }
1315
+ if (!valid) {
1316
+ return NC_ERROR;
1317
+ }
1318
+
1319
+ /* disallow pools with duplicate names */
1320
+ array_sort(&cf->pool, conf_pool_name_cmp);
1321
+ for (valid = true, i = 0; i < npool - 1; i++) {
1322
+ struct conf_pool *p1, *p2;
1323
+
1324
+ p1 = array_get(&cf->pool, i);
1325
+ p2 = array_get(&cf->pool, i + 1);
1326
+
1327
+ if (string_compare(&p1->name, &p2->name) == 0) {
1328
+ log_error("conf: '%s' has pools with same name %.*s'", cf->fname,
1329
+ p1->name.len, p1->name.data);
1330
+ valid = false;
1331
+ break;
1332
+ }
1333
+ }
1334
+ if (!valid) {
1335
+ return NC_ERROR;
1336
+ }
1337
+
1338
+ return NC_OK;
1339
+ }
1340
+
1341
+ struct conf *
1342
+ conf_create(char *filename)
1343
+ {
1344
+ rstatus_t status;
1345
+ struct conf *cf;
1346
+
1347
+ cf = conf_open(filename);
1348
+ if (cf == NULL) {
1349
+ return NULL;
1350
+ }
1351
+
1352
+ /* validate configuration file before parsing */
1353
+ status = conf_pre_validate(cf);
1354
+ if (status != NC_OK) {
1355
+ goto error;
1356
+ }
1357
+
1358
+ /* parse the configuration file */
1359
+ status = conf_parse(cf);
1360
+ if (status != NC_OK) {
1361
+ goto error;
1362
+ }
1363
+
1364
+ /* validate parsed configuration */
1365
+ status = conf_post_validate(cf);
1366
+ if (status != NC_OK) {
1367
+ goto error;
1368
+ }
1369
+
1370
+ conf_dump(cf);
1371
+
1372
+ fclose(cf->fh);
1373
+ cf->fh = NULL;
1374
+
1375
+ return cf;
1376
+
1377
+ error:
1378
+ fclose(cf->fh);
1379
+ cf->fh = NULL;
1380
+ conf_destroy(cf);
1381
+ return NULL;
1382
+ }
1383
+
1384
+ void
1385
+ conf_destroy(struct conf *cf)
1386
+ {
1387
+ while (array_n(&cf->arg) != 0) {
1388
+ conf_pop_scalar(cf);
1389
+ }
1390
+ array_deinit(&cf->arg);
1391
+
1392
+ while (array_n(&cf->pool) != 0) {
1393
+ conf_pool_deinit(array_pop(&cf->pool));
1394
+ }
1395
+ array_deinit(&cf->pool);
1396
+
1397
+ nc_free(cf);
1398
+ }
1399
+
1400
+ char *
1401
+ conf_set_string(struct conf *cf, struct command *cmd, void *conf)
1402
+ {
1403
+ rstatus_t status;
1404
+ uint8_t *p;
1405
+ struct string *field, *value;
1406
+
1407
+ p = conf;
1408
+ field = (struct string *)(p + cmd->offset);
1409
+
1410
+ if (field->data != CONF_UNSET_PTR) {
1411
+ return "is a duplicate";
1412
+ }
1413
+
1414
+ value = array_top(&cf->arg);
1415
+
1416
+ status = string_duplicate(field, value);
1417
+ if (status != NC_OK) {
1418
+ return CONF_ERROR;
1419
+ }
1420
+
1421
+ return CONF_OK;
1422
+ }
1423
+
1424
+ char *
1425
+ conf_set_listen(struct conf *cf, struct command *cmd, void *conf)
1426
+ {
1427
+ rstatus_t status;
1428
+ struct string *value;
1429
+ struct conf_listen *field;
1430
+ uint8_t *p, *name;
1431
+ uint32_t namelen;
1432
+
1433
+ p = conf;
1434
+ field = (struct conf_listen *)(p + cmd->offset);
1435
+
1436
+ if (field->valid == 1) {
1437
+ return "is a duplicate";
1438
+ }
1439
+
1440
+ value = array_top(&cf->arg);
1441
+
1442
+ status = string_duplicate(&field->pname, value);
1443
+ if (status != NC_OK) {
1444
+ return CONF_ERROR;
1445
+ }
1446
+
1447
+ if (value->data[0] == '/') {
1448
+ name = value->data;
1449
+ namelen = value->len;
1450
+ } else {
1451
+ uint8_t *q, *start, *port;
1452
+ uint32_t portlen;
1453
+
1454
+ /* parse "hostname:port" from the end */
1455
+ p = value->data + value->len - 1;
1456
+ start = value->data;
1457
+ q = nc_strrchr(p, start, ':');
1458
+ if (q == NULL) {
1459
+ return "has an invalid \"hostname:port\" format string";
1460
+ }
1461
+
1462
+ port = q + 1;
1463
+ portlen = (uint32_t)(p - port + 1);
1464
+
1465
+ p = q - 1;
1466
+
1467
+ name = start;
1468
+ namelen = (uint32_t)(p - start + 1);
1469
+
1470
+ field->port = nc_atoi(port, portlen);
1471
+ if (field->port < 0 || !nc_valid_port(field->port)) {
1472
+ return "has an invalid port in \"hostname:port\" format string";
1473
+ }
1474
+ }
1475
+
1476
+ status = string_copy(&field->name, name, namelen);
1477
+ if (status != NC_OK) {
1478
+ return CONF_ERROR;
1479
+ }
1480
+
1481
+ status = nc_resolve(&field->name, field->port, &field->info);
1482
+ if (status != NC_OK) {
1483
+ return CONF_ERROR;
1484
+ }
1485
+
1486
+ field->valid = 1;
1487
+
1488
+ return CONF_OK;
1489
+ }
1490
+
1491
+ char *
1492
+ conf_add_server(struct conf *cf, struct command *cmd, void *conf)
1493
+ {
1494
+ rstatus_t status;
1495
+ struct array *a;
1496
+ struct string *value;
1497
+ struct conf_server *field;
1498
+ uint8_t *p, *q, *start;
1499
+ uint8_t *pname, *addr, *port, *weight, *name;
1500
+ uint32_t k, pnamelen, addrlen, portlen, weightlen, namelen;
1501
+ struct string address;
1502
+ char delim[] = " ::";
1503
+
1504
+ string_init(&address);
1505
+ p = conf;
1506
+ a = (struct array *)(p + cmd->offset);
1507
+
1508
+ field = array_push(a);
1509
+ if (field == NULL) {
1510
+ return CONF_ERROR;
1511
+ }
1512
+
1513
+ conf_server_init(field);
1514
+
1515
+ value = array_top(&cf->arg);
1516
+
1517
+ /* parse "hostname:port:weight [name]" from the end */
1518
+ p = value->data + value->len - 1;
1519
+ start = value->data;
1520
+ addr = NULL;
1521
+ addrlen = 0;
1522
+ weight = NULL;
1523
+ weightlen = 0;
1524
+ port = NULL;
1525
+ portlen = 0;
1526
+ name = NULL;
1527
+ namelen = 0;
1528
+
1529
+ for (k = 0; k < sizeof(delim); k++) {
1530
+ q = nc_strrchr(p, start, delim[k]);
1531
+ if (q == NULL) {
1532
+ if (k == 0) {
1533
+ /*
1534
+ * name in "hostname:port:weight [name]" format string is
1535
+ * optional
1536
+ */
1537
+ continue;
1538
+ }
1539
+ break;
1540
+ }
1541
+
1542
+ switch (k) {
1543
+ case 0:
1544
+ name = q + 1;
1545
+ namelen = (uint32_t)(p - name + 1);
1546
+ break;
1547
+
1548
+ case 1:
1549
+ weight = q + 1;
1550
+ weightlen = (uint32_t)(p - weight + 1);
1551
+ break;
1552
+
1553
+ case 2:
1554
+ port = q + 1;
1555
+ portlen = (uint32_t)(p - port + 1);
1556
+ break;
1557
+
1558
+ default:
1559
+ NOT_REACHED();
1560
+ }
1561
+
1562
+ p = q - 1;
1563
+ }
1564
+
1565
+ if (k != 3) {
1566
+ return "has an invalid \"hostname:port:weight [name]\" format string";
1567
+ }
1568
+
1569
+ pname = value->data;
1570
+ pnamelen = namelen > 0 ? value->len - (namelen + 1) : value->len;
1571
+ status = string_copy(&field->pname, pname, pnamelen);
1572
+ if (status != NC_OK) {
1573
+ array_pop(a);
1574
+ return CONF_ERROR;
1575
+ }
1576
+
1577
+ addr = start;
1578
+ addrlen = (uint32_t)(p - start + 1);
1579
+
1580
+ field->weight = nc_atoi(weight, weightlen);
1581
+ if (field->weight < 0) {
1582
+ return "has an invalid weight in \"hostname:port:weight [name]\" format string";
1583
+ }
1584
+
1585
+ field->port = nc_atoi(port, portlen);
1586
+ if (field->port < 0 || !nc_valid_port(field->port)) {
1587
+ return "has an invalid port in \"hostname:port:weight [name]\" format string";
1588
+ }
1589
+
1590
+ if (name == NULL) {
1591
+ /*
1592
+ * To maintain backward compatibility with libmemcached, we don't
1593
+ * include the port as the part of the input string to the consistent
1594
+ * hashing algorithm, when it is equal to 11211.
1595
+ */
1596
+ if (field->port == CONF_DEFAULT_KETAMA_PORT) {
1597
+ name = addr;
1598
+ namelen = addrlen;
1599
+ } else {
1600
+ name = addr;
1601
+ namelen = addrlen + 1 + portlen;
1602
+ }
1603
+ }
1604
+
1605
+ status = string_copy(&field->name, name, namelen);
1606
+ if (status != NC_OK) {
1607
+ return CONF_ERROR;
1608
+ }
1609
+
1610
+ status = string_copy(&address, addr, addrlen);
1611
+ if (status != NC_OK) {
1612
+ return CONF_ERROR;
1613
+ }
1614
+
1615
+ status = nc_resolve(&address, field->port, &field->info);
1616
+ if (status != NC_OK) {
1617
+ string_deinit(&address);
1618
+ return CONF_ERROR;
1619
+ }
1620
+
1621
+ string_deinit(&address);
1622
+ field->valid = 1;
1623
+
1624
+ return CONF_OK;
1625
+ }
1626
+
1627
+ char *
1628
+ conf_set_num(struct conf *cf, struct command *cmd, void *conf)
1629
+ {
1630
+ uint8_t *p;
1631
+ int num, *np;
1632
+ struct string *value;
1633
+
1634
+ p = conf;
1635
+ np = (int *)(p + cmd->offset);
1636
+
1637
+ if (*np != CONF_UNSET_NUM) {
1638
+ return "is a duplicate";
1639
+ }
1640
+
1641
+ value = array_top(&cf->arg);
1642
+
1643
+ num = nc_atoi(value->data, value->len);
1644
+ if (num < 0) {
1645
+ return "is not a number";
1646
+ }
1647
+
1648
+ *np = num;
1649
+
1650
+ return CONF_OK;
1651
+ }
1652
+
1653
+ char *
1654
+ conf_set_bool(struct conf *cf, struct command *cmd, void *conf)
1655
+ {
1656
+ uint8_t *p;
1657
+ int *bp;
1658
+ struct string *value, true_str, false_str;
1659
+
1660
+ p = conf;
1661
+ bp = (int *)(p + cmd->offset);
1662
+
1663
+ if (*bp != CONF_UNSET_NUM) {
1664
+ return "is a duplicate";
1665
+ }
1666
+
1667
+ value = array_top(&cf->arg);
1668
+ string_set_text(&true_str, "true");
1669
+ string_set_text(&false_str, "false");
1670
+
1671
+ if (string_compare(value, &true_str) == 0) {
1672
+ *bp = 1;
1673
+ } else if (string_compare(value, &false_str) == 0) {
1674
+ *bp = 0;
1675
+ } else {
1676
+ return "is not \"true\" or \"false\"";
1677
+ }
1678
+
1679
+ return CONF_OK;
1680
+ }
1681
+
1682
+ char *
1683
+ conf_set_hash(struct conf *cf, struct command *cmd, void *conf)
1684
+ {
1685
+ uint8_t *p;
1686
+ hash_type_t *hp;
1687
+ struct string *value, *hash;
1688
+
1689
+ p = conf;
1690
+ hp = (hash_type_t *)(p + cmd->offset);
1691
+
1692
+ if (*hp != CONF_UNSET_HASH) {
1693
+ return "is a duplicate";
1694
+ }
1695
+
1696
+ value = array_top(&cf->arg);
1697
+
1698
+ for (hash = hash_strings; hash->len != 0; hash++) {
1699
+ if (string_compare(value, hash) != 0) {
1700
+ continue;
1701
+ }
1702
+
1703
+ *hp = hash - hash_strings;
1704
+
1705
+ return CONF_OK;
1706
+ }
1707
+
1708
+ return "is not a valid hash";
1709
+ }
1710
+
1711
+ char *
1712
+ conf_set_distribution(struct conf *cf, struct command *cmd, void *conf)
1713
+ {
1714
+ uint8_t *p;
1715
+ dist_type_t *dp;
1716
+ struct string *value, *dist;
1717
+
1718
+ p = conf;
1719
+ dp = (dist_type_t *)(p + cmd->offset);
1720
+
1721
+ if (*dp != CONF_UNSET_DIST) {
1722
+ return "is a duplicate";
1723
+ }
1724
+
1725
+ value = array_top(&cf->arg);
1726
+
1727
+ for (dist = dist_strings; dist->len != 0; dist++) {
1728
+ if (string_compare(value, dist) != 0) {
1729
+ continue;
1730
+ }
1731
+
1732
+ *dp = dist - dist_strings;
1733
+
1734
+ return CONF_OK;
1735
+ }
1736
+
1737
+ return "is not a valid distribution";
1738
+ }
1739
+
1740
+ char *
1741
+ conf_set_hashtag(struct conf *cf, struct command *cmd, void *conf)
1742
+ {
1743
+ rstatus_t status;
1744
+ uint8_t *p;
1745
+ struct string *field, *value;
1746
+
1747
+ p = conf;
1748
+ field = (struct string *)(p + cmd->offset);
1749
+
1750
+ if (field->data != CONF_UNSET_PTR) {
1751
+ return "is a duplicate";
1752
+ }
1753
+
1754
+ value = array_top(&cf->arg);
1755
+
1756
+ if (value->len != 2) {
1757
+ return "is not a valid hash tag string with two characters";
1758
+ }
1759
+
1760
+ status = string_duplicate(field, value);
1761
+ if (status != NC_OK) {
1762
+ return CONF_ERROR;
1763
+ }
1764
+
1765
+ return CONF_OK;
1766
+ }