nutcracker 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ }