proxy_behavior cproxy_parse_behavior(char *behavior_str, proxy_behavior behavior_default) { // These are the default proxy behaviors. // struct proxy_behavior behavior = behavior_default; if (behavior_str == NULL || strlen(behavior_str) <= 0) { return behavior; } // Parse the key-value behavior_str, to override the defaults. // char *buff = trimstrdup(behavior_str); char *next = buff; while (next != NULL) { char *key_val = trimstr(strsep(&next, ",")); if (key_val != NULL) { cproxy_parse_behavior_key_val_str(key_val, &behavior); } } free(buff); return behavior; }
/** 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) { 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 *config = 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")) { jid = strsep(&val, "%"); jpw = val; } else if (wordeq(key, "config")) { config = val; } else if (wordeq(key, "host")) { 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 || strlen(jid) <= 0) { fprintf(stderr, "missing conflate id\n"); exit(EXIT_FAILURE); } 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 || strlen(jpw) <= 0) { fprintf(stderr, "missing conflate password\n"); exit(EXIT_FAILURE); } int config_alloc = 0; if (config == NULL) { config_alloc = strlen(jid) + 100; config = calloc(config_alloc, 1); if (config != NULL) { snprintf(config, config_alloc, CONFLATE_DB_PATH "/%s.cfg", jid); } else { fprintf(stderr, "conflate config buf alloc\n"); exit(EXIT_FAILURE); } } if (settings.verbose > 1) { fprintf(stderr, "cproxy_init jid %s\n", jid); } if (cproxy_init_agent_start(jid, jpw, config, host, behavior, nthreads) != NULL) { rv++; } if (config_alloc > 0 && config != NULL) { free(config); } if (jpwmem) { free(jpwmem); } } free(buff); return rv; }
int cproxy_init_string(char *cfg_str, proxy_behavior behavior, int nthreads) { /* cfg looks like "local_port=host:port,host:port;local_port=host:port" * like "11222=memcached1.foo.net:11211" This means local port 11222 * will be a proxy to downstream memcached server running at * host memcached1.foo.net on port 11211. */ if (cfg_str== NULL || strlen(cfg_str) <= 0) { return 0; } char *buff; char *next; char *proxy_name = "default"; char *proxy_sect; char *proxy_port_str; int proxy_port; if (settings.verbose > 1) { cproxy_dump_behavior(&behavior, "init_string", 2); } buff = trimstrdup(cfg_str); next = buff; while (next != NULL) { proxy_sect = strsep(&next, ";"); proxy_port_str = trimstr(strsep(&proxy_sect, "=")); if (proxy_sect == NULL) { moxi_log_write("bad moxi config, missing =\n"); exit(EXIT_FAILURE); } proxy_port = atoi(proxy_port_str); if (proxy_port <= 0) { moxi_log_write("missing proxy port\n"); exit(EXIT_FAILURE); } proxy_sect = trimstr(proxy_sect); int behaviors_num = 1; // Number of servers. for (char *x = proxy_sect; *x != '\0'; x++) { if (*x == ',') { behaviors_num++; } } proxy_behavior_pool behavior_pool; memset(&behavior_pool, 0, sizeof(proxy_behavior_pool)); behavior_pool.base = behavior; behavior_pool.num = behaviors_num; behavior_pool.arr = calloc(behaviors_num, sizeof(proxy_behavior)); if (behavior_pool.arr != NULL) { for (int i = 0; i < behaviors_num; i++) { behavior_pool.arr[i] = behavior; } proxy_main *m = cproxy_gen_proxy_main(behavior, nthreads, PROXY_CONF_TYPE_STATIC); if (m == NULL) { moxi_log_write("could not alloc proxy_main\n"); exit(EXIT_FAILURE); } proxy *p = cproxy_create(m, proxy_name, proxy_port, proxy_sect, 0, // config_ver. &behavior_pool, nthreads); if (p != NULL) { pthread_mutex_lock(&m->proxy_main_lock); p->next = m->proxy_head; m->proxy_head = p; pthread_mutex_unlock(&m->proxy_main_lock); int n = cproxy_listen(p); if (n > 0) { if (settings.verbose > 1) { moxi_log_write("moxi listening on %d with %d conns\n", proxy_port, n); } } else { moxi_log_write("moxi error -- port %d unavailable?\n", proxy_port); exit(EXIT_FAILURE); } } else { moxi_log_write("could not alloc proxy\n"); exit(EXIT_FAILURE); } free(behavior_pool.arr); } else { moxi_log_write("could not alloc behaviors\n"); exit(EXIT_FAILURE); } } free(buff); return 0; }
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); } // 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 {