/* * Parse the attributes component of a PCP connection string. * Optionally, an initial attribute:value pair can be passed * in as well to add to the parsed set. */ static int parseAttributeSpec( const char *spec, /* the original, complete string to parse */ char **position, /* parse from here onward and update at end */ int attribute, char *value, __pmHashCtl *attributes, char **errmsg) { char *s, *start, *v = NULL; char buffer[32]; /* must be large enough to hold largest attr name */ int buflen, attr, len, sts; if (attribute != PCP_ATTR_NONE) if ((sts = __pmHashAdd(attribute, (void *)value, attributes)) < 0) return sts; for (s = start = *position; s != NULL; s++) { /* parse: foo=bar&moo&goo=blah ... go! */ if (*s == '\0' || *s == '/' || *s == '&') { if ((*s == '\0' || *s == '/') && s == start) break; len = v ? (v - start - 1) : (s - start); buflen = (len < sizeof(buffer)-1) ? len : sizeof(buffer)-1; strncpy(buffer, start, buflen); buffer[buflen] = '\0'; attr = __pmLookupAttrKey(buffer, buflen+1); if (attr != PCP_ATTR_NONE) { char *val = NULL; if (v && (val = strndup(v, s - v)) == NULL) { sts = -ENOMEM; goto fail; } if ((sts = __pmHashAdd(attr, (void *)val, attributes)) < 0) { free(val); goto fail; } } v = NULL; if (*s == '\0' || *s == '/') break; start = s + 1; /* start of attribute name */ continue; } if (*s == '=') { v = s + 1; /* start of attribute value */ } } *position = s; return 0; fail: if (attribute != PCP_ATTR_NONE) /* avoid double free in caller */ __pmHashDel(attribute, (void *)value, attributes); __pmFreeAttrsSpec(attributes); return sts; }
/* Delete every instance of a given metric from the data structure. * The pmid is deleted from the pmidlist of every task containing an instance. * Return a pointer to the first pmDesc found (the only thing salvaged from the * smoking ruins), or nil if no instances were found. */ static pmDesc * del_insts(pmID pmid) { optreq_t *rqp; __pmHashNode *hp; task_t *tp; pmDesc *dp = NULL; int i, sts, keep; for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL; ) { /* Do that BEFORE we nuke the node */ __pmHashNode * nextnode = hp->next; if (pmid == (pmID)hp->key) { rqp = (optreq_t *)hp->data; tp = (task_t *)rqp->r_fetch->f_aux; if ((sts = __pmOptFetchDel(&tp->t_fetch, rqp)) < 0) die("del_insts: __pmOptFetchDel", sts); if ((sts = __pmHashDel(pmid, (void *)rqp, &pm_hash)) < 0) die("del_insts: __pmHashDel", sts); /* save the first pmDesc pointer for return and subsequent * re-use, but free all the others */ if (dp != NULL) free(rqp->r_desc); else dp = rqp->r_desc; if (rqp->r_numinst) free(rqp->r_instlist); free(rqp); /* remove pmid from the task's pmid list */ for (i = 0; i < tp->t_numpmid; i++) if (tp->t_pmidlist[i] == pmid) break; keep = (tp->t_numpmid - 1 - i) * sizeof(tp->t_pmidlist[0]); if (keep) { memmove(&tp->t_pmidlist[i], &tp->t_pmidlist[i+1], keep); memmove(&tp->t_desclist[i], &tp->t_desclist[i+1], keep); memmove(&tp->t_namelist[i], &tp->t_namelist[i+1], keep); } /* don't bother shrinking the pmidlist */ tp->t_numpmid--; if (tp->t_numpmid == 0) { /* TODO: nuke the task if that was the last pmID */ } } hp = nextnode; } return dp; }
static void undo(task_t *tp, optreq_t *rqp, int inst) { int j; int k; int sts; if (rqp->r_numinst >= 1) { /* remove instance from list of instance */ for (k =0, j = 0; j < rqp->r_numinst; j++) { if (rqp->r_instlist[j] != inst) rqp->r_instlist[k++] = rqp->r_instlist[j]; } rqp->r_numinst = k; if ((sts = __pmOptFetchDel(&tp->t_fetch, rqp)) < 0) die("undo: __pmOptFetchDel", sts); if (rqp->r_numinst == 0) { /* no more instances, remove specification */ if (tp->t_fetch == NULL) { /* no more specifications, remove task */ task_t *xtp; task_t *ltp = NULL; for (xtp = tasklist; xtp != NULL; xtp = xtp->t_next) { if (xtp == tp) { if (ltp == NULL) tasklist = tp->t_next; else ltp->t_next = tp->t_next; break; } ltp = xtp; } } __pmHashDel(rqp->r_desc->pmid, (void *)rqp, &pm_hash); free(rqp); } else /* re-insert modified specification */ __pmOptFetchAdd(&tp->t_fetch, rqp); } else { /* * TODO ... current specification is for all instances, * need to remove this instance from the set ... * this requires some enhancement to optFetch * * pro tem, this metric-instance pair may continue to get * logged, even though the logging state is recorded as * OFF (this is the worst thing that can happen here) */ } }
/* Delete an optreq_t from its task, free it and remove it from the hash list. */ static void del_optreq(optreq_t *rqp) { int sts; task_t *tp = (task_t *)rqp->r_fetch->f_aux; if ((sts = __pmOptFetchDel(&tp->t_fetch, rqp)) < 0) die("del_optreq: __pmOptFetchDel", sts); if ((sts = __pmHashDel(rqp->r_desc->pmid, (void *)rqp, &pm_hash)) < 0) die("del_optreq: __pmHashDel", sts); free(rqp->r_desc); if (rqp->r_numinst) free(rqp->r_instlist); free(rqp); /* TODO: remove pmid from task if that was the last optreq_t for it */ /* TODO: remove task if last pmid removed */ }
/* Update an existing metric (given a pmValueSet) adding it to the specified * task. Allocate and return a new task_t if the specified task pointer is nil. */ static int update_metric(pmValueSet *vsp, int reqstate, int mflags, task_t **result) { pmID pmid = vsp->pmid; task_t *ntp = *result; /* pointer to new task */ task_t *ctp; /* pointer to current task */ optreq_t *rqp; pmDesc *dp; int i, j, inst; int sts, need = 0; int addpmid = 0; int freedp; /* allocate a new task if null task pointer passed in */ if (ntp == NULL) { ntp = calloc(1, sizeof(task_t)); if (ntp == NULL) { __pmNoMem("update_metric: new task calloc", sizeof(task_t), PM_FATAL_ERR); } *result = ntp; } if ((mflags & MF_HAS_INDOM) == 0) { rqp = findoptreq(pmid, 0); ctp = (task_t *)(rqp->r_fetch->f_aux); if (!update_ok(ctp->t_state, reqstate)) return 1; /* if the new state is advisory off, just remove the metric */ if ((PMLC_GET_MAYBE(reqstate)) || (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) del_optreq(rqp); else { /* update the optreq. For single valued metrics there are no * instances involved so the sole optreq can just be re-used. */ if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0) die("update_metric: 1 metric __pmOptFetchDel", sts); __pmOptFetchAdd(&ntp->t_fetch, rqp); linkback(ntp); addpmid = 1; } } else { /* metric has an instance domain */ if (vsp->numval > 0) { /* tricky: since optFetch can't handle instance profiles of the * form "all except these specific instances", and managing it * manually is just too hard, reject requests for specific * metric instances if "all instances" of the metric are already * being logged. * Note: advisory off "all instances" is excepted since ANY request * overrides and advisory off. E.g. "advisory off all" followed by * "advisory on someinsts" turns on advisory logging for * "someinsts". mflags will be zero for "advisory off" metrics. */ if (mflags & MF_HAS_ALL) return 1; /* can't turn "all" into specific insts */ for (i = 0; i < vsp->numval; i++) { dp = NULL; freedp = 0; inst = vsp->vlist[i].inst; rqp = findoptreq(pmid, inst); if (rqp != NULL) { dp = rqp->r_desc; ctp = (task_t *)(rqp->r_fetch->f_aux); /* no work required if new task and current are the same */ if (ntp == ctp) continue; if (!update_ok(ctp->t_state, reqstate)) continue; /* remove inst's group from current task */ if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0) die("update_metric: instance add __pmOptFetchDel", sts); /* put group back if there are any instances left */ if (rqp->r_numinst > 1) { /* remove inst from group */ for (j = 0; j < rqp->r_numinst; j++) if (inst == rqp->r_instlist[j]) break; /* don't call memmove to move zero bytes */ if (j < rqp->r_numinst - 1) memmove(&rqp->r_instlist[j], &rqp->r_instlist[j+1], (rqp->r_numinst - 1 - j) * sizeof(rqp->r_instlist[0])); rqp->r_numinst--; /* (don't bother realloc-ing the instlist to a smaller size) */ __pmOptFetchAdd(&ctp->t_fetch, rqp); linkback(ctp); /* no need to update hash list, rqp already there */ } /* if that was the last instance, free the group */ else { if (( sts = __pmHashDel(pmid, (void *)rqp, &pm_hash)) < 0) die("update_metric: instance __pmHashDel", sts); freedp = 1; free(rqp->r_instlist); free(rqp); } } /* advisory off (mandatory maybe) metrics don't get put into * the data structure */ if (PMLC_GET_MAYBE(reqstate) || (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) { if (freedp) free(dp); continue; } addpmid = 1; /* try to find an existing optreq_t for the instance */ rqp = find_instoptreq(ntp, pmid); if (rqp != NULL) { if ((sts = __pmOptFetchDel(&ntp->t_fetch, rqp)) < 0) die("update_metric: instance add __pmOptFetchDel", sts); } /* no existing optreq_t found, allocate & populate a new one */ else { rqp = (optreq_t *)calloc(1, sizeof(optreq_t)); if (rqp == NULL) { __pmNoMem("update_metric: optreq calloc", sizeof(optreq_t), PM_FATAL_ERR); } /* if the metric existed but the instance didn't, we don't * have a valid pmDesc (dp), so find one. */ if (dp == NULL) { /* find metric and associated pmDesc */ __pmHashNode *hp; for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL; hp = hp->next) { if (pmid == (pmID)hp->key) break; } assert(hp != NULL); dp = ((optreq_t *)hp->data)->r_desc; } /* recycle pmDesc from the old group, if possible */ if (freedp) { rqp->r_desc = dp; freedp = 0; } /* otherwise allocate & copy a new pmDesc via dp */ else { need = sizeof(pmDesc); rqp->r_desc = (pmDesc *)malloc(need); if (rqp->r_desc == NULL) { __pmNoMem("update_metric: new inst pmDesc malloc", need, PM_FATAL_ERR); } memcpy(rqp->r_desc, dp, need); } if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0) die("update_metric: __pmHashAdd", sts); } need = (rqp->r_numinst + 1) * sizeof(rqp->r_instlist[0]); rqp->r_instlist = (int *)realloc(rqp->r_instlist, need); if (rqp->r_instlist == NULL) { __pmNoMem("update_metric: inst list resize", need, PM_FATAL_ERR); } rqp->r_instlist[rqp->r_numinst++] = inst; __pmOptFetchAdd(&ntp->t_fetch, rqp); linkback(ntp); if (freedp) free(dp); } } /* the vset has numval == 0, a request for "all instances" */ else { /* if the metric is a singular instance that has mandatory logging * or has at least one instance with mandatory logging on, a * request for advisory logging cannot be honoured */ if ((mflags & MF_HAS_MAND) && PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_MAYBE(reqstate) == 0) return 1; if (mflags & MF_HAS_ALL) { /* if there is an "all instances" for the metric, it will be * the only optreq_t for the metric */ rqp = findoptreq(pmid, 0); ctp = (task_t *)rqp->r_fetch->f_aux; /* if the metric is "advisory on, all instances" and the * request is for "mandatory maybe, all instances" the current * advisory logging state of the metric is retained */ if (PMLC_GET_MAND(ctp->t_state) == 0 && PMLC_GET_MAYBE(reqstate)) return 0; /* advisory off & mandatory maybe metrics don't get put into * the data structure */ if (PMLC_GET_MAYBE(reqstate) || (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) { del_optreq(rqp); return 0; } addpmid = 1; if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0) die("update_metric: all inst __pmOptFetchDel", sts); /* don't delete from hash list, rqp re-used */ __pmOptFetchAdd(&ntp->t_fetch, rqp); linkback(ntp); } else { /* there are one or more specific instances for the metric. * The metric cannot have an "all instances" at the same time. * * if the request is for "mandatory maybe, all instances" and * the only instances of the metric all have advisory logging * on, retain the current advisory semantics. */ if (PMLC_GET_MAYBE(reqstate) && (mflags & MF_HAS_INST) && !(mflags & MF_HAS_MAND)) return 0; dp = del_insts(pmid); /* advisory off (mandatory maybe) metrics don't get put into * the data structure */ if (PMLC_GET_MAYBE(reqstate) || (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) { free(dp); return 0; } addpmid = 1; rqp = (optreq_t *)calloc(1, sizeof(optreq_t)); if (rqp == NULL) { __pmNoMem("update_metric: all inst calloc", sizeof(optreq_t), PM_FATAL_ERR); } rqp->r_desc = dp; __pmOptFetchAdd(&ntp->t_fetch, rqp); linkback(ntp); if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0) die("update_metric: all inst __pmHashAdd", sts); } } } if (!addpmid) return 0; /* add pmid to new task if not already there */ for (i = 0; i < ntp->t_numpmid; i++) if (pmid == ntp->t_pmidlist[i]) break; if (i >= ntp->t_numpmid) { pmDesc desc; char *name; int need; if ((sts = pmLookupDesc(pmid, &desc)) < 0) die("update_metric: cannot lookup desc", sts); if ((sts = pmNameID(pmid, &name)) < 0) die("update_metric: cannot lookup name", sts); need = (ntp->t_numpmid + 1) * sizeof(pmID); if (!(ntp->t_pmidlist = (pmID *)realloc(ntp->t_pmidlist, need))) __pmNoMem("update_metric: grow task pmidlist", need, PM_FATAL_ERR); need = (ntp->t_numpmid + 1) * sizeof(char *); if (!(ntp->t_namelist = (char **)realloc(ntp->t_namelist, need))) __pmNoMem("update_metric: grow task namelist", need, PM_FATAL_ERR); need = (ntp->t_numpmid + 1) * sizeof(pmDesc); if (!(ntp->t_desclist = (pmDesc *)realloc(ntp->t_desclist, need))) __pmNoMem("update_metric: grow task desclist", need, PM_FATAL_ERR); i = ntp->t_numpmid; ntp->t_pmidlist[i] = pmid; ntp->t_namelist[i] = name; ntp->t_desclist[i] = desc; ntp->t_numpmid++; } return 0; }