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; }
/* * Service a request from the pmlogger client. * Return non-zero if the client has closed the connection. */ int client_req(void) { int sts; __pmPDU *pb; __pmPDUHdr *php; int pinpdu; if ((pinpdu = sts = __pmGetPDU(clientfd, ANY_SIZE, TIMEOUT_DEFAULT, &pb)) <= 0) { if (sts != 0) fprintf(stderr, "client_req: %s\n", pmErrStr(sts)); return 1; } if (qa_case == QA_SLEEPY) { /* error injection - delay before processing and responding */ sleep(5); } php = (__pmPDUHdr *)pb; sts = 0; switch (php->type) { case PDU_CREDS: /* version 2 PDU */ sts = do_creds(pb); break; case PDU_LOG_REQUEST: /* version 2 PDU */ sts = do_request(pb); break; case PDU_LOG_CONTROL: /* version 2 PDU */ sts = do_control(pb); break; default: /* unknown PDU */ fprintf(stderr, "client_req: bad PDU type 0x%x\n", php->type); sts = PM_ERR_IPC; break; } if (pinpdu > 0) __pmUnpinPDUBuf(pb); if (sts >= 0) return 0; else { /* the client isn't playing by the rules */ __pmSendError(clientfd, FROM_ANON, sts); 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; }
/* * client connects to pmcd handshake */ static int __pmConnectHandshake(int fd, const char *hostname, int ctxflags, __pmHashCtl *attrs) { __pmPDU *pb; int ok; int version; int challenge; int sts; int pinpdu; /* Expect an error PDU back from PMCD: ACK/NACK for connection */ pinpdu = sts = __pmGetPDU(fd, ANY_SIZE, TIMEOUT_DEFAULT, &pb); if (sts == PDU_ERROR) { /* * See comments in pmcd ... we actually get an extended error PDU * from pmcd, of the form * * :----------:-----------: * | status | challenge | * :----------:-----------: * * For a good connection, status is 0, else a PCP error code; * challenge contains server-side info (e.g. enabled features) */ version = __pmDecodeXtendError(pb, &sts, &challenge); if (version < 0) { __pmUnpinPDUBuf(pb); return version; } if (sts < 0) { __pmUnpinPDUBuf(pb); return sts; } if (version == PDU_VERSION2) { __pmPDUInfo pduinfo; __pmVersionCred handshake; int pduflags = 0; pduinfo = __ntohpmPDUInfo(*(__pmPDUInfo *)&challenge); if (pduinfo.features & PDU_FLAG_CREDS_REQD) /* * This is a mandatory connection feature - pmcd must be * sent user credential information one way or another - * i.e. via SASL2 authentication, or AF_UNIX peer creds. */ pduflags |= PDU_FLAG_CREDS_REQD; if (ctxflags) { /* * If an optional connection feature (e.g. encryption) is * desired, the pmcd that we're talking to must advertise * support for the feature. And if it did, the client in * turn must request it be enabled (now, via pduflags). */ if (ctxflags & (PM_CTXFLAG_SECURE|PM_CTXFLAG_RELAXED)) { if (pduinfo.features & PDU_FLAG_SECURE) { pduflags |= PDU_FLAG_SECURE; /* * Determine whether the server can send an ACK for a * secure connection request. We can still connect * whether it does or not, but we need to know the * protocol. */ if (pduinfo.features & PDU_FLAG_SECURE_ACK) pduflags |= PDU_FLAG_SECURE_ACK; } else if (ctxflags & PM_CTXFLAG_SECURE) { __pmUnpinPDUBuf(pb); return -EOPNOTSUPP; } } if (ctxflags & PM_CTXFLAG_COMPRESS) { if (pduinfo.features & PDU_FLAG_COMPRESS) pduflags |= PDU_FLAG_COMPRESS; else { __pmUnpinPDUBuf(pb); return -EOPNOTSUPP; } } if (ctxflags & PM_CTXFLAG_AUTH) { if (pduinfo.features & PDU_FLAG_AUTH) pduflags |= PDU_FLAG_AUTH; else { __pmUnpinPDUBuf(pb); return -EOPNOTSUPP; } } } /* * Negotiate connection version and features (via creds PDU) */ if ((ok = __pmSetVersionIPC(fd, version)) < 0) { __pmUnpinPDUBuf(pb); return ok; } memset(&handshake, 0, sizeof(handshake)); handshake.c_type = CVERSION; handshake.c_version = PDU_VERSION; handshake.c_flags = pduflags; sts = __pmSendCreds(fd, (int)getpid(), 1, (__pmCred *)&handshake); /* * At this point we know caller wants to set channel options and * pmcd supports them so go ahead and update the socket now (this * completes the SSL handshake in encrypting mode, authentication * via SASL, and/or enabling compression in NSS). */ if (sts >= 0 && pduflags) sts = __pmSecureClientHandshake(fd, pduflags, hostname, attrs); } else sts = PM_ERR_IPC; } else if (sts != PM_ERR_TIMEOUT) sts = PM_ERR_IPC; if (pinpdu > 0) __pmUnpinPDUBuf(pb); return sts; }
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; }
int main(int argc, char **argv) { int c; int sts; int sep = __pmPathSeparator(); int use_localtime = 0; int isdaemon = 0; char *pmnsfile = PM_NS_DEFAULT; char *username; char *logfile = "pmlogger.log"; /* default log (not archive) file name */ char *endnum; int i; task_t *tp; optcost_t ocp; __pmFdSet readyfds; char *p; char *runtime = NULL; int ctx; /* handle corresponding to ctxp below */ __pmContext *ctxp; /* pmlogger has just this one context */ int niter; pid_t target_pid = 0; __pmGetUsername(&username); /* * Warning: * If any of the pmlogger options change, make sure the * corresponding changes are made to pmnewlog when pmlogger * options are passed through from the control file */ while ((c = pmgetopt_r(argc, argv, &opts)) != EOF) { switch (c) { case 'c': /* config file */ if (access(opts.optarg, F_OK) == 0) configfile = opts.optarg; else { /* does not exist as given, try the standard place */ char *sysconf = pmGetConfig("PCP_VAR_DIR"); int sz = strlen(sysconf)+strlen("/config/pmlogger/")+strlen(opts.optarg)+1; if ((configfile = (char *)malloc(sz)) == NULL) __pmNoMem("config file name", sz, PM_FATAL_ERR); snprintf(configfile, sz, "%s%c" "config%c" "pmlogger%c" "%s", sysconf, sep, sep, sep, opts.optarg); if (access(configfile, F_OK) != 0) { /* still no good, error handling happens below */ free(configfile); configfile = opts.optarg; } } break; case 'D': /* debug flag */ sts = __pmParseDebug(opts.optarg); if (sts < 0) { pmprintf("%s: unrecognized debug flag specification (%s)\n", pmProgname, opts.optarg); opts.errors++; } else pmDebug |= sts; break; case 'h': /* hostname for PMCD to contact */ pmcd_host_conn = opts.optarg; break; case 'l': /* log file name */ logfile = opts.optarg; break; case 'L': /* linger if not primary logger */ linger = 1; break; case 'm': /* note for port map file */ note = opts.optarg; isdaemon = ((strcmp(note, "pmlogger_check") == 0) || (strcmp(note, "pmlogger_daily") == 0)); break; case 'n': /* alternative name space file */ pmnsfile = opts.optarg; break; case 'p': target_pid = (int)strtol(opts.optarg, &endnum, 10); if (*endnum != '\0') { pmprintf("%s: invalid process identifier (%s)\n", pmProgname, opts.optarg); opts.errors++; } else if (!__pmProcessExists(target_pid)) { pmprintf("%s: PID error - no such process (%d)\n", pmProgname, target_pid); opts.errors++; } break; case 'P': /* this is the primary pmlogger */ primary = 1; isdaemon = 1; break; case 'r': /* report sizes of pmResult records */ rflag = 1; break; case 's': /* exit size */ sts = ParseSize(opts.optarg, &exit_samples, &exit_bytes, &exit_time); if (sts < 0) { pmprintf("%s: illegal size argument '%s' for exit size\n", pmProgname, opts.optarg); opts.errors++; } else if (exit_time.tv_sec > 0) { __pmAFregister(&exit_time, NULL, run_done_callback); } break; case 'T': /* end time */ runtime = opts.optarg; break; case 't': /* change default logging interval */ if (pmParseInterval(opts.optarg, &delta, &p) < 0) { pmprintf("%s: illegal -t argument\n%s", pmProgname, p); free(p); opts.errors++; } break; case 'U': /* run as named user */ username = opts.optarg; isdaemon = 1; break; case 'u': /* flush output buffers after each fetch */ /* * all archive write I/O is unbuffered now, so maintain -u * for backwards compatibility only */ break; case 'v': /* volume switch after given size */ sts = ParseSize(opts.optarg, &vol_switch_samples, &vol_switch_bytes, &vol_switch_time); if (sts < 0) { pmprintf("%s: illegal size argument '%s' for volume size\n", pmProgname, opts.optarg); opts.errors++; } else if (vol_switch_time.tv_sec > 0) { vol_switch_afid = __pmAFregister(&vol_switch_time, NULL, vol_switch_callback); } break; case 'V': archive_version = (int)strtol(opts.optarg, &endnum, 10); if (*endnum != '\0' || archive_version != PM_LOG_VERS02) { pmprintf("%s: -V requires a version number of %d\n", pmProgname, PM_LOG_VERS02); opts.errors++; } break; case 'x': /* recording session control fd */ rsc_fd = (int)strtol(opts.optarg, &endnum, 10); if (*endnum != '\0' || rsc_fd < 0) { pmprintf("%s: -x requires a non-negative numeric argument\n", pmProgname); opts.errors++; } else { time(&rsc_start); } break; case 'y': use_localtime = 1; break; case '?': default: opts.errors++; break; } } if (primary && pmcd_host != NULL) { pmprintf( "%s: -P and -h are mutually exclusive; use -P only when running\n" "%s on the same (local) host as the PMCD to which it connects.\n", pmProgname, pmProgname); opts.errors++; } if (!opts.errors && opts.optind != argc - 1) { pmprintf("%s: insufficient arguments\n", pmProgname); opts.errors++; } if (opts.errors) { pmUsageMessage(&opts); exit(1); } if (rsc_fd != -1 && note == NULL) { /* add default note to indicate running with -x */ static char xnote[10]; snprintf(xnote, sizeof(xnote), "-x %d", rsc_fd); note = xnote; } /* if we are running as a daemon, change user early */ if (isdaemon) __pmSetProcessIdentity(username); __pmOpenLog("pmlogger", logfile, stderr, &sts); if (sts != 1) { fprintf(stderr, "%s: Warning: log file (%s) creation failed\n", pmProgname, logfile); /* continue on ... writing to stderr */ } /* base name for archive is here ... */ archBase = argv[opts.optind]; if (pmcd_host_conn == NULL) pmcd_host_conn = "local:"; /* initialise access control */ if (__pmAccAddOp(PM_OP_LOG_ADV) < 0 || __pmAccAddOp(PM_OP_LOG_MAND) < 0 || __pmAccAddOp(PM_OP_LOG_ENQ) < 0) { fprintf(stderr, "%s: access control initialisation failed\n", pmProgname); exit(1); } if (pmnsfile != PM_NS_DEFAULT) { if ((sts = pmLoadASCIINameSpace(pmnsfile, 1)) < 0) { fprintf(stderr, "%s: Cannot load namespace from \"%s\": %s\n", pmProgname, pmnsfile, pmErrStr(sts)); exit(1); } } if ((ctx = pmNewContext(PM_CONTEXT_HOST, pmcd_host_conn)) < 0) { fprintf(stderr, "%s: Cannot connect to PMCD on host \"%s\": %s\n", pmProgname, pmcd_host_conn, pmErrStr(ctx)); exit(1); } pmcd_host = (char *)pmGetContextHostName(ctx); if (strlen(pmcd_host) == 0) { fprintf(stderr, "%s: pmGetContextHostName(%d) failed\n", pmProgname, ctx); exit(1); } if (rsc_fd == -1) { /* no -x, so register client id with pmcd */ __pmSetClientIdArgv(argc, argv); } /* * discover fd for comms channel to PMCD ... */ if ((ctxp = __pmHandleToPtr(ctx)) == NULL) { fprintf(stderr, "%s: botch: __pmHandleToPtr(%d) returns NULL!\n", pmProgname, ctx); exit(1); } pmcdfd = ctxp->c_pmcd->pc_fd; PM_UNLOCK(ctxp->c_lock); if (configfile != NULL) { if ((yyin = fopen(configfile, "r")) == NULL) { fprintf(stderr, "%s: Cannot open config file \"%s\": %s\n", pmProgname, configfile, osstrerror()); exit(1); } } else { /* **ANY** Lex would read from stdin automagically */ configfile = "<stdin>"; } __pmOptFetchGetParams(&ocp); ocp.c_scope = 1; __pmOptFetchPutParams(&ocp); /* prevent early timer events ... */ __pmAFblock(); if (yyparse() != 0) exit(1); if (configfile != NULL) fclose(yyin); yyend(); #ifdef PCP_DEBUG fprintf(stderr, "Config parsed\n"); #endif fprintf(stderr, "Starting %slogger for host \"%s\" via \"%s\"\n", primary ? "primary " : "", pmcd_host, pmcd_host_conn); #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_LOG) { fprintf(stderr, "optFetch Cost Parameters: pmid=%d indom=%d fetch=%d scope=%d\n", ocp.c_pmid, ocp.c_indom, ocp.c_fetch, ocp.c_scope); fprintf(stderr, "\nAfter loading config ...\n"); for (tp = tasklist; tp != NULL; tp = tp->t_next) { if (tp->t_numvalid == 0) continue; fprintf(stderr, " state: %sin log, %savail, %s, %s", PMLC_GET_INLOG(tp->t_state) ? "" : "not ", PMLC_GET_AVAIL(tp->t_state) ? "" : "un", PMLC_GET_MAND(tp->t_state) ? "mand" : "adv", PMLC_GET_ON(tp->t_state) ? "on" : "off"); fprintf(stderr, " delta: %ld usec", (long)1000 * tp->t_delta.tv_sec + tp->t_delta.tv_usec); fprintf(stderr, " numpmid: %d\n", tp->t_numpmid); for (i = 0; i < tp->t_numpmid; i++) { fprintf(stderr, " %s (%s):\n", pmIDStr(tp->t_pmidlist[i]), tp->t_namelist[i]); } __pmOptFetchDump(stderr, tp->t_fetch); } } #endif if (!primary && tasklist == NULL && !linger) { fprintf(stderr, "Nothing to log, and not the primary logger instance ... good-bye\n"); exit(1); } if ((sts = __pmLogCreate(pmcd_host, archBase, archive_version, &logctl)) < 0) { fprintf(stderr, "__pmLogCreate: %s\n", pmErrStr(sts)); exit(1); } else { /* * try and establish $TZ from the remote PMCD ... * Note the label record has been set up, but not written yet */ char *name = "pmcd.timezone"; pmID pmid; pmResult *resp; __pmtimevalNow(&epoch); sts = pmUseContext(ctx); if (sts >= 0) sts = pmLookupName(1, &name, &pmid); if (sts >= 0) sts = pmFetch(1, &pmid, &resp); if (sts >= 0) { if (resp->vset[0]->numval > 0) { /* pmcd.timezone present */ strcpy(logctl.l_label.ill_tz, resp->vset[0]->vlist[0].value.pval->vbuf); /* prefer to use remote time to avoid clock drift problems */ epoch = resp->timestamp; /* struct assignment */ if (! use_localtime) pmNewZone(logctl.l_label.ill_tz); } #ifdef PCP_DEBUG else if (pmDebug & DBG_TRACE_LOG) { fprintf(stderr, "main: Could not get timezone from host %s\n", pmcd_host); } #endif pmFreeResult(resp); } } /* do ParseTimeWindow stuff for -T */ if (runtime) { struct timeval res_end; /* time window end */ struct timeval start; struct timeval end; struct timeval last_delta; char *err_msg; /* parsing error message */ time_t now; struct timeval now_tv; time(&now); now_tv.tv_sec = now; now_tv.tv_usec = 0; start = now_tv; end.tv_sec = INT_MAX; end.tv_usec = INT_MAX; sts = __pmParseTime(runtime, &start, &end, &res_end, &err_msg); if (sts < 0) { fprintf(stderr, "%s: illegal -T argument\n%s", pmProgname, err_msg); exit(1); } last_delta = res_end; tsub(&last_delta, &now_tv); __pmAFregister(&last_delta, NULL, run_done_callback); last_stamp = res_end; } fprintf(stderr, "Archive basename: %s\n", archBase); #ifndef IS_MINGW /* detach yourself from the launching process */ if (isdaemon) setpgid(getpid(), 0); #endif /* set up control port */ init_ports(); __pmFD_ZERO(&fds); for (i = 0; i < CFD_NUM; ++i) { if (ctlfds[i] >= 0) __pmFD_SET(ctlfds[i], &fds); } #ifndef IS_MINGW __pmFD_SET(pmcdfd, &fds); #endif if (rsc_fd != -1) __pmFD_SET(rsc_fd, &fds); numfds = maxfd() + 1; if ((sts = do_preamble()) < 0) fprintf(stderr, "Warning: problem writing archive preamble: %s\n", pmErrStr(sts)); sts = 0; /* default exit status */ parse_done = 1; /* enable callback processing */ __pmAFunblock(); for ( ; ; ) { int nready; #ifdef PCP_DEBUG if ((pmDebug & DBG_TRACE_APPL2) && (pmDebug & DBG_TRACE_DESPERATE)) { fprintf(stderr, "before __pmSelectRead(%d,...): run_done_alarm=%d vol_switch_alarm=%d log_alarm=%d\n", numfds, run_done_alarm, vol_switch_alarm, log_alarm); } #endif niter = 0; while (log_alarm && niter++ < 10) { __pmAFblock(); log_alarm = 0; #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_APPL2) fprintf(stderr, "delayed callback: log_alarm\n"); #endif for (tp = tasklist; tp != NULL; tp = tp->t_next) { if (tp->t_alarm) { tp->t_alarm = 0; do_work(tp); } } __pmAFunblock(); } if (vol_switch_alarm) { __pmAFblock(); vol_switch_alarm = 0; #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_APPL2) fprintf(stderr, "delayed callback: vol_switch_alarm\n"); #endif newvolume(VOL_SW_TIME); __pmAFunblock(); } if (run_done_alarm) { #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_APPL2) fprintf(stderr, "delayed callback: run_done_alarm\n"); #endif run_done(0, NULL); /*NOTREACHED*/ } __pmFD_COPY(&readyfds, &fds); nready = __pmSelectRead(numfds, &readyfds, NULL); #ifdef PCP_DEBUG if ((pmDebug & DBG_TRACE_APPL2) && (pmDebug & DBG_TRACE_DESPERATE)) { fprintf(stderr, "__pmSelectRead(%d,...) done: nready=%d run_done_alarm=%d vol_switch_alarm=%d log_alarm=%d\n", numfds, nready, run_done_alarm, vol_switch_alarm, log_alarm); } #endif __pmAFblock(); if (nready > 0) { /* handle request on control port */ for (i = 0; i < CFD_NUM; ++i) { if (ctlfds[i] >= 0 && __pmFD_ISSET(ctlfds[i], &readyfds)) { if (control_req(ctlfds[i])) { /* new client has connected */ __pmFD_SET(clientfd, &fds); if (clientfd >= numfds) numfds = clientfd + 1; } } } if (clientfd >= 0 && __pmFD_ISSET(clientfd, &readyfds)) { /* process request from client, save clientfd in case client * closes connection, resetting clientfd to -1 */ int fd = clientfd; if (client_req()) { /* client closed connection */ __pmFD_CLR(fd, &fds); __pmCloseSocket(clientfd); clientfd = -1; numfds = maxfd() + 1; qa_case = 0; } } #ifndef IS_MINGW if (pmcdfd >= 0 && __pmFD_ISSET(pmcdfd, &readyfds)) { /* * do not expect this, given synchronous commumication with the * pmcd ... either pmcd has terminated, or bogus PDU ... or its * Win32 and we are operating under the different conditions of * our AF.c implementation there, which has to deal with a lack * of signal support on Windows - race condition exists between * this check and the async event timer callback. */ __pmPDU *pb; __pmPDUHdr *php; sts = __pmGetPDU(pmcdfd, ANY_SIZE, TIMEOUT_NEVER, &pb); if (sts <= 0) { if (sts < 0) fprintf(stderr, "Error: __pmGetPDU: %s\n", pmErrStr(sts)); disconnect(sts); } else { php = (__pmPDUHdr *)pb; fprintf(stderr, "Error: Unsolicited %s PDU from PMCD\n", __pmPDUTypeStr(php->type)); disconnect(PM_ERR_IPC); } if (sts > 0) __pmUnpinPDUBuf(pb); } #endif if (rsc_fd >= 0 && __pmFD_ISSET(rsc_fd, &readyfds)) { /* * some action on the recording session control fd * end-of-file means launcher has quit, otherwise we * expect one of these commands * V<number>\n - version * F<folio>\n - folio name * P<name>\n - launcher's name * R\n - launcher can replay * D\n - detach from launcher * Q\n - quit pmlogger */ char rsc_buf[MAXPATHLEN]; char *rp = rsc_buf; char myc; int fake_x = 0; for (rp = rsc_buf; ; rp++) { if (read(rsc_fd, &myc, 1) <= 0) { #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_APPL2) fprintf(stderr, "recording session control: eof\n"); #endif if (rp != rsc_buf) { *rp = '\0'; fprintf(stderr, "Error: incomplete recording session control message: \"%s\"\n", rsc_buf); } fake_x = 1; break; } if (rp >= &rsc_buf[MAXPATHLEN]) { fprintf(stderr, "Error: absurd recording session control message: \"%100.100s ...\"\n", rsc_buf); fake_x = 1; break; } if (myc == '\n') { *rp = '\0'; break; } *rp = myc; } #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_APPL2) { if (fake_x == 0) fprintf(stderr, "recording session control: \"%s\"\n", rsc_buf); } #endif if (fake_x) do_dialog('X'); else if (strcmp(rsc_buf, "Q") == 0 || strcmp(rsc_buf, "D") == 0 || strcmp(rsc_buf, "?") == 0) do_dialog(rsc_buf[0]); else if (rsc_buf[0] == 'F') folio_name = strdup(&rsc_buf[1]); else if (rsc_buf[0] == 'P') rsc_prog = strdup(&rsc_buf[1]); else if (strcmp(rsc_buf, "R") == 0) rsc_replay = 1; else if (rsc_buf[0] == 'V' && rsc_buf[1] == '0') { /* * version 0 of the recording session control ... * this is all we grok at the moment */ ; } else { fprintf(stderr, "Error: illegal recording session control message: \"%s\"\n", rsc_buf); do_dialog('X'); } } } else if (vol_switch_flag) { newvolume(VOL_SW_SIGHUP); vol_switch_flag = 0; } else if (nready < 0 && neterror() != EINTR) fprintf(stderr, "Error: select: %s\n", netstrerror()); __pmAFunblock(); if (target_pid && !__pmProcessExists(target_pid)) exit(EXIT_SUCCESS); if (exit_code) break; } exit(exit_code); }
/* Determine which clients (if any) have sent data to the server and handle it * as required. */ void HandleInput(__pmFdSet *fdsPtr) { int i, sts; __pmPDU *pb; ClientInfo *cp; /* input from clients */ for (i = 0; i < nClients; i++) { if (!client[i].status.connected || !__pmFD_ISSET(client[i].fd, fdsPtr)) continue; cp = &client[i]; sts = __pmGetPDU(cp->fd, LIMIT_SIZE, 0, &pb); if (sts <= 0) { CleanupClient(cp, sts); continue; } /* We *must* see a credentials PDU as the first PDU */ if (!cp->status.allowed) { sts = VerifyClient(cp, pb); __pmUnpinPDUBuf(pb); if (sts < 0) { CleanupClient(cp, sts); continue; } cp->status.allowed = 1; continue; } sts = __pmXmitPDU(cp->pmcd_fd, pb); __pmUnpinPDUBuf(pb); if (sts <= 0) { CleanupClient(cp, sts); continue; } } /* input from pmcds */ for (i = 0; i < nClients; i++) { if (!client[i].status.connected || !__pmFD_ISSET(client[i].pmcd_fd, fdsPtr)) continue; cp = &client[i]; sts = __pmGetPDU(cp->pmcd_fd, ANY_SIZE, 0, &pb); if (sts <= 0) { CleanupClient(cp, sts); continue; } sts = __pmXmitPDU(cp->fd, pb); __pmUnpinPDUBuf(pb); if (sts <= 0) { CleanupClient(cp, sts); continue; } } }
/* 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; }
/* * Determine which clients (if any) have sent data to the server and handle it * as required. */ void HandleClientInput(__pmFdSet *fdsPtr) { int sts; int i; __pmPDU *pb; __pmPDUHdr *php; ClientInfo *cp; for (i = 0; i < nClients; i++) { int pinpdu; if (!client[i].status.connected || !__pmFD_ISSET(client[i].fd, fdsPtr)) continue; cp = &client[i]; this_client_id = i; pinpdu = sts = __pmGetPDU(cp->fd, LIMIT_SIZE, _pmcd_timeout, &pb); if (sts > 0) { pmcd_trace(TR_RECV_PDU, cp->fd, sts, (int)((__psint_t)pb & 0xffffffff)); } else { CleanupClient(cp, sts); continue; } php = (__pmPDUHdr *)pb; if (__pmVersionIPC(cp->fd) == UNKNOWN_VERSION && php->type != PDU_CREDS) { /* old V1 client protocol, no longer supported */ sts = PM_ERR_IPC; CleanupClient(cp, sts); __pmUnpinPDUBuf(pb); continue; } if (pmDebug & DBG_TRACE_APPL0) ShowClients(stderr); switch (php->type) { case PDU_PROFILE: sts = (cp->denyOps & PMCD_OP_FETCH) ? PM_ERR_PERMISSION : DoProfile(cp, pb); break; case PDU_FETCH: sts = (cp->denyOps & PMCD_OP_FETCH) ? PM_ERR_PERMISSION : DoFetch(cp, pb); break; case PDU_INSTANCE_REQ: sts = (cp->denyOps & PMCD_OP_FETCH) ? PM_ERR_PERMISSION : DoInstance(cp, pb); break; case PDU_DESC_REQ: sts = (cp->denyOps & PMCD_OP_FETCH) ? PM_ERR_PERMISSION : DoDesc(cp, pb); break; case PDU_TEXT_REQ: sts = (cp->denyOps & PMCD_OP_FETCH) ? PM_ERR_PERMISSION : DoText(cp, pb); break; case PDU_RESULT: sts = (cp->denyOps & PMCD_OP_STORE) ? PM_ERR_PERMISSION : DoStore(cp, pb); break; case PDU_PMNS_IDS: sts = (cp->denyOps & PMCD_OP_FETCH) ? PM_ERR_PERMISSION : DoPMNSIDs(cp, pb); break; case PDU_PMNS_NAMES: sts = (cp->denyOps & PMCD_OP_FETCH) ? PM_ERR_PERMISSION : DoPMNSNames(cp, pb); break; case PDU_PMNS_CHILD: sts = (cp->denyOps & PMCD_OP_FETCH) ? PM_ERR_PERMISSION : DoPMNSChild(cp, pb); break; case PDU_PMNS_TRAVERSE: sts = (cp->denyOps & PMCD_OP_FETCH) ? PM_ERR_PERMISSION : DoPMNSTraverse(cp, pb); break; case PDU_CREDS: sts = DoCreds(cp, pb); break; default: sts = PM_ERR_IPC; } if (sts < 0) { if (pmDebug & DBG_TRACE_APPL0) fprintf(stderr, "PDU: %s client[%d]: %s\n", __pmPDUTypeStr(php->type), i, pmErrStr(sts)); /* Make sure client still alive before sending. */ if (cp->status.connected) { pmcd_trace(TR_XMIT_PDU, cp->fd, PDU_ERROR, sts); sts = __pmSendError(cp->fd, FROM_ANON, sts); if (sts < 0) __pmNotifyErr(LOG_ERR, "HandleClientInput: " "error sending Error PDU to client[%d] %s\n", i, pmErrStr(sts)); } } if (pinpdu > 0) __pmUnpinPDUBuf(pb); } }