static int http_client_connectto(const char *host, int port, struct timeval *timeout) { struct timeval stv, *ptv; __pmHostEnt *servInfo; __pmSockAddr *myAddr; __pmFdSet readyFds, allFds; void *enumIx; int fdFlags[FD_SETSIZE]; int i, fd, sts, maxFd; if ((servInfo = __pmGetAddrInfo(host)) == NULL) { if (pmDebug & DBG_TRACE_HTTP) fprintf(stderr, "HTTP connect(%s, %d): hosterror=%d, ``%s''\n", host, port, hosterror(), hoststrerror()); return -EHOSTUNREACH; } /* * We want to respect the connect timeout that has been configured, but we * may have more than one address to try. Do this by creating a socket for * each address and then using __pmSelectWrite() to wait for one of them to * respond. That way, the timeout is applied to all of the addresses * simultaneously. First, create the sockets, add them to the fd set and * try to establish connectections. */ __pmFD_ZERO(&allFds); maxFd = -1; enumIx = NULL; for (myAddr = __pmHostEntGetSockAddr(servInfo, &enumIx); myAddr != NULL; myAddr = __pmHostEntGetSockAddr(servInfo, &enumIx)) { /* Create a socket */ if (__pmSockAddrIsInet(myAddr)) fd = __pmCreateSocket(); else if (__pmSockAddrIsIPv6(myAddr)) fd = __pmCreateIPv6Socket(); else { if (pmDebug & DBG_TRACE_HTTP) fprintf(stderr, "HTTP connect(%s, %d): bad address family %d\n", host, port, __pmSockAddrGetFamily(myAddr)); fd = -EINVAL; } if (fd < 0) { __pmSockAddrFree(myAddr); continue; /* Try the next address */ } /* Attempt to connect */ fdFlags[fd] = __pmConnectTo(fd, myAddr, port); __pmSockAddrFree(myAddr); if (fdFlags[fd] < 0) { /* * Mark failure in case we fall out the end of the loop * and try next address */ __pmCloseSocket(fd); continue; } /* Add it to the fd set. */ __pmFD_SET(fd, &allFds); if (fd > maxFd) maxFd = fd; } __pmHostEntFree(servInfo); /* If we were unable to open any sockets, then give up. */ if (maxFd == -1) return -ECONNREFUSED; /* FNDELAY and we're in progress - wait on select */ __pmFD_COPY(&readyFds, &allFds); stv = *timeout; ptv = (stv.tv_sec || stv.tv_usec) ? &stv : NULL; sts = __pmSelectWrite(maxFd+1, &readyFds, ptv); /* Figure out what happened. */ if (sts == 0) fd = -ETIMEDOUT; else if (sts < 0) fd = -neterror(); else { /* Scan fd set, find first successfully connected socket (if any). */ fd = -EINVAL; for (i = 0; i <= maxFd; ++i) { if (__pmFD_ISSET(i, &readyFds)) { /* Successful connection? */ sts = __pmConnectCheckError(i); if (sts == 0) { fd = i; break; } fd = -sts; } } } /* Clean up the unused fds. */ for (i = 0; i <= maxFd; ++i) { if (i != fd && __pmFD_ISSET(i, &allFds)) __pmCloseSocket(i); } if (fd < 0) return fd; /* * If we're here, it means we have a valid connection; restore the * flags and make sure this file descriptor is closed if exec() is * called */ return __pmConnectRestoreFlags(fd, fdFlags[fd]); }
static int _pmauxtraceconnect(void) { int port = TRACE_PORT; char hostname[MAXHOSTNAMELEN]; struct timeval timeout = { 3, 0 }; /* default 3 secs */ __pmSockAddr *myaddr; __pmHostEnt *servinfo; void *enumIx; #ifndef IS_MINGW struct itimerval _pmolditimer; void (*old_handler)(int foo); #endif int rc, sts; int flags = 0; char *sptr, *endptr, *endnum; struct timeval canwait = { 5, 000000 }; struct timeval stv; struct timeval *pstv; __pmFdSet wfds; #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_NOAGENT) { fprintf(stderr, "_pmtraceconnect: connecting to PMDA (skipped)\n"); return 0; } else if (__pmstate & PMTRACE_STATE_COMMS) fprintf(stderr, "_pmtraceconnect: connecting to PMDA ...\n"); #endif /* * get optional stuff from environment ... * PCP_TRACE_HOST, PCP_TRACE_PORT, PCP_TRACE_TIMEOUT, and * PCP_TRACE_NOAGENT */ if ((sptr = getenv(TRACE_ENV_HOST)) != NULL) strcpy(hostname, sptr); else { (void)gethostname(hostname, MAXHOSTNAMELEN); hostname[MAXHOSTNAMELEN-1] = '\0'; } if ((sptr = getenv(TRACE_ENV_PORT)) != NULL) { port = (int)strtol(sptr, &endnum, 0); if (*endnum != '\0' || port < 0) { fprintf(stderr, "trace warning: bad PCP_TRACE_PORT ignored."); port = TRACE_PORT; } } if ((sptr = getenv(TRACE_ENV_TIMEOUT)) != NULL) { double timesec = strtod(sptr, &endptr); if (*endptr != '\0' || timesec < 0.0) fprintf(stderr, "trace warning: bogus PCP_TRACE_TIMEOUT."); else { timeout.tv_sec = (time_t)timesec; timeout.tv_usec = (int)((timesec - (double)timeout.tv_sec)*1000000); } } if (getenv(TRACE_ENV_NOAGENT) != NULL) __pmstate |= PMTRACE_STATE_NOAGENT; if ((servinfo = __pmGetAddrInfo(hostname)) == NULL) { #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_COMMS) fprintf(stderr, "_pmtraceconnect(__pmGetAddrInfo(hostname=%s): " "hosterror=%d, ``%s''\n", hostname, hosterror(), hoststrerror()); #endif return -EHOSTUNREACH; } /* Try each address in turn until one connects. */ sts = EHOSTUNREACH; __pmfd = -1; enumIx = NULL; for (myaddr = __pmHostEntGetSockAddr(servinfo, &enumIx); myaddr != NULL; myaddr = __pmHostEntGetSockAddr(servinfo, &enumIx)) { /* Create a socket */ if (__pmSockAddrIsInet(myaddr)) __pmfd = __pmCreateSocket(); else if (__pmSockAddrIsIPv6(myaddr)) __pmfd = __pmCreateIPv6Socket(); else { fprintf(stderr, "_pmtraceconnect(invalid address family): %d\n", __pmSockAddrGetFamily(myaddr)); } if (__pmfd < 0) { sts = neterror(); __pmSockAddrFree(myaddr); continue; /* Try the next address */ } /* Set the port. */ __pmSockAddrSetPort(myaddr, port); #ifndef IS_MINGW /* arm interval timer */ _pmmyitimer.it_value.tv_sec = timeout.tv_sec; _pmmyitimer.it_value.tv_usec = timeout.tv_usec; _pmmyitimer.it_interval.tv_sec = 0; _pmmyitimer.it_interval.tv_usec = 0; old_handler = signal(SIGALRM, _pmtracealarm); setitimer(ITIMER_REAL, &_pmmyitimer, &_pmolditimer); #endif #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_COMMS) { char *name = __pmHostEntGetName (servinfo); fprintf(stderr, "_pmtraceconnect: PMDA host=%s port=%d timeout=%d" "secs\n", name == NULL ? "unknown" : name, port, (int)timeout.tv_sec); if (name != NULL) free(name); } #endif /* Attempt to connect */ flags = __pmConnectTo(__pmfd, myaddr, port); __pmSockAddrFree(myaddr); if (flags < 0) { /* * Mark failure in case we fall out the end of the loop * and try next address. __pmfd has been closed in __pmConnectTo(). */ sts = -flags; __pmfd = -1; continue; } /* FNDELAY and we're in progress - wait on select */ stv = canwait; pstv = (stv.tv_sec || stv.tv_usec) ? &stv : NULL; __pmFD_ZERO(&wfds); __pmFD_SET(__pmfd, &wfds); if ((rc = __pmSelectWrite(__pmfd+1, &wfds, pstv)) == 1) { sts = __pmConnectCheckError(__pmfd); } else if (rc == 0) { sts = ETIMEDOUT; } else { sts = (rc < 0) ? neterror() : EINVAL; } #ifndef IS_MINGW /* re-arm interval timer */ setitimer(ITIMER_REAL, &off_itimer, &_pmmyitimer); signal(SIGALRM, old_handler); if (_pmolditimer.it_value.tv_sec != 0 && _pmolditimer.it_value.tv_usec != 0) { _pmolditimer.it_value.tv_usec -= timeout.tv_usec - _pmmyitimer.it_value.tv_usec; while (_pmolditimer.it_value.tv_usec < 0) { _pmolditimer.it_value.tv_usec += 1000000; _pmolditimer.it_value.tv_sec--; } while (_pmolditimer.it_value.tv_usec > 1000000) { _pmolditimer.it_value.tv_usec -= 1000000; _pmolditimer.it_value.tv_sec++; } _pmolditimer.it_value.tv_sec -= timeout.tv_sec - _pmmyitimer.it_value.tv_sec; if (_pmolditimer.it_value.tv_sec < 0) { /* missed the user's itimer, pretend there is 1 msec to go! */ _pmolditimer.it_value.tv_sec = 0; _pmolditimer.it_value.tv_usec = 1000; } setitimer(ITIMER_REAL, &_pmolditimer, &_pmmyitimer); } #endif /* Was the connection successful? */ if (sts == 0) break; /* Unsuccessful connection. */ __pmCloseSocket(__pmfd); __pmfd = -1; } /* loop over addresses */ __pmHostEntFree(servinfo); /* Was the connection successful? */ if (__pmfd < 0) { #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_COMMS) fprintf(stderr, "_pmtraceconnect(socket failed): %s\n", netstrerror()); #endif return -sts; } _pmtimedout = 0; /* Restore the original file status flags. */ if (__pmSetFileStatusFlags(__pmfd, flags) < 0) { #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_COMMS) fprintf(stderr, ":_pmtraceconnect: cannot restore file status flags\n"); #endif return -oserror(); } /* make sure this file descriptor is closed if exec() is called */ if ((flags = __pmGetFileDescriptorFlags(__pmfd)) != -1) sts = __pmSetFileDescriptorFlags(__pmfd, flags | FD_CLOEXEC); else sts = -1; if (sts == -1) return -oserror(); if (__pmtraceprotocol(TRACE_PROTOCOL_QUERY) == TRACE_PROTOCOL_ASYNC) { /* in the asynchronoous protocol - ensure no delay after close */ if ((flags = __pmGetFileStatusFlags(__pmfd)) != -1) sts = __pmSetFileStatusFlags(__pmfd, flags | FNDELAY); else sts = -1; if (sts == -1) return -oserror(); #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_COMMS) fprintf(stderr, "_pmtraceconnect: async protocol setup complete\n"); #endif } else #ifdef PMTRACE_DEBUG if (__pmstate & PMTRACE_STATE_COMMS) fprintf(stderr, "_pmtraceconnect: sync protocol setup complete\n"); #endif /* trace PMDA sends an ACK on successful connect */ sts = _pmtracegetack(sts, 0); return sts; }