F_NONNULL static bool config_addr_group_addr(const char* lb_name, const unsigned lb_name_len, const vscf_data_t* lb_data, void* iaga_asvoid) { dmn_assert(lb_name); dmn_assert(lb_name_len); dmn_assert(lb_data); dmn_assert(iaga_asvoid); iaga_t* iaga = (iaga_t*)iaga_asvoid; addrset_t* addrset = iaga->addrset; res_aitem_t* res_item = iaga->res_item; unsigned lb_idx = iaga->lb_idx++; const char* res_name = iaga->res_name; const char* stanza = iaga->stanza; const char* item_name = iaga->item_name; const bool ipv6 = iaga->ipv6; const unsigned res_name_len = strlen(res_name); const unsigned item_name_len = strlen(item_name); long lb_weight = 0; if(!vscf_is_array(lb_data) || (2 != vscf_array_get_len(lb_data)) || !vscf_is_simple(vscf_array_get_data(lb_data, 0)) || !vscf_is_simple(vscf_array_get_data(lb_data, 1)) || !vscf_simple_get_as_long(vscf_array_get_data(lb_data, 1), &lb_weight) || lb_weight < 1 || lb_weight > MAX_WEIGHT ) log_fatal("plugin_weighted: resource '%s', group '%s': values in address group mode must be arrays of [ IPADDR, WEIGHT ], where weight must be an integer in the range 1 - " MAX_WEIGHT_STR, res_name, item_name); res_item->as[lb_idx].states = calloc(addrset->num_svcs, sizeof(mon_state_t)); res_item->as[lb_idx].weight = lb_weight; const char* addr_txt = vscf_simple_get_data(vscf_array_get_data(lb_data, 0)); int addr_err = gdnsd_anysin_getaddrinfo(addr_txt, NULL, &res_item->as[lb_idx].addr); if(addr_err) log_fatal("plugin_weighted: resource '%s', group '%s', addr '%s': parsing '%s' as an IP address failed: %s", res_name, item_name, lb_name, addr_txt, gai_strerror(addr_err)); if(ipv6 && res_item->as[lb_idx].addr.sa.sa_family != AF_INET6) log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv4, was expecting IPv6", res_name, stanza, item_name, addr_txt); else if(!ipv6 && res_item->as[lb_idx].addr.sa.sa_family != AF_INET) log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv6, was expecting IPv4", res_name, stanza, item_name, addr_txt); for(unsigned i = 0; i < addrset->num_svcs; i++) { const unsigned svc_name_len = strlen(addrset->svc_names[i]); char *complete_desc = malloc(res_name_len + 1 + 4 + 1 + item_name_len + 1 + lb_name_len + 1 + svc_name_len + 1); sprintf(complete_desc, "%s/%s/%s/%s/%s", res_name, ipv6 ? "ipv6" : "ipv4", item_name, lb_name, addrset->svc_names[i]); mon_list.info = realloc(mon_list.info, sizeof(mon_info_t) * (mon_list.count + 1)); mon_info_t* m = &mon_list.info[mon_list.count++]; m->svctype = addrset->svc_names[i]; m->desc = complete_desc; m->addr = addr_txt; m->state_ptr = &res_item->as[lb_idx].states[i]; } log_debug("plugin_weighted: resource '%s' (%s), item '%s', address %s added with weight %u", res_name, stanza, item_name, addr_txt, res_item->as[lb_idx].weight); return true; }
F_NONNULL static void config_item_addrs(res_aitem_t* res_item, const char* res_name, const char* stanza, const char* item_name, const bool ipv6, const vscf_data_t* cfg_data, addrset_t* addrset) { dmn_assert(res_name); dmn_assert(stanza); dmn_assert(item_name); dmn_assert(res_item); dmn_assert(cfg_data); dmn_assert(addrset); const unsigned res_name_len = strlen(res_name); const unsigned item_name_len = strlen(item_name); 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 addrs mode must be arrays of [ IPADDR, WEIGHT ], where weight must be an integer in the range 1 - " MAX_WEIGHT_STR, res_name, stanza, item_name); res_item->count = 1; res_item->as = calloc(res_item->count, sizeof(addrstate_t)); res_item->as[0].states = calloc(addrset->num_svcs, sizeof(mon_state_t)); res_item->as[0].weight = wtemp; res_item->max_weight = wtemp; res_item->weight = wtemp; const char* addr_txt = vscf_simple_get_data(vscf_array_get_data(cfg_data, 0)); int addr_err = gdnsd_anysin_getaddrinfo(addr_txt, NULL, &res_item->as[0].addr); if(addr_err) log_fatal("plugin_weighted: resource '%s' (%s): item '%s': parsing '%s' as an IP address failed: %s", res_name, stanza, item_name, addr_txt, gai_strerror(addr_err)); if(ipv6 && res_item->as[0].addr.sa.sa_family != AF_INET6) log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv4, was expecting IPv6", res_name, stanza, item_name, addr_txt); else if(!ipv6 && res_item->as[0].addr.sa.sa_family != AF_INET) log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv6, was expecting IPv4", res_name, stanza, item_name, addr_txt); for(unsigned i = 0; i < addrset->num_svcs; i++) { const unsigned svc_name_len = strlen(addrset->svc_names[i]); char *complete_desc = malloc(res_name_len + 1 + 4 + 1 + item_name_len + 1 + svc_name_len + 1); sprintf(complete_desc, "%s/%s/%s/%s", res_name, ipv6 ? "ipv6" : "ipv4", item_name, addrset->svc_names[i]); mon_list.info = realloc(mon_list.info, sizeof(mon_info_t) * (mon_list.count + 1)); mon_info_t* m = &mon_list.info[mon_list.count++]; m->svctype = addrset->svc_names[i]; m->desc = complete_desc; m->addr = addr_txt; m->state_ptr = &res_item->as[0].states[i]; } log_debug("plugin_weighted: resource '%s' (%s), item '%s': A '%s' added w/ weight %u", res_name, stanza, item_name, addr_txt, res_item->weight); }
F_NONNULL static bool config_addr_group_addr(const char* lb_name, const unsigned lb_name_len V_UNUSED, const vscf_data_t* lb_data, void* iaga_asvoid) { dmn_assert(lb_name); dmn_assert(lb_name_len); dmn_assert(lb_data); dmn_assert(iaga_asvoid); iaga_t* iaga = (iaga_t*)iaga_asvoid; addrset_t* addrset = iaga->addrset; res_aitem_t* res_item = iaga->res_item; unsigned lb_idx = iaga->lb_idx++; const char* res_name = iaga->res_name; const char* stanza = iaga->stanza; const char* item_name = iaga->item_name; const bool ipv6 = iaga->ipv6; long lb_weight = 0; if(!vscf_is_array(lb_data) || (2 != vscf_array_get_len(lb_data)) || !vscf_is_simple(vscf_array_get_data(lb_data, 0)) || !vscf_is_simple(vscf_array_get_data(lb_data, 1)) || !vscf_simple_get_as_long(vscf_array_get_data(lb_data, 1), &lb_weight) || lb_weight < 1 || lb_weight > MAX_WEIGHT ) log_fatal("plugin_weighted: resource '%s', group '%s': values in address group mode must be arrays of [ IPADDR, WEIGHT ], where weight must be an integer in the range 1 - " MAX_WEIGHT_STR, res_name, item_name); res_item->as[lb_idx].weight = lb_weight; const char* addr_txt = vscf_simple_get_data(vscf_array_get_data(lb_data, 0)); int addr_err = gdnsd_anysin_getaddrinfo(addr_txt, NULL, &res_item->as[lb_idx].addr); if(addr_err) log_fatal("plugin_weighted: resource '%s', group '%s', addr '%s': parsing '%s' as an IP address failed: %s", res_name, item_name, lb_name, addr_txt, gai_strerror(addr_err)); if(ipv6 && res_item->as[lb_idx].addr.sa.sa_family != AF_INET6) log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv4, was expecting IPv6", res_name, stanza, item_name, addr_txt); else if(!ipv6 && res_item->as[lb_idx].addr.sa.sa_family != AF_INET) log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv6, was expecting IPv4", res_name, stanza, item_name, addr_txt); if(addrset->num_svcs) { res_item->as[lb_idx].indices = malloc(addrset->num_svcs * sizeof(unsigned)); for(unsigned i = 0; i < addrset->num_svcs; i++) res_item->as[lb_idx].indices[i] = gdnsd_mon_addr(addrset->svc_names[i], &res_item->as[lb_idx].addr); } log_debug("plugin_weighted: resource '%s' (%s), item '%s', address %s added with weight %u", res_name, stanza, item_name, addr_txt, res_item->as[lb_idx].weight); return true; }
F_NONNULL static void config_item_addrs(res_aitem_t* res_item, const char* res_name, const char* stanza, const char* item_name, const bool ipv6, const vscf_data_t* cfg_data, addrset_t* addrset) { dmn_assert(res_name); dmn_assert(stanza); dmn_assert(item_name); dmn_assert(res_item); dmn_assert(cfg_data); dmn_assert(addrset); 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 addrs mode must be arrays of [ IPADDR, WEIGHT ], where weight must be an integer in the range 1 - " MAX_WEIGHT_STR, res_name, stanza, item_name); res_item->count = 1; res_item->as = calloc(res_item->count, sizeof(addrstate_t)); res_item->as[0].weight = wtemp; res_item->max_weight = wtemp; res_item->weight = wtemp; const char* addr_txt = vscf_simple_get_data(vscf_array_get_data(cfg_data, 0)); int addr_err = gdnsd_anysin_getaddrinfo(addr_txt, NULL, &res_item->as[0].addr); if(addr_err) log_fatal("plugin_weighted: resource '%s' (%s): item '%s': parsing '%s' as an IP address failed: %s", res_name, stanza, item_name, addr_txt, gai_strerror(addr_err)); if(ipv6 && res_item->as[0].addr.sa.sa_family != AF_INET6) log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv4, was expecting IPv6", res_name, stanza, item_name, addr_txt); else if(!ipv6 && res_item->as[0].addr.sa.sa_family != AF_INET) log_fatal("plugin_weighted: resource '%s' (%s): item '%s': '%s' is IPv6, was expecting IPv4", res_name, stanza, item_name, addr_txt); if(addrset->num_svcs) { res_item->as[0].indices = malloc(addrset->num_svcs * sizeof(unsigned)); for(unsigned i = 0; i < addrset->num_svcs; i++) res_item->as[0].indices[i] = gdnsd_mon_addr(addrset->svc_names[i], &res_item->as[0].addr); } log_debug("plugin_weighted: resource '%s' (%s), item '%s': A '%s' added w/ weight %u", res_name, stanza, item_name, addr_txt, res_item->weight); }
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; }