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