F_NONNULL static dc_t* config_res_perdc(const unsigned mapnum, const vscf_data_t* cfg, const char* resname) { dmn_assert(cfg); dmn_assert(resname); dmn_assert(vscf_is_hash(cfg)); const unsigned num_dcs = vscf_hash_get_len(cfg); dc_t* store = calloc((num_dcs + 1), sizeof(dc_t)); for(unsigned i = 0; i < num_dcs; i++) { const char* dcname = vscf_hash_get_key_byindex(cfg, i, NULL); const unsigned dc_idx = map_get_dcidx(mapnum, dcname); if(!dc_idx) log_fatal("plugin_" PNSTR ": resource '%s': datacenter name '%s' is not valid", resname, dcname); dmn_assert(dc_idx <= num_dcs); dc_t* this_dc = &store[dc_idx]; this_dc->dc_name = strdup(dcname); const vscf_data_t* plugdata = vscf_hash_get_data_byindex(cfg, i); if(vscf_is_simple(plugdata)) { const char* textdata = vscf_simple_get_data(plugdata); if(*textdata == '%') { char* child_plugname = strdup(textdata + 1); this_dc->plugin_name = child_plugname; char* child_resname = strchr(child_plugname, '!'); if(child_resname) { *child_resname++ = '\0'; this_dc->res_name = strdup(child_resname); } if(!strcmp(this_dc->plugin_name, PNSTR) && !strcmp(this_dc->res_name, resname)) log_fatal("plugin_" PNSTR ": resource '%s': not allowed to reference itself!", resname); } else if(*textdata == '!') { this_dc->res_name = strdup(textdata + 1); const vscf_data_t* res_cfg = vscf_get_parent(cfg); this_dc->plugin_name = get_defaulted_plugname(res_cfg, resname, dcname); if(!strcmp(this_dc->plugin_name, PNSTR) && !strcmp(this_dc->res_name, resname)) log_fatal("plugin_" PNSTR ": resource '%s': not allowed to reference itself!", resname); } else { anysin_t tempsin; if(gdnsd_anysin_getaddrinfo(textdata, NULL, &tempsin)) { // failed to parse as address, so set up direct CNAME if possible uint8_t* dname = malloc(256); dname_status_t dnstat = vscf_simple_get_as_dname(plugdata, dname); if(dnstat == DNAME_INVALID) log_fatal("plugin_" PNSTR ": resource '%s': CNAME for datacenter '%s' is not a legal domainname", resname, dcname); if(dnstat == DNAME_VALID) dname = dname_trim(dname); this_dc->dname = dname; } else { inject_child_plugin_config(this_dc, resname, (vscf_data_t*)plugdata); } } } else { inject_child_plugin_config(this_dc, resname, (vscf_data_t*)plugdata); } } return store; }
F_NONNULL static void config_addrset(const char* res_name, const char* stanza, const bool ipv6, addrset_t* addrset, const vscf_data_t* cfg) { dmn_assert(res_name); dmn_assert(stanza); dmn_assert(addrset); dmn_assert(cfg); if(!vscf_is_hash(cfg)) log_fatal("plugin_weighted: resource '%s' stanza '%s' value must be a hash", res_name, stanza); const vscf_data_t* parent = vscf_get_parent(cfg); // inherit down the applicable res-level parameters vscf_hash_inherit(parent, (vscf_data_t*)cfg, "service_types", true); vscf_hash_inherit(parent, (vscf_data_t*)cfg, "multi", true); vscf_hash_inherit(parent, (vscf_data_t*)cfg, "up_thresh", true); // Get a starting assumption of our item count addrset->count = vscf_hash_get_len(cfg); /////// Process the parameters... // service_types const vscf_data_t* res_stypes = vscf_hash_get_data_byconstkey(cfg, "service_types", true); if (res_stypes) { addrset->count--; // minus one for service_types entry addrset->num_svcs = vscf_array_get_len(res_stypes); if(!addrset->num_svcs) log_fatal("plugin_weighted: resource '%s' (%s): service_types cannot be an empty array", res_name, stanza); addrset->svc_names = malloc(addrset->num_svcs * sizeof(char*)); for(unsigned i = 0; i < addrset->num_svcs; i++) { const vscf_data_t* this_svc_cfg = vscf_array_get_data(res_stypes, i); if(!vscf_is_simple(this_svc_cfg)) log_fatal("plugin_weighted: resource '%s' (%s): service_types values must be strings", res_name, stanza); addrset->svc_names[i] = strdup(vscf_simple_get_data(this_svc_cfg)); } } else { addrset->num_svcs = 1; addrset->svc_names = malloc(sizeof(char*)); addrset->svc_names[0] = strdup("default"); } // multi option addrset->multi = false; const vscf_data_t* multi_cfg = vscf_hash_get_data_byconstkey(cfg, "multi", true); if(multi_cfg) { addrset->count--; // minus one for multi entry if(!vscf_is_simple(multi_cfg) || !vscf_simple_get_as_bool(multi_cfg, &addrset->multi)) log_fatal("plugin_weighted: resource '%s' (%s): 'multi' must be a boolean value ('true' or 'false')", res_name, stanza); } // up threshold as double double up_thresh = 0.5; const vscf_data_t* thresh_cfg = vscf_hash_get_data_byconstkey(cfg, "up_thresh", true); if(thresh_cfg) { addrset->count--; // minus one for up_thresh entry if(!vscf_is_simple(thresh_cfg) || !vscf_simple_get_as_double(thresh_cfg, &up_thresh) || up_thresh <= 0.0 || up_thresh > 1.0) log_fatal("plugin_weighted: resource '%s' (%s): 'up_thresh' must be a floating point value in the range (0.0 - 1.0]", res_name, stanza); } if(addrset->count > MAX_ITEMS_PER_SET) log_fatal("plugin_weighted: resource '%s' (%s): number of direct groups or addrs within one family cannot be more than %u", res_name, stanza, MAX_ITEMS_PER_SET); if(!addrset->count) log_fatal("plugin_weighted: resource '%s' (%s): empty address-family sets not allowed", res_name, stanza); // track maximum res-size actually configured if(cfg_max_items_per_res < addrset->count) cfg_max_items_per_res = addrset->count; addrset->items = calloc(addrset->count, sizeof(res_aitem_t)); addrset->gmode = RES_ASET_UNKNOWN; addr_iter_data_t aid = { .item_idx = 0, .addrset = addrset, .res_name = res_name, .stanza = stanza, .ipv6 = ipv6 }; vscf_hash_iterate(cfg, true, config_addrset_item, &aid); addrset->weight = 0; addrset->max_weight = 0; for(unsigned i = 0; i < addrset->count; i++) { const unsigned iwt = addrset->items[i].weight; dmn_assert(iwt); dmn_assert(addrset->items[i].max_weight); addrset->weight += iwt; if(addrset->max_weight < iwt) addrset->max_weight = iwt; } dmn_assert(addrset->weight); dmn_assert(addrset->max_weight); addrset->up_weight = ceil(up_thresh * addrset->weight); dmn_assert(addrset->up_weight); } typedef struct { cnset_t* cnset; const char* res_name; const char* stanza; unsigned item_idx; } cname_iter_data_t; F_NONNULL static bool config_item_cname(const char* item_name, unsigned klen V_UNUSED, const vscf_data_t* cfg_data, void* cid_asvoid) { dmn_assert(item_name); dmn_assert(cfg_data); dmn_assert(cid_asvoid); cname_iter_data_t* cid = (cname_iter_data_t*)cid_asvoid; cnset_t* cnset = cid->cnset; const char* res_name = cid->res_name; const char* stanza = cid->stanza; const unsigned item_idx = cid->item_idx++; res_citem_t* res_item = &cnset->items[item_idx]; long wtemp = 0; if(!vscf_is_array(cfg_data) || (2 != vscf_array_get_len(cfg_data)) || !vscf_is_simple(vscf_array_get_data(cfg_data, 0)) || !vscf_is_simple(vscf_array_get_data(cfg_data, 1)) || !vscf_simple_get_as_long(vscf_array_get_data(cfg_data, 1), &wtemp) || wtemp < 1 || wtemp > MAX_WEIGHT ) log_fatal("plugin_weighted: resource '%s' (%s), item '%s': values in cname mode must be arrays of [ CNAME, WEIGHT ], where weight must be an integer in the range 1 - " MAX_WEIGHT_STR, res_name, stanza, item_name); res_item->weight = wtemp; uint8_t* dname = malloc(256); dname_status_t dnstat = vscf_simple_get_as_dname(vscf_array_get_data(cfg_data, 0), dname); if(dnstat == DNAME_INVALID) log_fatal("plugin_weighted: resource '%s' (%s), item '%s': '%s' is not a legal domainname", res_name, stanza, item_name, vscf_simple_get_data(vscf_array_get_data(cfg_data, 0))); if(dnstat == DNAME_VALID) dname = dname_trim(dname); res_item->cname = dname; log_debug("plugin_weighted: resource '%s' (%s), item '%s', CNAME '%s' added with weight %u", res_name, stanza, item_name, logf_dname(dname), res_item->weight); return true; }
F_NONNULL static void inject_child_plugin_config(dc_t* this_dc, const char* resname, vscf_data_t* cfg) { dmn_assert(this_dc); dmn_assert(resname); dmn_assert(cfg); char* child_resname = make_synth_resname(resname, this_dc->dc_name); this_dc->res_name = child_resname; // Move up 2 layers: dcX -> dcmap -> resX vscf_data_t* res_cfg = (vscf_data_t*)cfg; for(unsigned i = 0; i < 2; i++) { res_cfg = (vscf_data_t*)vscf_get_parent(res_cfg); dmn_assert(res_cfg); } // Move up 3 more layers: // resX -> resources -> metafo|geoip -> plugins vscf_data_t* plugins_top = res_cfg; for(unsigned i = 0; i < 3; i++) { plugins_top = (vscf_data_t*)vscf_get_parent(plugins_top); dmn_assert(plugins_top); } // synth multifo stanza for: dc1 => 192.0.2.1, or dc1 => [ 192.0.2.1, ... ] bool cfg_synthed = false; if(!vscf_is_hash(cfg)) { // synthesize a hash for multifo for single/array vscf_data_t* newhash = vscf_hash_new(); vscf_data_t* plugname_cfg = vscf_simple_new("multifo", 7); vscf_hash_add_val("plugin", 6, newhash, plugname_cfg); const unsigned alen = vscf_array_get_len(cfg); for(unsigned i = 0; i < alen; i++) { const vscf_data_t* this_addr_cfg = vscf_array_get_data(cfg, i); if(!vscf_is_simple(this_addr_cfg)) log_fatal("plugin_" PNSTR ": resource '%s': datacenter '%s': if defined as an array, array values must all be address strings", resname, this_dc->dc_name); const unsigned lnum = i + 1; char lbuf[12]; snprintf(lbuf, 12, "%u", lnum); vscf_hash_add_val(lbuf, strlen(lbuf), newhash, vscf_clone(this_addr_cfg, false)); } cfg_synthed = true; cfg = newhash; } // inherit resource-level stuff down to dc-level vscf_hash_inherit_all(res_cfg, cfg, true); this_dc->plugin_name = get_defaulted_plugname(cfg, resname, this_dc->dc_name); if(!strcmp(this_dc->plugin_name, PNSTR)) log_fatal("plugin_" PNSTR ": resource '%s': datacenter '%s': plugin_" PNSTR " cannot synthesize config for itself...", resname, this_dc->dc_name); // Create top-level plugins => { foo => {} } if necc vscf_data_t* plug_cfg = (vscf_data_t*)vscf_hash_get_data_bystringkey(plugins_top, this_dc->plugin_name, false); if(!plug_cfg) { plug_cfg = vscf_hash_new(); vscf_hash_add_val(this_dc->plugin_name, strlen(this_dc->plugin_name), plugins_top, plug_cfg); } // special-case for geoip -> metafo synthesis, use resources sub-stanza if(!strcmp(this_dc->plugin_name, "metafo")) { vscf_data_t* synth_res_cfg = (vscf_data_t*)vscf_hash_get_data_byconstkey(plug_cfg, "resources", false); if(!synth_res_cfg) { synth_res_cfg = vscf_hash_new(); vscf_hash_add_val("resources", strlen("resources"), plug_cfg, synth_res_cfg); } plug_cfg = synth_res_cfg; // for below } // Check if resource already exists if(vscf_hash_get_data_bystringkey(plug_cfg, child_resname, false)) log_fatal("plugin_" PNSTR ": resource '%s': datacenter '%s': synthesis of resource '%s' for plugin '%s' failed (resource name already exists)", resname, this_dc->dc_name, child_resname, this_dc->plugin_name); // Add it, using clone() to skip marked key "plugin" vscf_hash_add_val(child_resname, strlen(child_resname), plug_cfg, vscf_clone(cfg, true)); // destroy clone source if synthesized and disconnected from real config tree if(cfg_synthed) vscf_destroy(cfg); }