static int _pmtracereconnect(void) { #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_NOAGENT) { fprintf(stderr, "_pmtracereconnect: reconnect attempt (skipped)\n"); return 0; } else if (__pmstate & PMTRACE_STATE_COMMS) { fprintf(stderr, "_pmtracereconnect: attempting PMDA reconnection\n"); } #endif if (_pmtimedout && time(NULL) < _pmttimeout) { /* too soon to retry */ #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_COMMS) fprintf(stderr, "_pmtracereconnect: too soon to retry " "(%d seconds remain)\n", (int)(_pmttimeout - time(NULL))); #endif return -ETIMEDOUT; } if (__pmfd >= 0) { __pmtracenomoreinput(__pmfd); __pmCloseSocket(__pmfd); __pmfd = -1; } if (_pmtraceconnect(1) < 0) { #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_COMMS) fprintf(stderr, "_pmtracereconnect: failed to reconnect\n"); #endif _pmtraceupdatewait(); return -ETIMEDOUT; } else { #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_COMMS) fprintf(stderr, "_pmtracereconnect: reconnect succeeded!\n"); #endif _pmtimedout = 0; } return 0; }
int __pmtracegetPDU(int fd, int timeout, __pmTracePDU **result) { int need, len; char *handle; static int maxsize = TRACE_PDU_CHUNK; __pmTracePDU *pdubuf; __pmTracePDU *pdubuf_prev; __pmTracePDUHdr *php; /* * This stuff is a little tricky. What we try to do is read() * an amount of data equal to the largest PDU we have (or are * likely to have) seen thus far. In the majority of cases * this returns exactly one PDU's worth, i.e. read() returns * a length equal to php->len. * * For this to work, we have a special "mode" of -1 * to pduread() which means read, but return after the * first read(), rather than trying to read up to the request * length with multiple read()s, which would of course "hang" * after the first PDU arrived. * * We need to handle the following tricky cases: * 1. We get _more_ than we need for a single PDU -- happens * when PDU's arrive together. This requires "moreinput" * to handle leftovers here (it gets even uglier if we * have part, but not all of the second PDU). * 2. We get _less_ than we need for a single PDU -- this * requires at least another read(), and possibly acquiring * another pdubuf and doing a memcpy() for the partial PDU * from the earlier call. */ if (__pmtracemoreinput(fd)) { /* some leftover from last time ... handle -> start of PDU */ pdubuf = more[fd].pdubuf; len = more[fd].len; __pmtracenomoreinput(fd); } else { if ((pdubuf = __pmtracefindPDUbuf(maxsize)) == NULL) return -oserror(); len = pduread(fd, (void *)pdubuf, maxsize, -1, timeout); } php = (__pmTracePDUHdr *)pdubuf; if (len < (int)sizeof(__pmTracePDUHdr)) { if (len == -1) { if (oserror() == ECONNRESET|| oserror() == ETIMEDOUT || oserror() == ENETDOWN || oserror() == ENETUNREACH || oserror() == EHOSTDOWN || oserror() == EHOSTUNREACH || oserror() == ECONNREFUSED) /* * failed as a result of pmdatrace exiting and the * connection being reset, or as a result of the kernel * ripping down the connection (most likely because the * host at the other end just took a dive) * * treat this like end of file on input * * from irix/kern/fs/nfs/bds.c seems like all of the * following are peers here: * ECONNRESET (pmdatrace terminated?) * ETIMEDOUT ENETDOWN ENETUNREACH EHOSTDOWN EHOSTUNREACH * ECONNREFUSED * peers for bds but not here: * ENETRESET ENONET ESHUTDOWN (cache_fs only?) * ECONNABORTED (accept, user req only?) * ENOTCONN (udp?) * EPIPE EAGAIN (nfs, bds & ..., but not ip or tcp?) */ len = 0; else fprintf(stderr, "__pmtracegetPDU: fd=%d hdr: %s", fd, osstrerror()); } else if (len > 0) fprintf(stderr, "__pmtracegetPDU: fd=%d hdr: len=%d, not %d?", fd, len, (int)sizeof(__pmTracePDUHdr)); else if (len == PMTRACE_ERR_TIMEOUT) return PMTRACE_ERR_TIMEOUT; else if (len < 0) fprintf(stderr, "__pmtracegetPDU: fd=%d hdr: %s", fd, pmtraceerrstr(len)); return len ? PMTRACE_ERR_IPC : 0; } php->len = ntohl(php->len); if (php->len < 0) { fprintf(stderr, "__pmtracegetPDU: fd=%d illegal len=%d in hdr\n", fd, php->len); return PMTRACE_ERR_IPC; } if (len == php->len) /* return below */ ; else if (len > php->len) { /* * read more than we need for this one, save it up for next time */ handle = (char *)pdubuf; moreinput(fd, (__pmTracePDU *)&handle[php->len], len - php->len); } else { int tmpsize; /* * need to read more ... */ __pmtracepinPDUbuf(pdubuf); pdubuf_prev = pdubuf; if (php->len > maxsize) tmpsize = TRACE_PDU_CHUNK * ( 1 + php->len / TRACE_PDU_CHUNK); else tmpsize = maxsize; if ((pdubuf = __pmtracefindPDUbuf(tmpsize)) == NULL) { __pmtraceunpinPDUbuf(pdubuf_prev); return -oserror(); } if (php->len > maxsize) maxsize = tmpsize; memmove((void *)pdubuf, (void *)php, len); __pmtraceunpinPDUbuf(pdubuf_prev); php = (__pmTracePDUHdr *)pdubuf; need = php->len - len; handle = (char *)pdubuf; /* block until all of the PDU is received this time */ len = pduread(fd, (void *)&handle[len], need, 0, timeout); if (len != need) { if (len == PMTRACE_ERR_TIMEOUT) return PMTRACE_ERR_TIMEOUT; if (len < 0) fprintf(stderr, "__pmtracegetPDU: error (%d) fd=%d: %s\n", (int)oserror(), fd, osstrerror()); else fprintf(stderr, "__pmtracegetPDU: len=%d, not %d? (fd=%d)\n", len, need, fd); fprintf(stderr, "hdr: len=0x%08x type=0x%08x from=0x%08x\n", php->len, (int)ntohl(php->type), (int)ntohl(php->from)); return PMTRACE_ERR_IPC; } } *result = (__pmTracePDU *)php; php->type = ntohl(php->type); php->from = ntohl(php->from); #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_PDU) { int j; int jend = (int)(php->len+(int)sizeof(__pmTracePDU)-1)/(int)sizeof(__pmTracePDU); char *p; /* for Purify ... */ p = (char *)*result + php->len; while (p < (char *)*result + jend*sizeof(__pmTracePDU)) *p++ = '~'; /* buffer end */ fprintf(stderr, "[%" FMT_PID "]__pmtracegetPDU: %s fd=%d len=%d from=%d", (pid_t)getpid(), pdutypestr(php->type), fd, php->len, php->from); for (j = 0; j < jend; j++) { if ((j % 8) == 0) fprintf(stderr, "\n%03d: ", j); fprintf(stderr, "%8x ", (*result)[j]); } putc('\n', stderr); } #endif return php->type; }