/* See the DRL-LICENSE file for this file's software license. */ #include #include #include #include #include #include #include #include #include #include #include "raterouter.h" #include "ratetypes.h" #include "config.h" #include "util.h" #include "logging.h" void free_ident(ident_config *ident) { /* Free peers. */ while (ident->peers) { ident_peer *tofree = ident->peers; ident->peers = ident->peers->next; free(tofree); } /* Free members. */ while (ident->members) { ident_member *tofree = ident->members; ident->members = ident->members->next; free(tofree); } /* Free ident. */ free(ident); } void free_ident_list(ident_config *list) { ident_config *tofree; while (list) { tofree = list; list = list->next; free_ident(tofree); } } static int xid_filter(const struct dirent *d) { if (atoi(d->d_name) > 0) return 1; else return 0; } int get_eligible_leaves(drl_instance_t *instance) { struct dirent **names; int count, i; leaf_t *leaves = NULL; map_handle leaf_map = allocate_map(); if (leaf_map == NULL) { return ENOMEM; } count = scandir("/proc/virtual", &names, xid_filter, alphasort); if (count < 1) { return 1; } leaves = malloc(count * sizeof(leaf_t)); if (leaves == NULL) { /* Couldn't allocate leaves array. Need to free names memory. */ while (count--) { free(names[count]); } free(names); return ENOMEM; } memset(leaves, 0, count * sizeof(leaf_t)); for (i = 0; i < count; ++i) { leaves[i].xid = atoi(names[i]->d_name); leaves[i].parent = NULL; leaves[i].drop_prob = 0.0; leaves[i].delay = 0; free(names[i]); map_insert(leaf_map, &leaves[i].xid, sizeof(leaves[i].xid), &leaves[i]); } free(names); instance->leaf_map = leaf_map; instance->leaves = leaves; instance->leaf_count = count; return 0; } int parse_leaves(drl_instance_t *instance, char *leafstring) { int count = 1; int i; char *comma = leafstring; leaf_t *leaves = NULL; map_handle leaf_map; while ((comma = strchr(comma, ',')) != NULL) { count += 1; comma++; } leaf_map = allocate_map(); if (leaf_map == NULL) { return ENOMEM; } leaves = malloc(count * sizeof(leaf_t)); if (leaves == NULL) { return ENOMEM; } memset(leaves, 0, count * sizeof(leaf_t)); comma = leafstring; for (i = 0; i < count; ++i) { leaves[i].xid = (uint32_t) strtol(comma, NULL, 16); leaves[i].parent = NULL; leaves[i].drop_prob = 0.0; leaves[i].delay = 0; printlog(LOG_DEBUG, "Read leaf - hex: %x, dec: %d\n", leaves[i].xid, leaves[i].xid); comma = strchr(comma, ','); comma++; } instance->leaf_map = leaf_map; instance->leaves = leaves; instance->leaf_count = count; return 0; } static int parse_common(xmlDocPtr doc, xmlNodePtr ident, ident_config *common) { xmlChar *id; xmlChar *limit; xmlChar *commfabric; xmlChar *branch; xmlChar *membership; xmlChar *failure_behavior; xmlChar *accounting; xmlChar *ewma; xmlChar *mainloop_intervals; xmlChar *communication_intervals; xmlChar *independent; xmlChar *htb_node; xmlChar *htb_parent; xmlNodePtr fields = ident->children; ident_peer *current = NULL; /* The struct has been memsetted to 0, this is just to be safe. */ #ifdef BUILD_ZOOKEEPER common->zk_host = NULL; #endif common->peers = NULL; common->members = NULL; common->next = NULL; /* Make sure no required fields are missing. */ id = xmlGetProp(ident, (const xmlChar *) "id"); if (id == NULL) { printlog(LOG_CRITICAL, "Ident missing globally unique identifier.\n"); return EINVAL; } else { common->id = atoi((const char *) id); xmlFree(id); } limit = xmlGetProp(ident, (const xmlChar *) "limit"); if (limit == NULL) { printlog(LOG_CRITICAL, "Ident missing global rate limit.\n"); return EINVAL; } else { common->limit = atoi((const char *) limit); xmlFree(limit); } commfabric = xmlGetProp(ident, (const xmlChar *) "commfabric"); if (commfabric == NULL) { printlog(LOG_CRITICAL, "Ident missing comm fabric specifier.\n"); return EINVAL; } else { if (!xmlStrcmp(commfabric, (const xmlChar *) "MESH")) { common->commfabric = COMM_MESH; } else if (!xmlStrcmp(commfabric, (const xmlChar *) "GOSSIP")) { common->commfabric = COMM_GOSSIP; } else { printlog(LOG_CRITICAL, "Unknown/invalid comm fabric.\n"); xmlFree(commfabric); return EINVAL; } xmlFree(commfabric); } /* Only care about branching factor and failure detector if we're using gossip. */ if (common->commfabric == COMM_GOSSIP) { branch = xmlGetProp(ident, (const xmlChar *) "branch"); if (branch == NULL) { printlog(LOG_CRITICAL, "Ident missing gossip branch.\n"); return EINVAL; } else { common->branch = atoi((const char *) branch); xmlFree(branch); } membership = xmlGetProp(ident, (const xmlChar *) "membership"); if (membership == NULL) { printlog(LOG_CRITICAL, "Ident missing membership protocol selection.\n"); return EINVAL; } else { if (!xmlStrcmp(membership, (const xmlChar *) "SWIM")) { common->membership = SWIM; } else if (!xmlStrcmp(membership, (const xmlChar *) "ZOOKEEPER")) { #ifdef BUILD_ZOOKEEPER common->membership = ZOOKEEPER; #else printlog(LOG_CRITICAL, "Zookeeper requested, but support not compiled into DRL at configure time.\n"); xmlFree(membership); return EINVAL; #endif } else { printlog(LOG_CRITICAL, "Unknown/invalid gossip group membership protocol.\n"); xmlFree(membership); return EINVAL; } xmlFree(membership); } failure_behavior = xmlGetProp(ident, (const xmlChar *) "failure_behavior"); if (failure_behavior == NULL) { printlog(LOG_CRITICAL, "Ident missing failure handling behavior.\n"); return EINVAL; } else { if (!xmlStrcmp(failure_behavior, (const xmlChar *) "PANIC")) { common->failure_behavior = PANIC; } else if (!xmlStrcmp(failure_behavior, (const xmlChar *) "QUORUM")) { common->failure_behavior = QUORUM; } else { printlog(LOG_CRITICAL, "Unknown/invalid gossip failure behavior policy.\n"); xmlFree(failure_behavior); return EINVAL; } xmlFree(failure_behavior); } } accounting = xmlGetProp(ident, (const xmlChar *) "accounting"); if (accounting == NULL) { printlog(LOG_CRITICAL, "Ident missing accounting.\n"); return EINVAL; } else { if (!xmlStrcmp(accounting, (const xmlChar *) "STANDARD")) { common->accounting = ACT_STANDARD; } else if (!xmlStrcmp(accounting, (const xmlChar *) "SAMPLEHOLD")) { common->accounting = ACT_SAMPLEHOLD; } else if (!xmlStrcmp(accounting, (const xmlChar *) "SIMPLE")) { common->accounting = ACT_SIMPLE; } else if (!xmlStrcmp(accounting, (const xmlChar *) "MULTIPLEINTERVAL")) { common->accounting = ACT_MULTIPLE; } else { printlog(LOG_CRITICAL, "Unknown/invalid accounting table.\n"); xmlFree(accounting); return EINVAL; } xmlFree(accounting); } ewma = xmlGetProp(ident, (const xmlChar *) "ewma"); if (ewma == NULL) { printlog(LOG_CRITICAL, "Ident missing ewma weight.\n"); return EINVAL; } else { common->fixed_ewma_weight = atof((const char *) ewma); xmlFree(ewma); } mainloop_intervals = xmlGetProp(ident, (const xmlChar *) "loop_intervals"); if (mainloop_intervals == NULL) { printlog(LOG_WARN, "Ident id: %d missing loop_intervals, assuming 1.\n", common->id); common->mainloop_intervals = 1; } else { common->mainloop_intervals = atoi((const char *) mainloop_intervals); xmlFree(mainloop_intervals); } communication_intervals = xmlGetProp(ident, (const xmlChar *) "comm_intervals"); if (communication_intervals == NULL) { printlog(LOG_WARN, "Ident id: %d missing comm_intervals, assuming 1.\n", common->id); common->communication_intervals = 1; } else { common->communication_intervals = atoi((const char *) communication_intervals); xmlFree(communication_intervals); } independent = xmlGetProp(ident, (const xmlChar *) "independent"); if (independent == NULL) { common->independent = 0; } else { common->independent = atoi((const char *) independent); xmlFree(independent); } htb_node = xmlGetProp(ident, (const xmlChar *) "htb_node"); htb_parent = xmlGetProp(ident, (const xmlChar *) "htb_parent"); if (htb_node == NULL) { common->htb_node = -1; } else { sscanf((const char *)htb_node, "%x", &common->htb_node); xmlFree(htb_node); } if (htb_parent == NULL) { common->htb_parent = -1; } else { sscanf((const char *)htb_parent, "%x", &common->htb_parent); xmlFree(htb_parent); } while (fields != NULL) { if((!xmlStrcmp(fields->name, (const xmlChar *) "peer"))) { xmlChar *ip = xmlNodeListGetString(doc, fields->children, 1); if (current == NULL) { /* No peers yet. */ common->peers = malloc(sizeof(ident_peer)); if (common->peers == NULL) { return ENOMEM; } common->peers->ip = inet_addr((const char *) ip); common->peers->next = NULL; common->peer_count += 1; current = common->peers; } else { /* Add it to the list. */ current->next = malloc(sizeof(ident_peer)); if (current->next == NULL) { return ENOMEM; } current = current->next; current->ip = inet_addr((const char *) ip); common->peer_count += 1; current->next = NULL; } xmlFree(ip); } else if ((!xmlStrcmp(fields->name, (const xmlChar *) "zkhost"))) { xmlChar *host = xmlNodeListGetString(doc, fields->children, 1); #ifdef BUILD_ZOOKEEPER common->zk_host = strdup((const char *) host); if (common->zk_host == NULL) { return ENOMEM; } #endif xmlFree(host); } fields = fields->next; } if (common->peers == NULL) { printlog(LOG_CRITICAL, "Must have at least one peer.\n"); return EINVAL; } #ifdef BUILD_ZOOKEEPER if (common->membership == ZOOKEEPER && common->zk_host == NULL) { printlog(LOG_CRITICAL, "Group membership protocol ZOOKEEPER requires a zkhost field.\n"); return EINVAL; } #endif /* No errors. */ return 0; } static ident_config *parse_machine(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) { ident_config *common = malloc(sizeof(ident_config)); if (common == NULL) { return NULL; } memset(common, 0, sizeof(ident_config)); if (parse_common(doc, ident, common)) { free_ident(common); return NULL; } /* No further information needed for machine-level identities. */ common->type = IDENT_MACHINE; common->members = NULL; common->next = NULL; if (configs->last_machine == NULL) { configs->machines = common; configs->last_machine = common; } else { configs->last_machine->next = common; configs->last_machine = common; } configs->machine_count += 1; return common; } static ident_config *parse_set(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) { xmlNodePtr fields = ident->children; ident_config *common = malloc(sizeof(ident_config)); ident_member *current = NULL; if (common == NULL) { return NULL; } memset(common, 0, sizeof(ident_config)); if (parse_common(doc, ident, common)) { free_ident(common); return NULL; } while (fields != NULL) { ident_member *member = NULL; if (!xmlStrcmp(fields->name, (const xmlChar *) "xid")) { xmlChar *xid = xmlNodeListGetString(doc, fields->children, 1); if (atoi((const char *) xid) >= 0) { member = malloc(sizeof(ident_member)); if (member == NULL) { free_ident(common); xmlFree(xid); return NULL; } member->type = MEMBER_XID; sscanf((const char *) xid, "%x", &member->value); member->next = NULL; } else { free_ident(common); xmlFree(xid); return NULL; } xmlFree(xid); } else if (!xmlStrcmp(fields->name, (const xmlChar *) "guid")) { xmlChar *guid = xmlNodeListGetString(doc, fields->children, 1); if (atoi((const char *) guid) >= 0) { member = malloc(sizeof(ident_member)); if (member == NULL) { free_ident(common); xmlFree(guid); return NULL; } member->type = MEMBER_GUID; member->value = atoi((const char *) guid); member->next = NULL; } else { free_ident(common); xmlFree(guid); return NULL; } xmlFree(guid); } if (member) { if (common->members == NULL) { common->members = member; current = member; } else { current->next = member; current = member; } } fields = fields->next; } /* A sliver set must have at least one member (xid or guid) or else it is * meaningless. */ if (common->members == NULL) { free_ident(common); return NULL; } common->type = IDENT_SET; common->next = NULL; if (configs->last_set == NULL) { configs->sets = common; configs->last_set = common; } else { configs->last_set->next = common; configs->last_set = common; } configs->set_count += 1; return common; } int parse_drl_config(const char *configfile, parsed_configs *configs) { xmlDocPtr doc; xmlNodePtr drl, ident; configs->machines = NULL; configs->sets = NULL; configs->last_machine = NULL; configs->last_set = NULL; configs->machine_count = 0; configs->set_count = 0; if(!(doc = xmlParseFile(configfile))){ printlog(LOG_CRITICAL, "Config file (%s) not parsed successfully.\n", configfile); xmlFreeDoc(doc); return EIO; } if(!(drl = xmlDocGetRootElement(doc))){ printlog(LOG_CRITICAL, "Config file (%s) has no root element.\n", configfile); xmlFreeDoc(doc); return EIO; } if(xmlStrcmp(drl->name, (const xmlChar *) "drl")){ printlog(LOG_CRITICAL, "Config file (%s) of the wrong type, root node != drl\n", configfile); xmlFreeDoc(doc); return EIO; } ident = drl->children; while(ident != NULL) { ident_config *new = NULL; if((!xmlStrcmp(ident->name, (const xmlChar *) "machine"))) { new = parse_machine(doc, ident, configs); } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "set"))) { new = parse_set(doc, ident, configs); } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "text"))) { /* libxml seems to wrap everything inside two 'text's. */ ident = ident->next; continue; } if (new == NULL) { /* FIXME: Make this more descriptive. :) */ printlog(LOG_CRITICAL, "Error occurred while parsing...\n"); free_ident_list(configs->machines); free_ident_list(configs->sets); configs->machines = NULL; configs->sets = NULL; configs->last_machine = NULL; configs->last_set = NULL; xmlFreeDoc(doc); return 1; } ident = ident->next; } xmlFreeDoc(doc); /* Return the list of parsed identities. */ return 0; }