int __pmRecvLabel(int fd, __pmContext *ctxp, int timeout, int *ident, int *type, pmLabelSet **sets, int *nsets) { __pmPDU *pb; int oident = *ident; int otype = *type; int pinpdu; int sts; sts = pinpdu = __pmGetPDU(fd, ANY_SIZE, timeout, &pb); if (sts == PDU_LABEL) { sts = __pmDecodeLabel(pb, ident, type, sets, nsets); if (sts >= 0) { /* verify response is for a matching request */ if (oident != *ident || otype != *type) sts = PM_ERR_IPC; } } else if (sts == PDU_ERROR) __pmDecodeError(pb, &sts); else if (sts != PM_ERR_TIMEOUT) sts = PM_ERR_IPC; if (pinpdu > 0) __pmUnpinPDUBuf(pb); return sts; }
static int ping_pmcd(int ctx) { /* * We're going to leveraging an existing host context, just make sure * pmcd is still alive at the other end ... we don't have a "ping" * pdu, but sending a pmDesc request for PM_ID_NULL is pretty much * the same thing ... expect a PM_ERR_PMID error PDU back. * The code here is based on pmLookupDesc() with some short cuts * because we know it is a host context and we already hold the * __pmLock_libpcp lock */ __pmContext *ctxp = contexts[ctx]; int sts; PM_LOCK(ctxp->c_pmcd->pc_lock); if ((sts = __pmSendDescReq(ctxp->c_pmcd->pc_fd, ctx, PM_ID_NULL)) >= 0) { int pinpdu; __pmPDU *pb; pinpdu = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, ctxp->c_pmcd->pc_tout_sec, &pb); if (pinpdu == PDU_ERROR) __pmDecodeError(pb, &sts); if (pinpdu > 0) __pmUnpinPDUBuf(pb); } PM_UNLOCK(ctxp->c_pmcd->pc_lock); if (sts != PM_ERR_PMID) { /* pmcd is not well on this context ... */ return 0; } return 1; }
static int __pmAuthServerNegotiation(int fd, int ssf, __pmHashCtl *attrs) { int sts, saslsts; int pinned, length, count; char *payload, *offset; sasl_conn_t *sasl_conn; __pmPDU *pb; if (pmDebugOptions.auth) fprintf(stderr, "__pmAuthServerNegotiation(fd=%d, ssf=%d)\n", fd, ssf); if ((sasl_conn = (sasl_conn_t *)__pmGetUserAuthData(fd)) == NULL) return -EINVAL; /* setup all the security properties for this connection */ if ((sts = __pmAuthServerSetProperties(sasl_conn, ssf)) < 0) return sts; saslsts = sasl_listmech(sasl_conn, NULL, NULL, " ", NULL, (const char **)&payload, (unsigned int *)&length, &count); if (saslsts != SASL_OK && saslsts != SASL_CONTINUE) { pmNotifyErr(LOG_ERR, "Generating client mechanism list: %s", sasl_errstring(saslsts, NULL, NULL)); return __pmSecureSocketsError(saslsts); } if (pmDebugOptions.auth) fprintf(stderr, "__pmAuthServerNegotiation - sending mechanism list " "(%d items, %d bytes): \"%s\"\n", count, length, payload); if ((sts = __pmSendAuth(fd, FROM_ANON, 0, payload, length)) < 0) return sts; if (pmDebugOptions.auth) fprintf(stderr, "__pmAuthServerNegotiation - wait for mechanism\n"); sts = pinned = __pmGetPDU(fd, ANY_SIZE, TIMEOUT_DEFAULT, &pb); if (sts == PDU_AUTH) { sts = __pmDecodeAuth(pb, &count, &payload, &length); if (sts >= 0) { for (count = 0; count < length; count++) { if (payload[count] == '\0') break; } if (count < length) { /* found an initial response */ length = length - count - 1; offset = payload + count + 1; } else { length = 0; offset = NULL; } saslsts = sasl_server_start(sasl_conn, payload, offset, length, (const char **)&payload, (unsigned int *)&length); if (saslsts != SASL_OK && saslsts != SASL_CONTINUE) { sts = __pmSecureSocketsError(saslsts); if (pmDebugOptions.auth) fprintf(stderr, "sasl_server_start failed: %d (%s)\n", saslsts, pmErrStr(sts)); } else { if (pmDebugOptions.auth) fprintf(stderr, "sasl_server_start success: sts=%s\n", saslsts == SASL_CONTINUE ? "continue" : "ok"); } } } else if (sts == PDU_ERROR) __pmDecodeError(pb, &sts); else if (sts != PM_ERR_TIMEOUT) sts = PM_ERR_IPC; if (pinned > 0) __pmUnpinPDUBuf(pb); if (sts < 0) return sts; if (pmDebugOptions.auth) fprintf(stderr, "__pmAuthServerNegotiation method negotiated\n"); while (saslsts == SASL_CONTINUE) { if (!payload) { pmNotifyErr(LOG_ERR, "No SASL data to send"); sts = -EINVAL; break; } if ((sts = __pmSendAuth(fd, FROM_ANON, 0, payload, length)) < 0) break; if (pmDebugOptions.auth) fprintf(stderr, "__pmAuthServerNegotiation awaiting response\n"); sts = pinned = __pmGetPDU(fd, ANY_SIZE, TIMEOUT_DEFAULT, &pb); if (sts == PDU_AUTH) { sts = __pmDecodeAuth(pb, &count, &payload, &length); if (sts >= 0) { sts = saslsts = sasl_server_step(sasl_conn, payload, length, (const char **)&payload, (unsigned int *)&length); if (sts != SASL_OK && sts != SASL_CONTINUE) { sts = __pmSecureSocketsError(sts); break; } if (pmDebugOptions.auth) { fprintf(stderr, "__pmAuthServerNegotiation" " step recv (%d bytes)\n", length); } } } else if (sts == PDU_ERROR) __pmDecodeError(pb, &sts); else if (sts != PM_ERR_TIMEOUT) sts = PM_ERR_IPC; if (pinned > 0) __pmUnpinPDUBuf(pb); if (sts < 0) break; } if (sts < 0) { if (pmDebugOptions.auth) fprintf(stderr, "__pmAuthServerNegotiation loop failed: %d\n", sts); return sts; } return __pmAuthServerSetAttributes(sasl_conn, attrs); }
int pmFetch(int numpmid, pmID pmidlist[], pmResult **result) { int n; if (numpmid < 1) { n = PM_ERR_TOOSMALL; goto done; } if ((n = pmWhichContext()) >= 0) { __pmContext *ctxp = __pmHandleToPtr(n); int newcnt; pmID *newlist = NULL; int have_dm; if (ctxp == NULL) { n = PM_ERR_NOCONTEXT; goto done; } if (ctxp->c_type == PM_CONTEXT_LOCAL && PM_MULTIPLE_THREADS(PM_SCOPE_DSO_PMDA)) { /* Local context requires single-threaded applications */ n = PM_ERR_THREAD; PM_UNLOCK(ctxp->c_lock); goto done; } /* for derived metrics, may need to rewrite the pmidlist */ have_dm = newcnt = __pmPrepareFetch(ctxp, numpmid, pmidlist, &newlist); if (newcnt > numpmid) { /* replace args passed into pmFetch */ numpmid = newcnt; pmidlist = newlist; } if (ctxp->c_type == PM_CONTEXT_HOST) { /* * Thread-safe note * * Need to be careful here, because the PMCD changed protocol * may mean several PDUs are returned, but __pmDecodeResult() * may request more info from PMCD if pmDebug is set. * * So unlock ctxp->c_pmcd->pc_lock as soon as possible. */ PM_LOCK(ctxp->c_pmcd->pc_lock); if ((n = request_fetch(n, ctxp, numpmid, pmidlist)) >= 0) { int changed = 0; do { __pmPDU *pb; int pinpdu; pinpdu = n = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, ctxp->c_pmcd->pc_tout_sec, &pb); if (n == PDU_RESULT) { PM_UNLOCK(ctxp->c_pmcd->pc_lock); n = __pmDecodeResult(pb, result); } else if (n == PDU_ERROR) { __pmDecodeError(pb, &n); if (n > 0) /* PMCD state change protocol */ changed = n; else PM_UNLOCK(ctxp->c_pmcd->pc_lock); } else { PM_UNLOCK(ctxp->c_pmcd->pc_lock); if (n != PM_ERR_TIMEOUT) n = PM_ERR_IPC; } if (pinpdu > 0) __pmUnpinPDUBuf(pb); } while (n > 0); if (n == 0) n |= changed; } else PM_UNLOCK(ctxp->c_pmcd->pc_lock); } else if (ctxp->c_type == PM_CONTEXT_LOCAL) { n = __pmFetchLocal(ctxp, numpmid, pmidlist, result); } else { /* assume PM_CONTEXT_ARCHIVE */ n = __pmLogFetch(ctxp, numpmid, pmidlist, result); if (n >= 0 && (ctxp->c_mode & __PM_MODE_MASK) != PM_MODE_INTERP) { ctxp->c_origin.tv_sec = (__int32_t)(*result)->timestamp.tv_sec; ctxp->c_origin.tv_usec = (__int32_t)(*result)->timestamp.tv_usec; } } /* process derived metrics, if any */ if (have_dm) { __pmFinishResult(ctxp, n, result); if (newlist != NULL) free(newlist); } PM_UNLOCK(ctxp->c_lock); } done: #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_FETCH) { fprintf(stderr, "pmFetch returns ...\n"); if (n > 0) { fprintf(stderr, "PMCD state changes: agent(s)"); if (n & PMCD_ADD_AGENT) fprintf(stderr, " added"); if (n & PMCD_RESTART_AGENT) fprintf(stderr, " restarted"); if (n & PMCD_DROP_AGENT) fprintf(stderr, " dropped"); fputc('\n', stderr); } if (n >= 0) __pmDumpResult(stderr, *result); else { char errmsg[PM_MAXERRMSGLEN]; fprintf(stderr, "Error: %s\n", pmErrStr_r(n, errmsg, sizeof(errmsg))); } } #endif return n; }
int DoFetch(ClientInfo *cip, __pmPDU* pb) { int i, j; int sts; int ctxnum; __pmTimeval when; int nPmids; pmID *pmidList; static pmResult *endResult = NULL; static int maxnpmids = 0; /* sizes endResult */ DomPmidList *dList; /* NOTE: NOT indexed by agent index */ static int nDoms = 0; static pmResult **results = NULL; static int *resIndex = NULL; __pmFdSet waitFds; __pmFdSet readyFds; int nWait; int maxFd; struct timeval timeout; if (nAgents > nDoms) { if (results != NULL) free(results); if (resIndex != NULL) free(resIndex); results = (pmResult **)malloc((nAgents + 1) * sizeof (pmResult *)); resIndex = (int *)malloc((nAgents + 1) * sizeof(int)); if (results == NULL || resIndex == NULL) { __pmNoMem("DoFetch.results", (nAgents + 1) * sizeof (pmResult *) + (nAgents + 1) * sizeof(int), PM_FATAL_ERR); } nDoms = nAgents; } memset(results, 0, (nAgents + 1) * sizeof(results[0])); sts = __pmDecodeFetch(pb, &ctxnum, &when, &nPmids, &pmidList); if (sts < 0) return sts; /* Check that a profile has been received from the specified context */ if (ctxnum < 0 || ctxnum >= cip->szProfile || cip->profile[ctxnum] == NULL) { __pmUnpinPDUBuf(pb); if (ctxnum < 0 || ctxnum >= cip->szProfile) __pmNotifyErr(LOG_ERR, "DoFetch: bad ctxnum=%d\n", ctxnum); else __pmNotifyErr(LOG_ERR, "DoFetch: no profile for ctxnum=%d\n", ctxnum); return PM_ERR_NOPROFILE; } if (nPmids > maxnpmids) { int need; if (endResult != NULL) free(endResult); need = (int)sizeof(pmResult) + (nPmids - 1) * (int)sizeof(pmValueSet *); if ((endResult = (pmResult *)malloc(need)) == NULL) { __pmNoMem("DoFetch.endResult", need, PM_FATAL_ERR); } maxnpmids = nPmids; } dList = SplitPmidList(nPmids, pmidList); /* For each domain in the split pmidList, dispatch the per-domain subset * of pmIDs to the appropriate agent. For DSO agents, the pmResult will * come back immediately. If a request cannot be sent to an agent, a * suitable pmResult (containing metric not available values) will be * returned. */ __pmFD_ZERO(&waitFds); nWait = 0; maxFd = -1; for (i = 0; dList[i].domain != -1; i++) { j = mapdom[dList[i].domain]; results[j] = SendFetch(&dList[i], &agent[j], cip, ctxnum); if (results[j] == NULL) { /* Wait for agent's response */ int fd = agent[j].outFd; agent[j].status.busy = 1; __pmFD_SET(fd, &waitFds); if (fd > maxFd) maxFd = fd; nWait++; } } /* Construct pmResult for bad-pmID list */ if (dList[i].listSize != 0) results[nAgents] = MakeBadResult(dList[i].listSize, dList[i].list, PM_ERR_NOAGENT); /* Wait for results to roll in from agents */ while (nWait > 0) { __pmFD_COPY(&readyFds, &waitFds); if (nWait > 1) { timeout.tv_sec = _pmcd_timeout; timeout.tv_usec = 0; retry: setoserror(0); sts = __pmSelectRead(maxFd+1, &readyFds, &timeout); if (sts == 0) { __pmNotifyErr(LOG_INFO, "DoFetch: select timeout"); /* Timeout, terminate agents with undelivered results */ for (i = 0; i < nAgents; i++) { if (agent[i].status.busy) { /* Find entry in dList for this agent */ for (j = 0; dList[j].domain != -1; j++) if (dList[j].domain == agent[i].pmDomainId) break; results[i] = MakeBadResult(dList[j].listSize, dList[j].list, PM_ERR_NOAGENT); pmcd_trace(TR_RECV_TIMEOUT, agent[i].outFd, PDU_RESULT, 0); CleanupAgent(&agent[i], AT_COMM, agent[i].inFd); } } break; } else if (sts < 0) { if (neterror() == EINTR) goto retry; /* this is not expected to happen! */ __pmNotifyErr(LOG_ERR, "DoFetch: fatal select failure: %s\n", netstrerror()); Shutdown(); exit(1); } } /* Read results from agents that have them ready */ for (i = 0; i < nAgents; i++) { AgentInfo *ap = &agent[i]; int pinpdu; if (!ap->status.busy || !__pmFD_ISSET(ap->outFd, &readyFds)) continue; ap->status.busy = 0; __pmFD_CLR(ap->outFd, &waitFds); nWait--; pinpdu = sts = __pmGetPDU(ap->outFd, ANY_SIZE, _pmcd_timeout, &pb); if (sts > 0) pmcd_trace(TR_RECV_PDU, ap->outFd, sts, (int)((__psint_t)pb & 0xffffffff)); if (sts == PDU_RESULT) { if ((sts = __pmDecodeResult(pb, &results[i])) >= 0) if (results[i]->numpmid != aFreq[i]) { pmFreeResult(results[i]); sts = PM_ERR_IPC; #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_APPL0) __pmNotifyErr(LOG_ERR, "DoFetch: \"%s\" agent given %d pmIDs, returned %d\n", ap->pmDomainLabel, aFreq[i], results[i]->numpmid); #endif } } else { if (sts == PDU_ERROR) { int s; if ((s = __pmDecodeError(pb, &sts)) < 0) sts = s; else if (sts >= 0) sts = PM_ERR_GENERIC; pmcd_trace(TR_RECV_ERR, ap->outFd, PDU_RESULT, sts); } else if (sts >= 0) { pmcd_trace(TR_WRONG_PDU, ap->outFd, PDU_RESULT, sts); sts = PM_ERR_IPC; } } if (pinpdu > 0) __pmUnpinPDUBuf(pb); if (sts < 0) { /* Find entry in dList for this agent */ for (j = 0; dList[j].domain != -1; j++) if (dList[j].domain == agent[i].pmDomainId) break; results[i] = MakeBadResult(dList[j].listSize, dList[j].list, sts); if (sts == PM_ERR_PMDANOTREADY) { /* the agent is indicating it can't handle PDUs for now */ int k; extern int CheckError(AgentInfo *ap, int sts); for (k = 0; k < dList[j].listSize; k++) results[i]->vset[k]->numval = PM_ERR_AGAIN; sts = CheckError(&agent[i], sts); } #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_APPL0) { fprintf(stderr, "RESULT error from \"%s\" agent : %s\n", ap->pmDomainLabel, pmErrStr(sts)); } #endif if (sts == PM_ERR_IPC || sts == PM_ERR_TIMEOUT) CleanupAgent(ap, AT_COMM, ap->outFd); } } } endResult->numpmid = nPmids; __pmtimevalNow(&endResult->timestamp); /* The order of the pmIDs in the per-domain results is the same as in the * original request, but on a per-domain basis. resIndex is an array of * indices (one per agent) of the next metric to be retrieved from each * per-domain result's vset. */ memset(resIndex, 0, (nAgents + 1) * sizeof(resIndex[0])); for (i = 0; i < nPmids; i++) { j = mapdom[((__pmID_int *)&pmidList[i])->domain]; endResult->vset[i] = results[j]->vset[resIndex[j]++]; } pmcd_trace(TR_XMIT_PDU, cip->fd, PDU_RESULT, endResult->numpmid); sts = 0; if (cip->status.changes) { /* notify client of PMCD state change */ sts = __pmSendError(cip->fd, FROM_ANON, (int)cip->status.changes); if (sts > 0) sts = 0; cip->status.changes = 0; } if (sts == 0) sts = __pmSendResult(cip->fd, FROM_ANON, endResult); if (sts < 0) { pmcd_trace(TR_XMIT_ERR, cip->fd, PDU_RESULT, sts); CleanupClient(cip, sts); } /* * pmFreeResult() all the accumulated results. */ for (i = 0; dList[i].domain != -1; i++) { j = mapdom[dList[i].domain]; if (agent[j].ipcType == AGENT_DSO && agent[j].status.connected && !agent[j].status.madeDsoResult) /* Living DSO's manage their own pmResult skeleton unless * MakeBadResult was called to create the result. The value sets * within the skeleton need to be freed though! */ __pmFreeResultValues(results[j]); else /* For others it is dynamically allocated in __pmDecodeResult or * MakeBadResult */ pmFreeResult(results[j]); } if (results[nAgents] != NULL) pmFreeResult(results[nAgents]); __pmUnpinPDUBuf(pmidList); return 0; }
void service_client(__pmPDU *pb) { int n; int i; int j; pmDesc desc; pmDesc foundDesc; pmResult *resp; pmValueSet *vsp; __pmPDUHdr *ph = (__pmPDUHdr *)pb; switch (ph->type) { case PDU_DESC: if ((n = __pmDecodeDesc(pb, &desc)) < 0) { fprintf(stderr, "service_client: __pmDecodeDesc failed: %s\n", pmErrStr(n)); exit(1); } if (desc.indom != PM_INDOM_NULL) { fprintf(stderr, "service_client: Warning: ignored desc for pmid=%s: indom is not singular\n", pmIDStr(desc.pmid)); return; } if (summary_desc(desc.pmid, &foundDesc, NULL) == 0) { /* already in table */ fprintf(stderr, "service_client: Warning: duplicate desc for pmid=%s\n", pmIDStr(desc.pmid)); return; } nmeta++; if ((meta = (meta_t *)realloc(meta, nmeta * sizeof(meta_t))) == NULL) { __pmNoMem("service_client: meta realloc", nmeta * sizeof(meta_t), PM_FATAL_ERR); } memcpy(&meta[nmeta-1].desc, &desc, sizeof(pmDesc)); break; case PDU_RESULT: if ((n = __pmDecodeResult(pb, &resp)) < 0) { fprintf(stderr, "service_client: __pmDecodeResult failed: %s\n", pmErrStr(n)); exit(1); } if (cachedResult == NULL) { int need; need = (int)sizeof(pmResult) - (int)sizeof(pmValueSet *); if ((cachedResult = (pmResult *)malloc(need)) == NULL) { __pmNoMem("service_client: result malloc", need, PM_FATAL_ERR); } cachedResult->numpmid = 0; } /* * swap values from resp with those in cachedResult, expanding * cachedResult if there are metrics we've not seen before */ for (i = 0; i < resp->numpmid; i++) { for (j = 0; j < cachedResult->numpmid; j++) { if (resp->vset[i]->pmid == cachedResult->vset[j]->pmid) { /* found matching PMID, update this value */ break; } } if (j == cachedResult->numpmid) { /* new PMID, expand cachedResult and initialize vset */ int need; cachedResult->numpmid++; need = (int)sizeof(pmResult) + (cachedResult->numpmid-1) * (int)sizeof(pmValueSet *); if ((cachedResult = (pmResult *)realloc(cachedResult, need)) == NULL) { __pmNoMem("service_client: result realloc", need, PM_FATAL_ERR); } if ((cachedResult->vset[j] = (pmValueSet *)malloc(sizeof(pmValueSet))) == NULL) { __pmNoMem("service_client: vset[]", sizeof(pmValueSet), PM_FATAL_ERR); } cachedResult->vset[j]->pmid = resp->vset[i]->pmid; cachedResult->vset[j]->numval = 0; } /* * swap vsets */ vsp = cachedResult->vset[j]; cachedResult->vset[j] = resp->vset[i]; resp->vset[i] = vsp; } pmFreeResult(resp); break; case PDU_ERROR: if ((n = __pmDecodeError(pb, &i)) < 0) { fprintf(stderr, "service_client: __pmDecodeError failed: %s\n", pmErrStr(n)); exit(1); } fprintf(stderr, "service_client: Error PDU! %s\n", pmErrStr(i)); break; default: fprintf(stderr, "service_client: Bogus PDU type %d\n", ph->type); exit(1); } }
/* Process I/O on file descriptors from agents that were marked as not ready * to handle PDUs. */ static int HandleReadyAgents(__pmFdSet *readyFds) { int i, s, sts; int fd; int reason; int ready = 0; AgentInfo *ap; __pmPDU *pb; for (i = 0; i < nAgents; i++) { ap = &agent[i]; if (ap->status.notReady) { fd = ap->outFd; if (__pmFD_ISSET(fd, readyFds)) { int pinpdu; /* Expect an error PDU containing PM_ERR_PMDAREADY */ reason = AT_COMM; /* most errors are protocol failures */ pinpdu = sts = __pmGetPDU(ap->outFd, ANY_SIZE, _pmcd_timeout, &pb); if (sts > 0) pmcd_trace(TR_RECV_PDU, ap->outFd, sts, (int)((__psint_t)pb & 0xffffffff)); if (sts == PDU_ERROR) { s = __pmDecodeError(pb, &sts); if (s < 0) { sts = s; pmcd_trace(TR_RECV_ERR, ap->outFd, PDU_ERROR, sts); } else { /* sts is the status code from the error PDU */ if (pmDebug & DBG_TRACE_APPL0) __pmNotifyErr(LOG_INFO, "%s agent (not ready) sent %s status(%d)\n", ap->pmDomainLabel, sts == PM_ERR_PMDAREADY ? "ready" : "unknown", sts); if (sts == PM_ERR_PMDAREADY) { ap->status.notReady = 0; sts = 1; ready++; } else { pmcd_trace(TR_RECV_ERR, ap->outFd, PDU_ERROR, sts); sts = PM_ERR_IPC; } } } else { if (sts < 0) pmcd_trace(TR_RECV_ERR, ap->outFd, PDU_RESULT, sts); else pmcd_trace(TR_WRONG_PDU, ap->outFd, PDU_ERROR, sts); sts = PM_ERR_IPC; /* Wrong PDU type */ } if (pinpdu > 0) __pmUnpinPDUBuf(pb); if (ap->ipcType != AGENT_DSO && sts <= 0) CleanupAgent(ap, reason, fd); } } } return ready; }