char * conf_set_distribution(struct conf *cf, struct command *cmd, void *conf) { uint8_t *p; dist_type_t *dp; struct string *value, *dist; p = conf; dp = (dist_type_t *)(p + cmd->offset); if (*dp != CONF_UNSET_DIST) { return "is a duplicate"; } value = array_top(&cf->arg); for (dist = dist_strings; dist->len != 0; dist++) { if (string_compare(value, dist) != 0) { continue; } *dp = dist - dist_strings; return CONF_OK; } return "is not a valid distribution"; }
char * conf_set_bool(struct conf *cf, struct command *cmd, void *conf) { uint8_t *p; int *bp; struct string *value, true_str, false_str; p = conf; bp = (int *)(p + cmd->offset); if (*bp != CONF_UNSET_NUM) { return "is a duplicate"; } value = array_top(&cf->arg); string_set_text(&true_str, "true"); string_set_text(&false_str, "false"); if (string_compare(value, &true_str) == 0) { *bp = 1; } else if (string_compare(value, &false_str) == 0) { *bp = 0; } else { return "is not \"true\" or \"false\""; } return CONF_OK; }
char * conf_set_hash(struct conf *cf, struct command *cmd, void *conf) { uint8_t *p; hash_type_t *hp; struct string *value, *hash; p = conf; hp = (hash_type_t *)(p + cmd->offset); if (*hp != CONF_UNSET_HASH) { return "is a duplicate"; } value = array_top(&cf->arg); for (hash = hash_strings; hash->len != 0; hash++) { if (string_compare(value, hash) != 0) { continue; } *hp = hash - hash_strings; return CONF_OK; } return "is not a valid hash"; }
char * conf_set_proto(struct conf *cf, struct command *cmd, void *conf) { proto_type_t *p; struct string *value, *proto; p = (proto_type_t *)((uint8_t *)conf + cmd->offset); if (*p != CONF_UNSET_PROTO) { return "is a duplicate"; } value = array_top(&cf->arg); if (value == NULL) { return CONF_ERROR; } for (proto = proto_strings; proto->len != 0; proto++) { if (string_compare(value, proto) != 0) { continue; } *p = (proto_type_t) (proto - proto_strings); return CONF_OK; } return "is not a valid protocol name"; }
char * conf_set_num(struct conf *cf, struct command *cmd, void *conf) { uint8_t *p; int num, *np; struct string *value; p = conf; np = (int *)(p + cmd->offset); if (*np != CONF_UNSET_NUM) { return "is a duplicate"; } value = array_top(&cf->arg); num = nc_atoi(value->data, value->len); if (num < 0) { return "is not a number"; } *np = num; return CONF_OK; }
char * conf_set_hashtag(struct conf *cf, struct command *cmd, void *conf) { rstatus_t status; uint8_t *p; struct string *field, *value; p = conf; field = (struct string *)(p + cmd->offset); if (field->data != CONF_UNSET_PTR) { return "is a duplicate"; } value = array_top(&cf->arg); if (value->len != 2) { return "is not a valid hash tag string with two characters"; } status = string_duplicate(field, value); if (status != NC_OK) { return CONF_ERROR; } return CONF_OK; }
char * conf_add_group(struct conf *cf, struct command *cmd, void *conf) { rstatus_t status; uint8_t *p; struct array *a; struct string *field, *value; p = conf; a = (struct array *)(p + cmd->offset); field = array_push(a); if (field == NULL) { return CONF_ERROR; } string_init(field); value = array_top(&cf->arg); status = string_duplicate(field, value); if (status != NC_OK) { return CONF_ERROR; } return CONF_OK; }
/********************************************************************** * array_push * * Add a new element onto the top of the array. If there is not room * more room is made by "realloc"ing the array. This means that the * new array location may change. All previous references to its old * location may no longer be valid. **********************************************************************/ ARRAY array_push(ARRAY array, void *value) { if (array_count (array) == array_limit (array)) { array = (ARRAY) memrealloc (array, (array_limit (array) * 2 - 2) * sizeof (char *) + sizeof (struct array_record), (array_limit (array) - 2) * sizeof (char *) + sizeof (struct array_record)); if (!array) { cprintf ("error: Out of memory in array_push\n"); exit (1); //?err_exit (); } array_limit (array) *= 2; } array_count (array)++; array_top (array) = value; return (array); }
static rstatus_t conf_handler(struct conf *cf, void *data) { struct command *cmd; struct string *key, *value; uint32_t narg; if (array_n(&cf->arg) == 1) { value = array_top(&cf->arg); log_debug(LOG_VVERB, "conf handler on '%.*s'", value->len, value->data); return conf_pool_init(data, value); } narg = array_n(&cf->arg); value = array_get(&cf->arg, narg - 1); key = array_get(&cf->arg, narg - 2); log_debug(LOG_VVERB, "conf handler on %.*s: %.*s", key->len, key->data, value->len, value->data); for (cmd = conf_commands; cmd->name.len != 0; cmd++) { char *rv; if (string_compare(key, &cmd->name) != 0) { continue; } rv = cmd->set(cf, cmd, data); if (rv != CONF_OK) { log_error("conf: directive \"%.*s\" %s", key->len, key->data, rv); return NC_ERROR; } return NC_OK; } log_error("conf: directive \"%.*s\" is unknown", key->len, key->data); return NC_ERROR; }
char * conf_set_string(struct conf *cf, struct command *cmd, void *conf) { rstatus_t status; uint8_t *p; struct string *field, *value; p = conf; field = (struct string *)(p + cmd->offset); if (field->data != CONF_UNSET_PTR) { return "is a duplicate"; } value = array_top(&cf->arg); status = string_duplicate(field, value); if (status != NC_OK) { return CONF_ERROR; } return CONF_OK; }
char * conf_add_server(struct conf *cf, struct command *cmd, void *conf) { rstatus_t status; struct array *a; struct string *value; struct conf_server *field; uint8_t *p, *q, *start; uint8_t *name, *port, *weight; uint32_t k, namelen, portlen, weightlen; p = conf; a = (struct array *)(p + cmd->offset); field = array_push(a); if (field == NULL) { return CONF_ERROR; } conf_server_init(field); value = array_top(&cf->arg); status = string_duplicate(&field->pname, value); if (status != NC_OK) { array_pop(a); return CONF_ERROR; } /* parse "hostname:port:weight" from the end */ p = value->data + value->len - 1; start = value->data; name = NULL; namelen = 0; weight = NULL; weightlen = 0; port = NULL; portlen = 0; for (k = 0; k < 2; k++) { q = nc_strrchr(p, start, ':'); if (q == NULL) { break; } switch (k) { case 0: weight = q + 1; weightlen = (uint32_t)(p - weight + 1); break; case 1: port = q + 1; portlen = (uint32_t)(p - port + 1); break; default: NOT_REACHED(); } p = q - 1; } if (k != 2) { return "has an invalid \"hostname:port:weight\" format string"; } name = start; namelen = (uint32_t)(p - start + 1); field->weight = nc_atoi(weight, weightlen); if (field->weight < 0) { return "has an invalid weight in \"hostname:port:weight\" format string"; } field->port = nc_atoi(port, portlen); if (field->port < 0 || !nc_valid_port(field->port)) { return "has an invalid port in \"hostname:port:weight\" format string"; } status = string_copy(&field->name, name, namelen); if (status != NC_OK) { return CONF_ERROR; } status = nc_resolve(&field->name, field->port, &field->info); if (status != NC_OK) { return CONF_ERROR; } field->valid = 1; return CONF_OK; }
char * conf_set_listen(struct conf *cf, struct command *cmd, void *conf) { rstatus_t status; struct string *value; struct conf_listen *field; uint8_t *p, *name; uint32_t namelen; p = conf; field = (struct conf_listen *)(p + cmd->offset); if (field->valid == 1) { return "is a duplicate"; } value = array_top(&cf->arg); status = string_duplicate(&field->pname, value); if (status != NC_OK) { return CONF_ERROR; } if (value->data[0] == '/') { name = value->data; namelen = value->len; } else { uint8_t *q, *start, *port; uint32_t portlen; /* parse "hostname:port" from the end */ p = value->data + value->len - 1; start = value->data; q = nc_strrchr(p, start, ':'); if (q == NULL) { return "has an invalid \"hostname:port\" format string"; } port = q + 1; portlen = (uint32_t)(p - port + 1); p = q - 1; name = start; namelen = (uint32_t)(p - start + 1); field->port = nc_atoi(port, portlen); if (field->port < 0 || !nc_valid_port(field->port)) { return "has an invalid port in \"hostname:port\" format string"; } } status = string_copy(&field->name, name, namelen); if (status != NC_OK) { return CONF_ERROR; } status = nc_resolve(&field->name, field->port, &field->info); if (status != NC_OK) { return CONF_ERROR; } field->valid = 1; return CONF_OK; }
char * conf_add_server(struct conf *cf, struct command *cmd, void *conf) { rstatus_t status; struct array *a; struct string *value; struct conf_server *field; uint8_t *p, *q, *start; uint8_t *pname, *addr, *port, *weight, *name; uint32_t k, delimlen, pnamelen, addrlen, portlen, weightlen, namelen; char delim[] = " ::"; p = conf; a = (struct array *)(p + cmd->offset); field = array_push(a); if (field == NULL) { return CONF_ERROR; } conf_server_init(field); value = array_top(&cf->arg); /* parse "hostname:port:weight [name]" or "/path/unix_socket:weight [name]" from the end */ p = value->data + value->len - 1; start = value->data; addr = NULL; addrlen = 0; weight = NULL; weightlen = 0; port = NULL; portlen = 0; name = NULL; namelen = 0; delimlen = value->data[0] == '/' ? 2 : 3; for (k = 0; k < sizeof(delim); k++) { q = nc_strrchr(p, start, delim[k]); if (q == NULL) { if (k == 0) { /* * name in "hostname:port:weight [name]" format string is * optional */ continue; } break; } switch (k) { case 0: name = q + 1; namelen = (uint32_t)(p - name + 1); break; case 1: weight = q + 1; weightlen = (uint32_t)(p - weight + 1); break; case 2: port = q + 1; portlen = (uint32_t)(p - port + 1); break; default: NOT_REACHED(); } p = q - 1; } if (k != delimlen) { return "has an invalid \"hostname:port:weight [name]\"or \"/path/unix_socket:weight [name]\" format string"; } pname = value->data; pnamelen = namelen > 0 ? value->len - (namelen + 1) : value->len; status = string_copy(&field->pname, pname, pnamelen); if (status != NC_OK) { array_pop(a); return CONF_ERROR; } addr = start; addrlen = (uint32_t)(p - start + 1); field->weight = nc_atoi(weight, weightlen); if (field->weight < 0) { return "has an invalid weight in \"hostname:port:weight [name]\" format string"; } else if (field->weight == 0) { return "has a zero weight in \"hostname:port:weight [name]\" format string"; } if (value->data[0] != '/') { field->port = nc_atoi(port, portlen); if (field->port < 0 || !nc_valid_port(field->port)) { return "has an invalid port in \"hostname:port:weight [name]\" format string"; } } if (name == NULL) { /* * To maintain backward compatibility with libmemcached, we don't * include the port as the part of the input string to the consistent * hashing algorithm, when it is equal to 11211. */ if (field->port == CONF_DEFAULT_KETAMA_PORT) { name = addr; namelen = addrlen; } else { name = addr; namelen = addrlen + 1 + portlen; } } status = string_copy(&field->name, name, namelen); if (status != NC_OK) { return CONF_ERROR; } status = string_copy(&field->addrstr, (uint8_t *)addr, addrlen); if (status != NC_OK) { return CONF_ERROR; } /* * The address resolution of the backend server hostname is lazy. * The resolution occurs when a new connection to the server is * created, which could either be the first time or every time * the server gets re-added to the pool after an auto ejection */ field->valid = 1; return CONF_OK; }
char * conf_set_listen(struct conf *cf, struct command *cmd, void *conf) { rstatus_t status; struct string *value; struct conf_listen *field; uint8_t *p, *name; uint32_t namelen; p = conf; field = (struct conf_listen *)(p + cmd->offset); if (field->valid == 1) { return "is a duplicate"; } value = array_top(&cf->arg); status = string_duplicate(&field->pname, value); if (status != NC_OK) { return CONF_ERROR; } if (value->data[0] == '/') { uint8_t *q, *start, *perm; uint32_t permlen; /* parse "socket_path permissions" from the end */ p = value->data + value->len -1; start = value->data; q = nc_strrchr(p, start, ' '); if (q == NULL) { /* no permissions field, so use defaults */ name = value->data; namelen = value->len; } else { perm = q + 1; permlen = (uint32_t)(p - perm + 1); p = q - 1; name = start; namelen = (uint32_t)(p - start + 1); errno = 0; field->perm = (mode_t)strtol((char *)perm, NULL, 8); if (errno || field->perm > 0777) { return "has an invalid file permission in \"socket_path permission\" format string"; } } } else { uint8_t *q, *start, *port; uint32_t portlen; /* parse "hostname:port" from the end */ p = value->data + value->len - 1; start = value->data; q = nc_strrchr(p, start, ':'); if (q == NULL) { return "has an invalid \"hostname:port\" format string"; } port = q + 1; portlen = (uint32_t)(p - port + 1); p = q - 1; name = start; namelen = (uint32_t)(p - start + 1); field->port = nc_atoi(port, portlen); if (field->port < 0 || !nc_valid_port(field->port)) { return "has an invalid port in \"hostname:port\" format string"; } } status = string_copy(&field->name, name, namelen); if (status != NC_OK) { return CONF_ERROR; } status = nc_resolve(&field->name, field->port, &field->info); if (status != NC_OK) { return CONF_ERROR; } field->valid = 1; return CONF_OK; }
char * conf_add_server(struct conf *cf, struct command *cmd, void *conf) { rstatus_t status; struct array *a; struct string *value; struct conf_server *field; uint8_t *p, *q, *start; uint8_t *pname, *addr, *port, *weight, *name; uint32_t k, pnamelen, addrlen, portlen, weightlen, namelen; struct string address; char delim[] = " ::"; string_init(&address); p = conf; a = (struct array *)(p + cmd->offset); field = array_push(a); if (field == NULL) { return CONF_ERROR; } conf_server_init(field); value = array_top(&cf->arg); /* parse "hostname:port:weight [name]" from the end */ p = value->data + value->len - 1; start = value->data; addr = NULL; addrlen = 0; weight = NULL; weightlen = 0; port = NULL; portlen = 0; name = NULL; namelen = 0; for (k = 0; k < sizeof(delim); k++) { q = nc_strrchr(p, start, delim[k]); if (q == NULL) { if (k == 0) { /* * name in "hostname:port:weight [name]" format string is * optional */ continue; } break; } switch (k) { case 0: name = q + 1; namelen = (uint32_t)(p - name + 1); break; case 1: weight = q + 1; weightlen = (uint32_t)(p - weight + 1); break; case 2: port = q + 1; portlen = (uint32_t)(p - port + 1); break; default: NOT_REACHED(); } p = q - 1; } if (k != 3) { return "has an invalid \"hostname:port:weight [name]\" format string"; } pname = value->data; pnamelen = namelen > 0 ? value->len - (namelen + 1) : value->len; status = string_copy(&field->pname, pname, pnamelen); if (status != NC_OK) { array_pop(a); return CONF_ERROR; } addr = start; addrlen = (uint32_t)(p - start + 1); field->weight = nc_atoi(weight, weightlen); if (field->weight < 0) { return "has an invalid weight in \"hostname:port:weight [name]\" format string"; } field->port = nc_atoi(port, portlen); if (field->port < 0 || !nc_valid_port(field->port)) { return "has an invalid port in \"hostname:port:weight [name]\" format string"; } if (name == NULL) { /* * To maintain backward compatibility with libmemcached, we don't * include the port as the part of the input string to the consistent * hashing algorithm, when it is equal to 11211. */ if (field->port == CONF_DEFAULT_KETAMA_PORT) { name = addr; namelen = addrlen; } else { name = addr; namelen = addrlen + 1 + portlen; } } status = string_copy(&field->name, name, namelen); if (status != NC_OK) { return CONF_ERROR; } status = string_copy(&address, addr, addrlen); if (status != NC_OK) { return CONF_ERROR; } status = nc_resolve(&address, field->port, &field->info); if (status != NC_OK) { string_deinit(&address); return CONF_ERROR; } string_deinit(&address); field->valid = 1; return CONF_OK; }