Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1138 lines (944 sloc) 34.669 kb
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sysexits.h>
#include <pthread.h>
#include <assert.h>
#include "memcached.h"
#include "cproxy.h"
#include "work.h"
#include "agent.h"
// Integration with libconflate.
//
static void update_ptd_config(void *data0, void *data1);
static bool update_str_config(char **curr, char *next, char *descrip);
static bool update_behaviors_config(proxy_behavior **curr,
int *curr_num,
proxy_behavior *next,
int next_num,
char *descrip);
char *parse_kvs_servers(char *prefix,
char *pool_name,
kvpair_t *kvs,
char **servers,
proxy_behavior_pool *behavior_pool);
char **parse_kvs_behavior(kvpair_t *kvs,
char *prefix,
char *name,
proxy_behavior *behavior);
static void agent_logger(void *userdata,
enum conflate_log_level lvl,
const char *msg, ...)
{
char *n = NULL;
bool v = false;
switch(lvl) {
case FATAL: n = "FATAL"; v = settings.verbose > 0; break;
case ERROR: n = "ERROR"; v = settings.verbose > 0; break;
case WARN: n = "WARN"; v = settings.verbose > 1; break;
case INFO: n = "INFO"; v = settings.verbose > 1; break;
case DEBUG: n = "DEBUG"; v = settings.verbose > 2; break;
}
if (!v) {
return;
}
char fmt[strlen(msg) + 16];
snprintf(fmt, sizeof(fmt), "%s: %s\n", n, msg);
va_list ap;
va_start(ap, msg);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
static void init_extensions(void)
{
conflate_register_mgmt_cb("client_stats", "Retrieve stats from moxi",
on_conflate_get_stats);
conflate_register_mgmt_cb("reset_stats", "Reset moxi stats",
on_conflate_reset_stats);
conflate_register_mgmt_cb("ping_test", "Perform a ping test",
on_conflate_ping_test);
}
/** The cfg_str looks like...
*
* apikey=jidname@jhostname%jpassword,config=config,host=host
* or...
* jidname@jhostname%jpassword,config=config,host=host
*
* Only the apikey is needed, so it can also look like...
*
* jidname@jhostname%jpassword
*/
int cproxy_init_agent(char *cfg_str,
proxy_behavior behavior,
int nthreads) {
init_extensions();
if (cfg_str == NULL) {
fprintf(stderr, "missing cfg\n");
exit(EXIT_FAILURE);
}
int cfg_len = strlen(cfg_str);
if (cfg_len <= 0) {
fprintf(stderr, "empty cfg\n");
exit(EXIT_FAILURE);
}
char *buff;
if (strncmp(cfg_str, "apikey=", 7) == 0 ||
strncmp(cfg_str, "auth=", 5) == 0 ||
strncmp(cfg_str, "url=", 4) == 0) {
buff = trimstrdup(cfg_str);
} else {
buff = calloc(cfg_len + 50, sizeof(char));
if (buff != NULL) {
snprintf(buff, cfg_len + 50, "apikey=%s", cfg_str);
}
buff = trimstr(buff);
}
char *next = buff;
int rv = 0;
while (next != NULL) {
char *jid = NULL;
char *jpw = NULL;
char *jpwmem = NULL;
char *dbpath = NULL;
char *host = NULL;
char *cur = trimstr(strsep(&next, ";"));
while (cur != NULL) {
char *key_val = trimstr(strsep(&cur, ",\r\n"));
if (key_val != NULL) {
char *key = trimstr(strsep(&key_val, "="));
char *val = trimstr(key_val);
bool handled = true;
if (key != NULL &&
val != NULL) {
if (wordeq(key, "apikey") ||
wordeq(key, "auth")) {
jid = strsep(&val, "%");
jpw = val;
} else if (wordeq(key, "config") ||
wordeq(key, "dbpath")) {
dbpath = val;
} else if (wordeq(key, "host") ||
wordeq(key, "url")) {
host = val;
} else {
handled = false;
}
} else {
handled = false;
}
if (handled == false &&
key != NULL &&
key[0] != '#' &&
key[0] != '\0') {
if (settings.verbose > 0) {
fprintf(stderr,
"unknown configuration key: %s\n", key);
}
}
}
}
if (jid == NULL) {
jid = "";
}
if (jpw == NULL) {
// Handle if jid/jpw is in user:password@fqdn format
// instead of user@fqdn%password format.
//
char *colon = strchr(jid, ':');
char *asign = strchr(jid, '@');
if (colon != NULL &&
asign != NULL &&
asign > colon) {
*asign = '\0';
jpw = jpwmem = strdup(colon + 1);
*asign = '@';
do {
*colon = *asign;
colon++;
asign++;
} while (*asign != '\0');
*colon = '\0';
}
}
if (jpw == NULL) {
jpw = "";
}
int dbpath_alloc = 0;
if (dbpath == NULL) {
dbpath_alloc = strlen(jid) + strlen(CONFLATE_DB_PATH) + 100;
dbpath = calloc(dbpath_alloc, 1);
if (dbpath != NULL) {
snprintf(dbpath, dbpath_alloc,
CONFLATE_DB_PATH "/conflate-%s.cfg",
(jid != NULL && strlen(jid) > 0 ? jid : "default"));
} else {
fprintf(stderr, "conflate dbpath buf alloc\n");
exit(EXIT_FAILURE);
}
}
if (settings.verbose > 1) {
fprintf(stderr, "cproxy_init jid: %s host: %s dbpath: %s\n", jid, host, dbpath);
}
if (cproxy_init_agent_start(jid, jpw, dbpath, host,
behavior,
nthreads) != NULL) {
rv++;
}
if (dbpath_alloc > 0 &&
dbpath != NULL) {
free(dbpath);
}
if (jpwmem) {
free(jpwmem);
}
}
free(buff);
return rv;
}
proxy_main *cproxy_init_agent_start(char *jid,
char *jpw,
char *dbpath,
char *host,
proxy_behavior behavior,
int nthreads) {
assert(dbpath);
if (settings.verbose > 2) {
fprintf(stderr, "cproxy_init_agent_start\n");;
}
proxy_main *m = calloc(1, sizeof(proxy_main));
if (m != NULL) {
m->proxy_head = NULL;
m->nthreads = nthreads;
m->behavior = behavior;
m->stat_configs = 0;
m->stat_config_fails = 0;
m->stat_proxy_starts = 0;
m->stat_proxy_start_fails = 0;
m->stat_proxy_existings = 0;
m->stat_proxy_shutdowns = 0;
conflate_config_t config;
memset(&config, 0, sizeof(config));
init_conflate(&config);
// Different jid's possible for production, staging, etc.
config.jid = jid; // "customer@stevenmb.local" or
// "Administrator"
config.pass = jpw; // "password"
config.host = host; // "localhost" or
// "http://x.com:8080"
// "http://x.com:8080/pools/default/buckets/default"
config.software = PACKAGE;
config.version = VERSION;
config.save_path = dbpath;
config.userdata = m;
config.new_config = on_conflate_new_config;
config.log = agent_logger;
if (start_conflate(config)) {
if (settings.verbose > 2) {
fprintf(stderr, "cproxy_init done\n");
}
return m;
}
free(m);
}
if (settings.verbose > 1) {
fprintf(stderr, "cproxy could not start conflate\n");
}
return NULL;
}
static
void cproxy_on_new_config(void *data0, void *data1);
void on_conflate_new_config(void *userdata, kvpair_t *config) {
assert(config != NULL);
proxy_main *m = userdata;
assert(m != NULL);
LIBEVENT_THREAD *mthread = thread_by_index(0);
assert(mthread != NULL);
if (settings.verbose > 2) {
fprintf(stderr, "agent_config ocnc on_conflate_new_config\n");
}
work_collect completion;
work_collect_init(&completion, 1, m);
kvpair_t *copy = dup_kvpair(config);
if (copy != NULL) {
if (!work_send(mthread->work_queue, cproxy_on_new_config, &completion, copy) &&
settings.verbose > 1) {
fprintf(stderr, "work_send failed\n");
}
} else {
if (settings.verbose > 1) {
fprintf(stderr, "agent_config ocnc failed dup_kvpair\n");
}
}
work_collect_wait(&completion);
}
static
void cproxy_on_new_config(void *data0, void *data1) {
work_collect *completion = data0;
proxy_main *m = completion->data;
assert(m);
kvpair_t *kvs = data1;
assert(kvs);
assert(is_listen_thread());
m->stat_configs++;
uint32_t max_config_ver = 0;
for (proxy *p = m->proxy_head; p != NULL; p = p->next) {
pthread_mutex_lock(&p->proxy_lock);
if (max_config_ver < p->config_ver) {
max_config_ver = p->config_ver;
}
pthread_mutex_unlock(&p->proxy_lock);
}
uint32_t new_config_ver = max_config_ver + 1;
if (settings.verbose > 2) {
fprintf(stderr, "conc new_config_ver %u\n", new_config_ver);
}
#ifdef MOXI_USE_VBUCKET
char **contents = get_key_values(kvs, "contents");
if (contents != NULL &&
contents[0] != NULL) {
char *config = trimstrdup(contents[0]);
if (settings.verbose > 2) {
fprintf(stderr, "conc contents config %s\n", config);
}
// The config should be JSON that should look like...
//
// {"name":"default",
// "nodes":[{"hostname":"10.17.1.46","status":"healthy",
// "version":"0.3.0_114_g31859fe","os":"i386-apple-darwin9.8.0",
// "ports":{"proxy":11213,"direct":11212}}],
// "buckets":{"uri":"/pools/default/buckets"},
// "controllers":{"ejectNode":{"uri":"/controller/ejectNode"},
// "testWorkload":{"uri":"/pools/default/controller/testWorkload"}},
// "stats":{"uri":"/pools/default/stats"},
// "vbucketServerMap":{...more json here...}}
//
VBUCKET_CONFIG_HANDLE vch = vbucket_config_parse_string(config);
if (vch) {
if (settings.verbose > 2) {
fprintf(stderr, "conc vbucket_config_parse_string: %d\n", (vch != NULL));
}
proxy_behavior proxyb = m->behavior;
int pool_port = proxyb.port_listen;
int nodes_num = vbucket_config_get_num_servers(vch);
if (settings.verbose > 2) {
fprintf(stderr, "conc pool_port: %d nodes_num: %d\n",
pool_port, nodes_num);
}
if (pool_port > 0 &&
nodes_num > 0) {
proxy_behavior_pool behavior_pool = {
.base = proxyb,
.num = nodes_num,
.arr = calloc(nodes_num, sizeof(proxy_behavior))
};
if (behavior_pool.arr != NULL) {
int j = 0;
for (; j < nodes_num; j++) {
// Inherit default behavior.
//
behavior_pool.arr[j] = behavior_pool.base;
const char *hostport = vbucket_config_get_server(vch, j);
if (hostport != NULL &&
strlen(hostport) > 0 &&
strlen(hostport) < sizeof(behavior_pool.arr[j].host) - 1) {
strncpy(behavior_pool.arr[j].host,
hostport,
sizeof(behavior_pool.arr[j].host) - 1);
char *colon = strchr(behavior_pool.arr[j].host, ':');
if (colon != NULL) {
*colon = '\0';
behavior_pool.arr[j].port = atoi(colon + 1);
if (behavior_pool.arr[j].port <= 0) {
break;
}
} else {
break;
}
} else {
break;
}
}
if (j >= nodes_num) {
cproxy_on_new_pool(m, "default", pool_port,
config, new_config_ver,
&behavior_pool);
} else {
if (settings.verbose > 1) {
fprintf(stderr,
"ERROR: error receiving host:port for server config %d in %s\n",
j, config);
}
}
free(behavior_pool.arr);
}
}
vbucket_config_destroy(vch);
}
free(config);
return; // Don't fall through since we received json Content.
}
#endif
// The kvs key-multivalues look roughly like...
//
// pool-customer1-a
// svrname3
// pool-customer1-b
// svrname1
// svrname2
// svr-svrname1
// host=mc1.foo.net
// port=11211
// weight=1
// bucket=buck1
// usr=test1
// pwd=password
// svr-svrnameX
// host=mc2.foo.net
// port=11211
// behavior-customer1-a
// wait_queue_timeout=1000
// downstream_max=10
// behavior-customer1-b
// wait_queue_timeout=1000
// downstream_max=10
// pool_drain-customer1-b
// svrname1
// svrname3
// pools
// customer1-a
// customer1-b
// bindings
// 11221
// 11331
//
char **pools = get_key_values(kvs, "pools");
char **bindings = get_key_values(kvs, "bindings");
if (pools == NULL) {
goto fail;
}
int npools = 0;
int nbindings = 0;
while (pools && pools[npools])
npools++;
while (bindings && bindings[nbindings])
nbindings++;
if (nbindings > 0 &&
nbindings != npools) {
if (settings.verbose > 1) {
fprintf(stderr, "npools does not match nbindings\n");
}
goto fail;
}
char **behavior_kvs = get_key_values(kvs, "behavior");
if (behavior_kvs != NULL) {
// Update the default behavior.
//
proxy_behavior m_behavior = m->behavior;
for (int k = 0; behavior_kvs[k]; k++) {
char *bstr = trimstrdup(behavior_kvs[k]);
if (bstr != NULL) {
cproxy_parse_behavior_key_val_str(bstr, &m_behavior);
free(bstr);
}
}
m->behavior = m_behavior;
}
for (int i = 0; i < npools; i++) {
char *pool_name = skipspace(pools[i]);
if (pool_name != NULL &&
pool_name[0] != '\0') {
char buf[200];
snprintf(buf, sizeof(buf), "pool-%s", pool_name);
char **servers = get_key_values(kvs, trimstr(buf));
if (servers != NULL) {
// Parse proxy-level behavior.
//
proxy_behavior proxyb = m->behavior;
if (parse_kvs_behavior(kvs, "behavior", pool_name, &proxyb)) {
if (settings.verbose > 1) {
cproxy_dump_behavior(&proxyb,
"conc proxy_behavior", 1);
}
}
// The legacy way to get a port is through the bindings,
// but they're also available as an inheritable
// proxy_behavior field of port_listen.
//
int pool_port = proxyb.port_listen;
if (i < nbindings &&
bindings != NULL &&
bindings[i]) {
pool_port = atoi(skipspace(bindings[i]));
}
if (pool_port > 0) {
// Number of servers in this pool.
//
int s = 0;
while (servers[s])
s++;
if (s > 0) {
// Parse server-level behaviors, so we'll have an
// array of behaviors, one entry for each server.
//
proxy_behavior_pool behavior_pool = {
.base = proxyb,
.num = s,
.arr = calloc(s, sizeof(proxy_behavior))
};
if (behavior_pool.arr != NULL) {
char *config_str =
parse_kvs_servers("svr", pool_name, kvs,
servers, &behavior_pool);
if (config_str != NULL &&
config_str[0] != '\0') {
if (settings.verbose > 2) {
fprintf(stderr, "conc config: %s\n",
config_str);
}
cproxy_on_new_pool(m, pool_name, pool_port,
config_str, new_config_ver,
&behavior_pool);
free(config_str);
}
free(behavior_pool.arr);
} else {
if (settings.verbose > 1) {
fprintf(stderr, "ERROR: oom on re-config malloc\n");;
}
goto fail;
}
} else {
// Note: ignore when no servers for an existing pool.
// Because the config_ver won't be updated, we'll
// fall into the empty_pool code path below.
}
} else {
if (settings.verbose > 1) {
fprintf(stderr, "ERROR: conc missing pool port\n");
}
goto fail;
}
} else {
// Note: ignore when no servers for an existing pool.
// Because the config_ver won't be updated, we'll
// fall into the empty_pool code path below.
}
} else {
if (settings.verbose > 1) {
fprintf(stderr, "ERROR: conc missing pool name\n");
}
goto fail;
}
}
// If there were any proxies that weren't updated in the
// previous loop, we need to shut them down. We mark the
// proxy->config as NULL, and cproxy_check_downstream_config()
// will catch it.
//
// TODO: Close any listening conns for the proxy?
// TODO: Close any upstream conns for the proxy?
// TODO: We still need to free proxy memory, after all its
// proxy_td's and downstreams are closed, and no more
// upstreams are pointed at the proxy.
//
proxy_behavior_pool empty_pool;
memset(&empty_pool, 0, sizeof(proxy_behavior_pool));
empty_pool.base = m->behavior;
empty_pool.num = 0;
empty_pool.arr = NULL;
for (proxy *p = m->proxy_head; p != NULL; p = p->next) {
bool down = false;
int port = 0;
char *name = NULL;
pthread_mutex_lock(&p->proxy_lock);
if (p->config_ver != new_config_ver) {
down = true;
assert(p->port > 0);
assert(p->name != NULL);
port = p->port;
name = strdup(p->name);
}
pthread_mutex_unlock(&p->proxy_lock);
if (down) {
cproxy_on_new_pool(m, name, port, NULL, new_config_ver,
&empty_pool);
}
if (name != NULL) {
free(name);
}
}
free_kvpair(kvs);
out:
work_collect_one(completion);
return;
fail:
m->stat_config_fails++;
free_kvpair(kvs);
if (settings.verbose > 1) {
fprintf(stderr, "ERROR: conc failed config %llu\n",
(long long unsigned int) m->stat_config_fails);
}
goto out;
}
/**
* A name and port uniquely identify a proxy.
*/
void cproxy_on_new_pool(proxy_main *m,
char *name, int port,
char *config,
uint32_t config_ver,
proxy_behavior_pool *behavior_pool) {
assert(m);
assert(name != NULL);
assert(port >= 0);
assert(is_listen_thread());
// See if we've already got a proxy running with that name and port,
// and create one if needed.
//
bool found = false;
proxy *p = m->proxy_head;
while (p != NULL && !found) {
pthread_mutex_lock(&p->proxy_lock);
assert(p->port > 0);
assert(p->name != NULL);
found = ((p->port == port) &&
(strcmp(p->name, name) == 0));
pthread_mutex_unlock(&p->proxy_lock);
if (found) {
break;
}
p = p->next;
}
if (p == NULL) {
p = cproxy_create(name, port,
config,
config_ver,
behavior_pool,
m->nthreads);
if (p != NULL) {
p->next = m->proxy_head;
m->proxy_head = p;
int n = cproxy_listen(p);
if (n > 0) {
if (settings.verbose > 2) {
fprintf(stderr,
"cproxy_listen success %u to %s with %d conns\n",
p->port, p->config, n);
}
m->stat_proxy_starts++;
} else {
if (settings.verbose > 1) {
fprintf(stderr,
"ERROR: cproxy_listen failed on %u to %s\n",
p->port, p->config);
}
m->stat_proxy_start_fails++;
}
}
} else {
if (settings.verbose > 2) {
fprintf(stderr, "conp existing config change %u\n",
p->port);
}
bool changed = false;
bool shutdown = false;
// Turn off the front_cache while we're reconfiguring.
//
mcache_stop(&p->front_cache);
matcher_stop(&p->front_cache_matcher);
matcher_stop(&p->front_cache_unmatcher);
matcher_stop(&p->optimize_set_matcher);
pthread_mutex_lock(&p->proxy_lock);
if (settings.verbose > 2) {
if (p->config && config &&
strcmp(p->config, config) != 0) {
fprintf(stderr,
"conp config changed from %s to %s\n",
p->config, config);
}
}
changed =
update_str_config(&p->config, config,
"conp config changed") ||
changed;
changed =
(cproxy_equal_behavior(&p->behavior_pool.base,
&behavior_pool->base) == false) ||
changed;
p->behavior_pool.base = behavior_pool->base;
changed =
update_behaviors_config(&p->behavior_pool.arr,
&p->behavior_pool.num,
behavior_pool->arr,
behavior_pool->num,
"conp behaviors changed") ||
changed;
if (p->config != NULL &&
p->behavior_pool.arr != NULL) {
m->stat_proxy_existings++;
} else {
m->stat_proxy_shutdowns++;
shutdown = true;
}
assert(config_ver != p->config_ver);
p->config_ver = config_ver;
pthread_mutex_unlock(&p->proxy_lock);
if (settings.verbose > 2) {
fprintf(stderr, "conp changed %s, shutdown %s\n",
changed ? "true" : "false",
shutdown ? "true" : "false");
}
// Restart the front_cache, if necessary.
//
if (shutdown == false) {
if (behavior_pool->base.front_cache_max > 0 &&
behavior_pool->base.front_cache_lifespan > 0) {
mcache_start(&p->front_cache,
behavior_pool->base.front_cache_max);
if (strlen(behavior_pool->base.front_cache_spec) > 0) {
matcher_start(&p->front_cache_matcher,
behavior_pool->base.front_cache_spec);
}
if (strlen(behavior_pool->base.front_cache_unspec) > 0) {
matcher_start(&p->front_cache_unmatcher,
behavior_pool->base.front_cache_unspec);
}
}
if (strlen(behavior_pool->base.optimize_set) > 0) {
matcher_start(&p->optimize_set_matcher,
behavior_pool->base.optimize_set);
}
}
// Send update across worker threads, avoiding locks.
//
work_collect wc = {0};
work_collect_init(&wc, m->nthreads - 1, NULL);
for (int i = 1; i < m->nthreads; i++) {
LIBEVENT_THREAD *t = thread_by_index(i);
assert(t);
assert(t->work_queue);
proxy_td *ptd = &p->thread_data[i];
if (t &&
t->work_queue) {
work_send(t->work_queue, update_ptd_config, ptd, &wc);
}
}
work_collect_wait(&wc);
}
}
// ----------------------------------------------------------
static void update_ptd_config(void *data0, void *data1) {
proxy_td *ptd = data0;
assert(ptd);
proxy *p = ptd->proxy;
assert(p);
work_collect *c = data1;
assert(c);
assert(is_listen_thread() == false); // Expecting a worker thread.
pthread_mutex_lock(&p->proxy_lock);
bool changed = false;
int port = p->port;
int prev = ptd->config_ver;
if (ptd->config_ver != p->config_ver) {
ptd->config_ver = p->config_ver;
changed =
update_str_config(&ptd->config, p->config, NULL) ||
changed;
ptd->behavior_pool.base = p->behavior_pool.base;
changed =
update_behaviors_config(&ptd->behavior_pool.arr,
&ptd->behavior_pool.num,
p->behavior_pool.arr,
p->behavior_pool.num,
NULL) ||
changed;
}
pthread_mutex_unlock(&p->proxy_lock);
// Restart the key_stats, if necessary.
//
if (changed) {
mcache_stop(&ptd->key_stats);
matcher_stop(&ptd->key_stats_matcher);
matcher_stop(&ptd->key_stats_unmatcher);
if (ptd->config != NULL) {
if (ptd->behavior_pool.base.key_stats_max > 0 &&
ptd->behavior_pool.base.key_stats_lifespan > 0) {
mcache_start(&ptd->key_stats,
ptd->behavior_pool.base.key_stats_max);
if (strlen(ptd->behavior_pool.base.key_stats_spec) > 0) {
matcher_start(&ptd->key_stats_matcher,
ptd->behavior_pool.base.key_stats_spec);
}
if (strlen(ptd->behavior_pool.base.key_stats_unspec) > 0) {
matcher_start(&ptd->key_stats_unmatcher,
ptd->behavior_pool.base.key_stats_unspec);
}
}
}
if (settings.verbose > 2) {
fprintf(stderr, "update_ptd_config %u, %u to %u\n",
port, prev, ptd->config_ver);
}
} else {
if (settings.verbose > 2) {
fprintf(stderr, "update_ptd_config %u, %u = %u no change\n",
port, prev, ptd->config_ver);
}
}
work_collect_one(c);
}
// ----------------------------------------------------------
static bool update_str_config(char **curr, char *next, char *descrip) {
bool rv = false;
if ((*curr != NULL) &&
(next == NULL ||
strcmp(*curr, next) != 0)) {
free(*curr);
*curr = NULL;
rv = true;
if (descrip != NULL &&
settings.verbose > 2) {
fprintf(stderr, "%s\n", descrip);
}
}
if (*curr == NULL && next != NULL) {
*curr = trimstrdup(next);
}
return rv;
}
static bool update_behaviors_config(proxy_behavior **curr,
int *curr_num,
proxy_behavior *next,
int next_num,
char *descrip) {
bool rv = false;
if ((*curr != NULL) &&
(next == NULL ||
cproxy_equal_behaviors(*curr_num,
*curr,
next_num,
next) == false)) {
free(*curr);
*curr = NULL;
*curr_num = 0;
rv = true;
if (descrip != NULL &&
settings.verbose > 2) {
fprintf(stderr, "%s\n", descrip);
}
}
if (*curr == NULL && next != NULL) {
*curr = cproxy_copy_behaviors(next_num,
next);
*curr_num = next_num;
}
return rv;
}
// ----------------------------------------------------------
/**
* Parse server-level behaviors from a pool into a given
* array of behaviors, one entry for each server.
*
* An example prefix is "svr".
*/
char *parse_kvs_servers(char *prefix,
char *pool_name,
kvpair_t *kvs,
char **servers,
proxy_behavior_pool *behavior_pool) {
assert(prefix);
assert(pool_name);
assert(kvs);
assert(servers);
assert(behavior_pool);
assert(behavior_pool->arr);
if (behavior_pool->num <= 0) {
return NULL;
}
// Create a config string that libmemcached likes.
// See memcached_servers_parse().
//
int config_len = 200;
char *config_str = calloc(config_len, 1);
for (int j = 0; servers[j]; j++) {
assert(j < behavior_pool->num);
// Inherit default behavior.
//
behavior_pool->arr[j] = behavior_pool->base;
parse_kvs_behavior(kvs, prefix, servers[j],
&behavior_pool->arr[j]);
// Grow config string for libmemcached.
//
int x = 40 + // For port and weight.
strlen(config_str) +
strlen(behavior_pool->arr[j].host);
if (config_len < x) {
config_len = 2 * (config_len + x);
config_str = realloc(config_str, config_len);
}
char *config_end = config_str + strlen(config_str);
if (config_end != config_str) {
*config_end++ = ',';
}
if (strlen(behavior_pool->arr[j].host) > 0 &&
behavior_pool->arr[j].port > 0) {
snprintf(config_end,
config_len - (config_end - config_str),
"%s:%u",
behavior_pool->arr[j].host,
behavior_pool->arr[j].port);
} else {
if (settings.verbose > 1) {
fprintf(stderr,
"ERROR: missing host:port for svr-%s in %s\n",
servers[j], pool_name);
}
}
if (behavior_pool->arr[j].downstream_weight > 0) {
config_end = config_str + strlen(config_str);
snprintf(config_end,
config_len - (config_end - config_str),
":%u",
behavior_pool->arr[j].downstream_weight);
}
if (settings.verbose > 2) {
cproxy_dump_behavior(&behavior_pool->arr[j],
"pks", 0);
}
}
return config_str;
}
// ----------------------------------------------------------
/**
* Parse a "[prefix]-[name]" configuration section into a behavior.
*/
char **parse_kvs_behavior(kvpair_t *kvs,
char *prefix,
char *name,
proxy_behavior *behavior) {
assert(kvs);
assert(prefix);
assert(name);
assert(behavior);
char key[800];
snprintf(key, sizeof(key), "%s-%s", prefix, name);
char **props = get_key_values(kvs, key);
for (int k = 0; props && props[k]; k++) {
char *key_val = trimstrdup(props[k]);
if (key_val != NULL) {
cproxy_parse_behavior_key_val_str(key_val, behavior);
free(key_val);
}
}
return props;
}
// ----------------------------------------------------------
char **get_key_values(kvpair_t *kvs, char *key) {
kvpair_t *x = find_kvpair(kvs, key);
if (x != NULL) {
return x->values;
}
return NULL;
}
Jump to Line
Something went wrong with that request. Please try again.