static int nc_resolve_inet(struct string *name, int port, struct sockinfo *si) { int status; struct addrinfo *ai, *cai; /* head and current addrinfo */ struct addrinfo hints; char *node, service[NC_UINTMAX_MAXLEN]; bool found; ASSERT(nc_valid_port(port)); memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICSERV; //ai_numericserv hints.ai_family = AF_UNSPEC; /* AF_INET or AF_INET6 */ hints.ai_socktype = SOCK_STREAM; //sock_stream hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; if (name != NULL) { node = (char *)name->data; } else { /* * If AI_PASSIVE flag is specified in hints.ai_flags, and node is * NULL, then the returned socket addresses will be suitable for * bind(2)ing a socket that will accept(2) connections. The returned * socket address will contain the wildcard IP address. */ node = NULL; hints.ai_flags |= AI_PASSIVE; } nc_snprintf(service, NC_UINTMAX_MAXLEN, "%d", port); status = getaddrinfo(node, service, &hints, &ai); if (status < 0) { log_error("address resolution of node '%s' service '%s' failed: %s", node, service, gai_strerror(status)); return -1; } /* * getaddrinfo() can return a linked list of more than one addrinfo, * since we requested for both AF_INET and AF_INET6 addresses and the * host itself can be multi-homed. Since we don't care whether we are * using ipv4 or ipv6, we just use the first address from this collection * in the order in which it was returned. * * The sorting function used within getaddrinfo() is defined in RFC 3484; * the order can be tweaked for a particular system by editing * /etc/gai.conf */ for (cai = ai, found = false; cai != NULL; cai = cai->ai_next) { si->family = cai->ai_family; si->addrlen = cai->ai_addrlen; nc_memcpy(&si->addr, cai->ai_addr, si->addrlen); found = true; break; } freeaddrinfo(ai); return !found ? -1 : 0; }
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; }
static rstatus_t nc_get_options(int argc, char **argv, struct instance *nci) { int c, value; opterr = 0; for (;;) { c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { /* no more options */ break; } switch (c) { case 'h': show_version = 1; show_help = 1; break; case 'V': show_version = 1; break; case 't': test_conf = 1; break; case 'd': daemonize = 1; break; case 'D': describe_stats = 1; show_version = 1; break; case 'v': value = nc_atoi(optarg, strlen(optarg)); if (value < 0) { log_stderr("nutcracker: option -v requires a number"); return NC_ERROR; } nci->log_level = value; break; case 'o': nci->log_filename = optarg; break; case 'c': nci->conf_filename = optarg; break; case 's': value = nc_atoi(optarg, strlen(optarg)); if (value < 0) { log_stderr("nutcracker: option -s requires a number"); return NC_ERROR; } if (!nc_valid_port(value)) { log_stderr("nutcracker: option -s value %d is not a valid " "port", value); return NC_ERROR; } nci->stats_port = (uint16_t)value; break; case 'i': value = nc_atoi(optarg, strlen(optarg)); if (value < 0) { log_stderr("nutcracker: option -i requires a number"); return NC_ERROR; } nci->stats_interval = value; break; case 'a': nci->stats_addr = optarg; break; case 'p': nci->pid_filename = optarg; break; case 'm': value = nc_atoi(optarg, strlen(optarg)); if (value <= 0) { log_stderr("nutcracker: option -m requires a non-zero number"); return NC_ERROR; } if (value < NC_MBUF_MIN_SIZE || value > NC_MBUF_MAX_SIZE) { log_stderr("nutcracker: mbuf chunk size must be between %zu and" " %zu bytes", NC_MBUF_MIN_SIZE, NC_MBUF_MAX_SIZE); return NC_ERROR; } nci->mbuf_chunk_size = (size_t)value; break; case 'M': value = nc_atoi(optarg, strlen(optarg)); if (value < 0) { log_stderr("nutcracker: option -M requires a non-negative number"); return NC_ERROR; } nci->mbuf_pool_size = (unsigned)value; break; case '?': switch (optopt) { case 'o': case 'c': case 'p': log_stderr("nutcracker: option -%c requires a file name", optopt); break; case 'm': case 'M': case 'v': case 's': case 'i': log_stderr("nutcracker: option -%c requires a number", optopt); break; case 'a': log_stderr("nutcracker: option -%c requires a string", optopt); break; default: log_stderr("nutcracker: invalid option -- '%c'", optopt); break; } return NC_ERROR; default: log_stderr("nutcracker: invalid option -- '%c'", optopt); return NC_ERROR; } } return NC_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; }
static rstatus_t nc_get_options(int argc, char **argv, struct env_master *env) { int c, value; int len; int cores; opterr = 0; //ASSERT(0); for (;;) { c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { /* no more options */ break; } switch (c) { case 'h': show_version = 1; show_help = 1; break; case 'V': show_version = 1; break; case 't': test_conf = 1; break; case 'd': daemonize = 1; break; case 'D': describe_stats = 1; show_version = 1; break; case 'v': value = nc_atoi(optarg, strlen(optarg)); if (value < 0) { log_stderr("nutcracker: option -v requires a number"); return NC_ERROR; } env->log_level = value; break; case 'o': env->log_filename = optarg; len = nc_strlen(optarg) + 1; log_filename_global = malloc(len); if (log_filename_global == NULL) { return NC_ERROR; } nc_cpystrn((u_char *) log_filename_global, (u_char *) optarg, len); break; case 'c': env->conf_filename = optarg; len = nc_strlen(optarg) + 1; conf_filename_global = malloc(len); if (conf_filename_global == NULL) { return NC_ERROR; } nc_cpystrn((u_char *) conf_filename_global, (u_char *) optarg, len); break; case 's': value = nc_atoi(optarg, strlen(optarg)); if (value < 0) { log_stderr("nutcracker: option -s requires a number"); return NC_ERROR; } if (!nc_valid_port(value)) { log_stderr("nutcracker: option -s value %d is not a valid " "port", value); return NC_ERROR; } env->stats_port = (uint16_t)value; break; case 'i': value = nc_atoi(optarg, strlen(optarg)); if (value < 0) { log_stderr("nutcracker: option -i requires a number"); return NC_ERROR; } env->stats_interval = value; break; case 'a': env->stats_addr = optarg; break; case 'p': env->pid_filename = optarg; break; case 'm': value = nc_atoi(optarg, strlen(optarg)); if (value <= 0) { log_stderr("nutcracker: option -m requires a non-zero number"); return NC_ERROR; } if (value < NC_MBUF_MIN_SIZE || value > NC_MBUF_MAX_SIZE) { log_stderr("nutcracker: mbuf chunk size must be between %zu and" \ " %zu bytes", NC_MBUF_MIN_SIZE, NC_MBUF_MAX_SIZE); return NC_ERROR; } env->mbuf_chunk_size = (size_t)value; break; case 'n': value = nc_atoi(optarg, strlen(optarg)); if (value <= 0) { log_stderr("nutcracker: option -n requires a non-zero number"); return NC_ERROR; } cores = sysconf(_SC_NPROCESSORS_ONLN); if (value > cores || value < 1) { log_stderr("bilitw: bilitw worker process number should be between %d and" \ " %d", 1, cores); return NC_ERROR; } env->worker_processes= value; break; case 'M': value = nc_atoi(optarg, strlen(optarg)); if (value <= 0) { log_stderr("nutcracker: option -M requires a non-zero number"); return NC_ERROR; } cores = sysconf(_SC_NPROCESSORS_ONLN); cores = (1<<cores) -1; if (value > cores || value < 1) { log_stderr("bilitw: bilitw worker process cpu mask should be set between %d and" \ " %d", 1, cores); return NC_ERROR; } env->cpu_mask = value; break; case '?': switch (optopt) { case 'o': case 'c': case 'p': log_stderr("nutcracker: option -%c requires a file name", optopt); break; case 'm': case 'v': case 's': case 'i': case 'n': case 'M': log_stderr("nutcracker: option -%c requires a number", optopt); break; case 'a': log_stderr("nutcracker: option -%c requires a string", optopt); break; default: log_stderr("nutcracker: invalid option -- '%c'", optopt); break; } return NC_ERROR; default: log_stderr("nutcracker: invalid option -- '%c'", optopt); return NC_ERROR; } } return NC_OK; }