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