/* * src/element.c Elements * * Copyright (c) 2001-2013 Thomas Graf * Copyright (c) 2013 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include static LIST_HEAD(allowed); static LIST_HEAD(denied); static int match_mask(const struct policy *p, const char *str) { int i, n; char c; if (!p || !str) return 0; for (i = 0, n = 0; p->p_rule[i] != '\0'; i++) { if (p->p_rule[i] == '*') { c = tolower(p->p_rule[i + 1]); if (c == '\0') return 1; while (tolower(str[n]) != c) if (str[n++] == '\0') return 0; } else if (tolower(p->p_rule[i]) != tolower(str[n++])) return 0; } return str[n] == '\0' ? 1 : 0; } int element_allowed(const char *name, struct element_cfg *cfg) { struct policy *p; if (cfg) { if (cfg->ec_flags & ELEMENT_CFG_HIDE) return 0; else if (cfg->ec_flags & ELEMENT_CFG_SHOW) return 1; } list_for_each_entry(p, &denied, p_list) if (match_mask(p, name)) return 0; if (!list_empty(&allowed)) { list_for_each_entry(p, &allowed, p_list) if (match_mask(p, name)) return 1; return 0; } return 1; } void element_parse_policy(const char *policy) { char *start, *copy, *save = NULL, *tok; struct policy *p; if (!policy) return; copy = strdup(policy); start = copy; while ((tok = strtok_r(start, ",", &save)) != NULL) { start = NULL; p = xcalloc(1, sizeof(*p)); if (*tok == '!') { p->p_rule = strdup(++tok); list_add_tail(&p->p_list, &denied); } else { p->p_rule = strdup(tok); list_add_tail(&p->p_list, &allowed); } } xfree(copy); } struct element *__lookup_element(struct element_group *group, const char *name, uint32_t id, struct element *parent) { struct list_head *list; struct element *e; if (parent) list = &parent->e_childs; else list = &group->g_elements; list_for_each_entry(e, list, e_list) if (!strcmp(name, e->e_name) && e->e_id == id) return e; return NULL; } struct element *element_lookup(struct element_group *group, const char *name, uint32_t id, struct element *parent, int flags) { struct element_cfg *cfg; struct element *e; int i; if (!group) BUG(); if ((e = __lookup_element(group, name, id, parent))) return e; if (!(flags & ELEMENT_CREAT)) return NULL; cfg = element_cfg_lookup(name); if (!element_allowed(name, cfg)) return NULL; DBG("Creating element %d \"%s\"", id, name); e = xcalloc(1, sizeof(*e)); init_list_head(&e->e_list); init_list_head(&e->e_childs); init_list_head(&e->e_info_list); init_list_head(&e->e_attr_sorted); for (i = 0; i < ATTR_HASH_SIZE; i++) init_list_head(&e->e_attrhash[i]); e->e_name = strdup(name); e->e_id = id; e->e_parent = parent; e->e_group = group; e->e_lifecycles = get_lifecycles(); e->e_flags = ELEMENT_FLAG_CREATED; e->e_cfg = cfg; if (e->e_cfg) { if (e->e_cfg->ec_description) element_update_info(e, "Description", e->e_cfg->ec_description); element_set_rxmax(e, e->e_cfg->ec_rxmax); element_set_txmax(e, e->e_cfg->ec_txmax); } if (parent) { DBG("Attached to parent %d \"%s\"", parent->e_id, parent->e_name); list_add_tail(&e->e_list, &parent->e_childs); } else { DBG("Attached to group %s", group->g_name); list_add_tail(&e->e_list, &group->g_elements); } group->g_nelements++; return e; } void element_free(struct element *e) { struct info *info, *ninfo; struct element *c, *cnext; struct attr *a, *an; int i; if (e->e_group->g_current == e) { element_select_prev(); if (e->e_group->g_current == e) e->e_group->g_current = NULL; } list_for_each_entry_safe(c, cnext, &e->e_childs, e_list) element_free(c); list_for_each_entry_safe(info, ninfo, &e->e_info_list, i_list) { xfree(info->i_name); xfree(info->i_value); list_del(&info->i_list); xfree(info); } for (i = 0; i < ATTR_HASH_SIZE; i++) list_for_each_entry_safe(a, an, &e->e_attrhash[i], a_list) attr_free(a); list_del(&e->e_list); e->e_group->g_nelements--; xfree(e->e_name); xfree(e); } #if 0 if (item->i_group->g_selected == item) { if (item_select_prev() == END_OF_LIST) { if (group_select_prev() == END_OF_LIST) { if (node_select_prev() != END_OF_LIST) item_select_last(); } else item_select_last(); } } #endif #if 0 void item_delete(struct item *item) { int m; struct item *child; for (m = 0; m < ATTR_HASH_MAX; m++) { struct attr *a, *next; for (a = item->i_attrs[m]; a; a = next) { next = a->a_next; xfree(a); } } if (item->i_group->g_selected == item) { if (item_select_prev() == END_OF_LIST) { if (group_select_prev() == END_OF_LIST) { if (node_select_prev() != END_OF_LIST) item_select_last(); } else item_select_last(); } } unlink_item(item); item->i_group->g_nitems--; for (child = item->i_childs; child; child = child->i_next) item_delete(child); if (item->i_path) xfree(item->i_path); xfree(item); } #endif void element_reset_update_flag(struct element_group *g, struct element *e, void *arg) { DBG("Reseting update flag of %s", e->e_name); e->e_flags &= ~ELEMENT_FLAG_UPDATED; } /** * Needs to be called after updating all attributes of an element */ void element_notify_update(struct element *e, timestamp_t *ts) { struct attr *a; int i; e->e_flags |= ELEMENT_FLAG_UPDATED; if (ts == NULL) ts = &rtiming.rt_last_read; for (i = 0; i < ATTR_HASH_SIZE; i++) list_for_each_entry(a, &e->e_attrhash[i], a_list) attr_notify_update(a, ts); if (e->e_usage_attr && e->e_cfg && (a = attr_lookup(e, e->e_usage_attr->ad_id))) { attr_calc_usage(a, &e->e_rx_usage, &e->e_tx_usage, e->e_cfg->ec_rxmax, e->e_cfg->ec_txmax); } else { e->e_rx_usage = FLT_MAX; e->e_tx_usage = FLT_MAX; } } void element_lifesign(struct element *e, int n) { e->e_lifecycles = n * get_lifecycles(); } void element_check_if_dead(struct element_group *g, struct element *e, void *arg) { if (--(e->e_lifecycles) <= 0) { element_free(e); DBG("Deleting dead element %s", e->e_name); } } void element_foreach_attr(struct element *e, void (*cb)(struct element *e, struct attr *, void *), void *arg) { struct attr *a; list_for_each_entry(a, &e->e_attr_sorted, a_sort_list) cb(e, a, arg); } int element_set_key_attr(struct element *e, const char *major, const char * minor) { if (!(e->e_key_attr[GT_MAJOR] = attr_def_lookup(major))) return -ENOENT; if (!(e->e_key_attr[GT_MINOR] = attr_def_lookup(minor))) return -ENOENT; return 0; } int element_set_usage_attr(struct element *e, const char *usage) { if (!(e->e_usage_attr = attr_def_lookup(usage))) return -ENOENT; return 0; } struct element *element_current(void) { struct element_group *g; if (!(g = group_current())) return NULL; if (!g->g_current) element_select_first(); return g->g_current; } struct element *element_select_first(void) { struct element_group *g; if (!(g = group_current())) return NULL; if (list_empty(&g->g_elements)) g->g_current = NULL; else g->g_current = list_first_entry(&g->g_elements, struct element, e_list); return g->g_current; } struct element *element_select_last(void) { struct element_group *g; if (!(g = group_current())) return NULL; if (list_empty(&g->g_elements)) g->g_current = NULL; else { struct element *e; e = list_entry(g->g_elements.prev, struct element, e_list); while (!list_empty(&e->e_childs)) e = list_entry(e->e_childs.prev, struct element, e_list); g->g_current = e; } return g->g_current; } struct element *element_select_next(void) { struct element_group *g; struct element *e; if (!(g = group_current())) return NULL; if (!(e = g->g_current)) return element_select_first(); if (!list_empty(&e->e_childs)) e = list_first_entry(&e->e_childs, struct element, e_list); else { /* * move upwards until we have no parent or there is a next * entry in the list */ while (e->e_parent && e->e_list.next == &e->e_parent->e_childs) e = e->e_parent; if (!e->e_parent && e->e_list.next == &g->g_elements) { group_select_next(); return element_select_first(); } else e = list_entry(e->e_list.next, struct element, e_list); } g->g_current = e; return e; } struct element *element_select_prev(void) { struct element_group *g; struct element *e; if (!(g = group_current())) return NULL; if (!(e = g->g_current)) return element_select_last(); if (!e->e_parent && e->e_list.prev == &g->g_elements) { group_select_prev(); return element_select_last(); } if (e->e_parent && e->e_list.prev == &e->e_parent->e_childs) e = e->e_parent; else { e = list_entry(e->e_list.prev, struct element, e_list); while (!list_empty(&e->e_childs)) e = list_entry(e->e_childs.prev, struct element, e_list); } g->g_current = e; return e; } static struct info *element_info_lookup(struct element *e, const char *name) { struct info *i; list_for_each_entry(i, &e->e_info_list, i_list) if (!strcmp(i->i_name, name)) return i; return NULL; } void element_update_info(struct element *e, const char *name, const char *value) { struct info *i; if ((i = element_info_lookup(e, name))) { xfree(i->i_value); i->i_value = strdup(value); return; } DBG("Created element info %s (\"%s\")", name, value); i = xcalloc(1, sizeof(*i)); i->i_name = strdup(name); i->i_value = strdup(value); e->e_ninfo++; list_add_tail(&i->i_list, &e->e_info_list); } void element_set_txmax(struct element *e, uint64_t max) { char buf[32]; if (!e->e_cfg) e->e_cfg = element_cfg_create(e->e_name); if (e->e_cfg->ec_txmax != max) e->e_cfg->ec_txmax = max; unit_bit2str(e->e_cfg->ec_txmax * 8, buf, sizeof(buf)); element_update_info(e, "TxMax", buf); } void element_set_rxmax(struct element *e, uint64_t max) { char buf[32]; if (!e->e_cfg) e->e_cfg = element_cfg_create(e->e_name); if (e->e_cfg->ec_rxmax != max) e->e_cfg->ec_rxmax = max; unit_bit2str(e->e_cfg->ec_rxmax * 8, buf, sizeof(buf)); element_update_info(e, "RxMax", buf); }