diff options
Diffstat (limited to 'src/pmdas/trace/src/comms.c')
-rw-r--r-- | src/pmdas/trace/src/comms.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/src/pmdas/trace/src/comms.c b/src/pmdas/trace/src/comms.c new file mode 100644 index 0000000..4432c05 --- /dev/null +++ b/src/pmdas/trace/src/comms.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2012-2013 Red Hat. All Rights Reserved. + * Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "trace.h" +#include "trace_dev.h" +#include "domain.h" +#include "client.h" +#include "comms.h" + +extern struct timeval interval; +extern int readData(int, int *); +extern void timerUpdate(void); + +extern int maxfd; +extern int nclients; +extern client_t *clients; +extern int ctlport; /* control port number */ +static int ctlfd; /* fd for control port */ +static int pmcdfd; /* fd for pmcd */ + +void alarming(int, void *); +static void hangup(int); +static int getcport(void); + +/* currently in-use fd mask */ +fd_set fds; + +/* the AF event number */ +int afid = -1; + +void +traceMain(pmdaInterface *dispatch) +{ + client_t *cp; + fd_set readyfds; + int nready, i, pdutype, sts, protocol; + + ctlfd = getcport(); + pmcdfd = __pmdaInFd(dispatch); + maxfd = (ctlfd > pmcdfd) ? (ctlfd):(pmcdfd); + FD_ZERO(&fds); + FD_SET(ctlfd, &fds); + FD_SET(pmcdfd, &fds); + + signal(SIGHUP, hangup); + + /* arm interval timer */ + if ((afid = __pmAFregister(&interval, NULL, alarming)) < 0) { + __pmNotifyErr(LOG_ERR, "error registering asynchronous event handler"); + exit(1); + } + + for (;;) { + memcpy(&readyfds, &fds, sizeof(readyfds)); + nready = select(maxfd+1, &readyfds, NULL, NULL, NULL); + + if (nready == 0) + continue; + else if (nready < 0) { + if (neterror() != EINTR) { + __pmNotifyErr(LOG_ERR, "select failure: %s", netstrerror()); + exit(1); + } + continue; + } + + __pmAFblock(); + if (FD_ISSET(pmcdfd, &readyfds)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "processing pmcd request [fd=%d]", pmcdfd); +#endif + if (__pmdaMainPDU(dispatch) < 0) { + __pmAFunblock(); + exit(1); /* fatal if we lose pmcd */ + } + } + /* handle request on control port */ + if (FD_ISSET(ctlfd, &readyfds)) { + if ((cp = acceptClient(ctlfd)) != NULL) { + sts = __pmAccAddClient(cp->addr, &cp->denyOps); + if (sts == PM_ERR_PERMISSION) + sts = PMTRACE_ERR_PERMISSION; + else if (sts == PM_ERR_CONNLIMIT) + sts = PMTRACE_ERR_CONNLIMIT; + else if (sts >= 0) + sts = TRACE_PDU_VERSION; + __pmtracesendack(cp->fd, sts); + if (sts < 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(cp->addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: connect refused, denyOps=0x%x: %s", + hostAddr, cp->fd, cp->denyOps, pmtraceerrstr(sts)); + free(hostAddr); + } +#endif + deleteClient(cp); + } + else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(cp->addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: new connection, denyOps=0x%x", + hostAddr, cp->fd, cp->denyOps); + free(hostAddr); + } +#endif + ; + } + } + } + for (i = 0; i < nclients; i++) { + if (!clients[i].status.connected) + continue; + if (clients[i].denyOps & TR_OP_SEND) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: send denied, denyOps=0x%x", + hostAddr, clients[i].fd, clients[i].denyOps); + free(hostAddr); + } +#endif + __pmtracesendack(clients[i].fd, PMTRACE_ERR_PERMISSION); + __pmAccDelClient(clients[i].addr); + deleteClient(&clients[i]); + } + else if (FD_ISSET(clients[i].fd, &readyfds)) { + protocol = 1; /* default to synchronous */ + do { + if ((pdutype = readData(clients[i].fd, &protocol)) < 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: close connection", + hostAddr, clients[i].fd); + free(hostAddr); + } +#endif + __pmAccDelClient(clients[i].addr); + deleteClient(&clients[i]); + } + else { + clients[i].status.protocol = protocol; + if (clients[i].status.connected) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: %s ACK (type=%d)", + hostAddr, clients[i].fd, + protocol ? "sending" : "no", pdutype); + free(hostAddr); + } +#endif + if (protocol == 1) { + sts = __pmtracesendack(clients[i].fd, pdutype); + if (sts < 0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_ERR, "client %s [fd=%d]: ACK send failed (type=%d): %s", + hostAddr, clients[i].fd, + pdutype, pmtraceerrstr(sts)); + free(hostAddr); + } + } + } + } + } while (__pmtracemoreinput(clients[i].fd)); + } + } + __pmAFunblock(); + } +} + + +void +alarming(int sig, void *ptr) +{ + timerUpdate(); +} + +static void +hangup(int sig) +{ + showClients(); + signal(SIGHUP, hangup); +} + +/* + * Create socket for incoming connections and bind to it an address for + * clients to use. Only returns if it succeeds (exits on failure). + */ +static int +getcport(void) +{ + int fd; + int i=1, one=1, sts; + struct sockaddr_in myAddr; + struct linger noLinger = {1, 0}; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + __pmNotifyErr(LOG_ERR, "getcport: socket: %s", netstrerror()); + exit(1); + } + /* avoid 200 ms delay */ + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &i, + (__pmSockLen)sizeof(i)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(nodelay): %s", + netstrerror()); + exit(1); + } + /* don't linger on close */ + if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &noLinger, + (__pmSockLen)sizeof(noLinger)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(nolinger): %s", + netstrerror()); + exit(1); + } +#ifndef IS_MINGW + /* ignore dead client connections */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, + (__pmSockLen)sizeof(one)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(reuseaddr): %s", + netstrerror()); + exit(1); + } +#else + /* see MSDN tech note: "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" */ + if (setsockopt(sfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &one, + (__pmSockLen)sizeof(one)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(excladdruse): %s", + netstrerror()); + exit(1); + } +#endif + + if (ctlport == -1) { + /* + * check for port info in the environment + */ + char *env_str; + if ((env_str = getenv(TRACE_ENV_PORT)) != NULL) { + char *end_ptr; + + ctlport = (int)strtol(env_str, &end_ptr, 0); + if (*end_ptr != '\0' || ctlport < 0) { + __pmNotifyErr(LOG_WARNING, "env port is bogus (%s)", env_str); + ctlport = TRACE_PORT; + } + } + else + ctlport = TRACE_PORT; + } + + /* TODO: IPv6 */ + memset(&myAddr, 0, sizeof(myAddr)); + myAddr.sin_family = AF_INET; + myAddr.sin_addr.s_addr = htonl(INADDR_ANY); + myAddr.sin_port = htons(ctlport); + + sts = bind(fd, (const struct sockaddr *)&myAddr, sizeof(myAddr)); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "bind(%d): %s", ctlport, netstrerror()); + exit(1); + } + sts = listen(fd, 5); /* Max. of 5 pending connection requests */ + if (sts == -1) { + __pmNotifyErr(LOG_ERR, "listen: %s", netstrerror()); + exit(1); + } + + return fd; +} |