summaryrefslogtreecommitdiff
path: root/src/pmproxy
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmproxy
downloadpcp-debian/3.9.10.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmproxy')
-rw-r--r--src/pmproxy/GNUmakefile50
-rw-r--r--src/pmproxy/client.c260
-rw-r--r--src/pmproxy/pmproxy.c543
-rw-r--r--src/pmproxy/pmproxy.h48
-rw-r--r--src/pmproxy/pmproxy.options31
-rw-r--r--src/pmproxy/pmproxy.service.in14
-rw-r--r--src/pmproxy/rc_pmproxy301
-rw-r--r--src/pmproxy/util.c97
8 files changed, 1344 insertions, 0 deletions
diff --git a/src/pmproxy/GNUmakefile b/src/pmproxy/GNUmakefile
new file mode 100644
index 0000000..a519c75
--- /dev/null
+++ b/src/pmproxy/GNUmakefile
@@ -0,0 +1,50 @@
+#
+# Copyright (c) 2014 Red Hat.
+# Copyright (c) 2000-2002 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.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/src/include/builddefs
+
+CMDTARGET = pmproxy$(EXECSUFFIX)
+HFILES = pmproxy.h
+CFILES = pmproxy.c client.c util.c
+
+LLDLIBS = $(PCPLIB)
+LDIRT = pmproxy.log pmproxy.service
+
+LCFLAGS += $(PIECFLAGS)
+LLDFLAGS += $(PIELDFLAGS)
+
+default: $(CMDTARGET) pmproxy.service
+
+pmproxy.service: pmproxy.service.in
+ $(SED) -e 's;@path@;'$(PCP_RC_DIR)';' $< > $@
+
+include $(BUILDRULES)
+
+install: default
+ $(INSTALL) -m 755 -d `dirname $(PCP_PMPROXYOPTIONS_PATH)`
+ $(INSTALL) -m 644 pmproxy.options $(PCP_PMPROXYOPTIONS_PATH)
+ $(INSTALL) -m 755 rc_pmproxy $(PCP_RC_DIR)/pmproxy
+ifeq ($(ENABLE_SYSTEMD),true)
+ $(INSTALL) -m 644 pmproxy.service $(PCP_SYSTEMDUNIT_DIR)/pmproxy.service
+endif
+ $(INSTALL) -m 755 $(CMDTARGET) $(PCP_BINADM_DIR)/$(CMDTARGET)
+ $(INSTALL) -m 775 -o $(PCP_USER) -g $(PCP_USER) -d $(PCP_LOG_DIR)/pmproxy
+
+default_pcp : default
+
+install_pcp : install
+
+pmproxy.o client.o: pmproxy.h
diff --git a/src/pmproxy/client.c b/src/pmproxy/client.c
new file mode 100644
index 0000000..f45534b
--- /dev/null
+++ b/src/pmproxy/client.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2012-2013 Red Hat.
+ * Copyright (c) 1995-2002 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 "pmproxy.h"
+
+#define MIN_CLIENTS_ALLOC 8
+
+ClientInfo *client;
+int nClients; /* Number in array, (not all in use) */
+int maxReqPortFd; /* highest request port fd */
+int maxSockFd; /* largest fd for a client */
+__pmFdSet sockFds; /* for client select() */
+
+static int
+NewClient(void)
+{
+ int i;
+ static int clientSize;
+
+ for (i = 0; i < nClients; i++)
+ if (!client[i].status.connected)
+ break;
+
+ if (i == clientSize) {
+ int j, sz;
+
+ clientSize = clientSize ? clientSize * 2 : MIN_CLIENTS_ALLOC;
+ sz = sizeof(ClientInfo) * clientSize;
+ client = (ClientInfo *) realloc(client, sz);
+ if (client == NULL) {
+ __pmNoMem("NewClient", sz, PM_RECOV_ERR);
+ Shutdown();
+ exit(1);
+ }
+ for (j = i; j < clientSize; j++) {
+ client[j].addr = NULL;
+ }
+ }
+ client[i].addr = __pmSockAddrAlloc();
+ if (client[i].addr == NULL) {
+ __pmNoMem("NewClient", __pmSockAddrSize(), PM_RECOV_ERR);
+ Shutdown();
+ exit(1);
+ }
+ if (i >= nClients)
+ nClients = i + 1;
+ return i;
+}
+
+/* MY_BUFLEN needs to big enough to hold "hostname port" */
+#define MY_BUFLEN (MAXHOSTNAMELEN+10)
+#define MY_VERSION "pmproxy-server 1\n"
+
+/* Establish a new socket connection to a client */
+ClientInfo *
+AcceptNewClient(int reqfd)
+{
+ int i;
+ int fd;
+ __pmSockLen addrlen;
+ int ok = 0;
+ char buf[MY_BUFLEN];
+ char *bp;
+ char *endp;
+ char *abufp;
+
+ i = NewClient();
+ addrlen = __pmSockAddrSize();
+ fd = __pmAccept(reqfd, client[i].addr, &addrlen);
+ if (fd == -1) {
+ __pmNotifyErr(LOG_ERR, "AcceptNewClient(%d) __pmAccept failed: %s",
+ reqfd, netstrerror());
+ Shutdown();
+ exit(1);
+ }
+ __pmSetSocketIPC(fd);
+ if (fd > maxSockFd)
+ maxSockFd = fd;
+ __pmFD_SET(fd, &sockFds);
+
+ client[i].fd = fd;
+ client[i].pmcd_fd = -1;
+ client[i].status.connected = 1;
+ client[i].pmcd_hostname = NULL;
+
+ /*
+ * version negotiation (converse to negotiate_proxy() logic in
+ * libpcp
+ *
+ * __pmRecv client version message
+ * __pmSend my server version message
+ * __pmRecv pmcd hostname and pmcd port
+ */
+ for (bp = buf; bp < &buf[MY_BUFLEN]; bp++) {
+ if (__pmRecv(fd, bp, 1, 0) != 1) {
+ *bp = '\0'; /* null terminate what we have */
+ bp = &buf[MY_BUFLEN]; /* flag error */
+ break;
+ }
+ /* end of line means no more ... */
+ if (*bp == '\n' || *bp == '\r') {
+ *bp = '\0';
+ break;
+ }
+ }
+ if (bp < &buf[MY_BUFLEN]) {
+ /* looks OK so far ... is this a version we can support? */
+ if (strcmp(buf, "pmproxy-client 1") == 0) {
+ client[i].version = 1;
+ ok = 1;
+ }
+ }
+
+ if (!ok) {
+ abufp = __pmSockAddrToString(client[i].addr);
+ __pmNotifyErr(LOG_WARNING, "Bad version string from client at %s", abufp);
+ free(abufp);
+ fprintf(stderr, "AcceptNewClient: bad version string was \"");
+ for (bp = buf; *bp && bp < &buf[MY_BUFLEN]; bp++)
+ fputc(*bp & 0xff, stderr);
+ fprintf(stderr, "\"\n");
+ DeleteClient(&client[i]);
+ return NULL;
+ }
+
+ if (__pmSend(fd, MY_VERSION, strlen(MY_VERSION), 0) != strlen(MY_VERSION)) {
+ abufp = __pmSockAddrToString(client[i].addr);
+ __pmNotifyErr(LOG_WARNING, "AcceptNewClient: failed to send version "
+ "string (%s) to client at %s\n", MY_VERSION, abufp);
+ free(abufp);
+ DeleteClient(&client[i]);
+ return NULL;
+ }
+
+ for (bp = buf; bp < &buf[MY_BUFLEN]; bp++) {
+ if (__pmRecv(fd, bp, 1, 0) != 1) {
+ *bp = '\0'; /* null terminate what we have */
+ bp = &buf[MY_BUFLEN]; /* flag error */
+ break;
+ }
+ /* end of line means no more ... */
+ if (*bp == '\n' || *bp == '\r') {
+ *bp = '\0';
+ break;
+ }
+ }
+ if (bp < &buf[MY_BUFLEN]) {
+ /* looks OK so far ... get hostname and port */
+ for (bp = buf; *bp && *bp != ' '; bp++)
+ ;
+ if (bp != buf) {
+ *bp = '\0';
+ client[i].pmcd_hostname = strdup(buf);
+ if (client[i].pmcd_hostname == NULL)
+ __pmNoMem("PMCD.hostname", strlen(buf), PM_FATAL_ERR);
+ bp++;
+ client[i].pmcd_port = (int)strtoul(bp, &endp, 10);
+ if (*endp != '\0') {
+ abufp = __pmSockAddrToString(client[i].addr);
+ __pmNotifyErr(LOG_WARNING, "AcceptNewClient: bad pmcd port "
+ "\"%s\" from client at %s", bp, abufp);
+ free(abufp);
+ DeleteClient(&client[i]);
+ return NULL;
+ }
+ }
+ /* error, fall through */
+ }
+
+ if (client[i].pmcd_hostname == NULL) {
+ abufp = __pmSockAddrToString(client[i].addr);
+ __pmNotifyErr(LOG_WARNING, "AcceptNewClient: failed to get PMCD "
+ "hostname (%s) from client at %s", buf, abufp);
+ free(abufp);
+ DeleteClient(&client[i]);
+ return NULL;
+ }
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_CONTEXT) {
+ /*
+ * note error message gets appended to once pmcd connection is
+ * made in ClientLoop()
+ */
+ abufp = __pmSockAddrToString(client[i].addr);
+ fprintf(stderr, "AcceptNewClient [%d] fd=%d from %s to %s (port %s)",
+ i, fd, abufp, client[i].pmcd_hostname, bp);
+ free(abufp);
+ }
+#endif
+
+ return &client[i];
+}
+
+void
+DeleteClient(ClientInfo *cp)
+{
+ int i;
+
+ for (i = 0; i < nClients; i++)
+ if (cp == &client[i])
+ break;
+
+ if (i == nClients) {
+ fprintf(stderr, "DeleteClient: Botch: tried to delete non-existent client @" PRINTF_P_PFX "%p\n", cp);
+ return;
+ }
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_CONTEXT)
+ fprintf(stderr, "DeleteClient [%d]\n", i);
+#endif
+
+ if (cp->fd >= 0) {
+ __pmFD_CLR(cp->fd, &sockFds);
+ __pmCloseSocket(cp->fd);
+ }
+ if (cp->pmcd_fd >= 0) {
+ __pmFD_CLR(cp->pmcd_fd, &sockFds);
+ __pmCloseSocket(cp->pmcd_fd);
+ }
+ if (i == nClients-1) {
+ i--;
+ while (i >= 0 && !client[i].status.connected)
+ i--;
+ nClients = (i >= 0) ? i + 1 : 0;
+ }
+ if (cp->fd == maxSockFd || cp->pmcd_fd == maxSockFd) {
+ maxSockFd = maxReqPortFd;
+ for (i = 0; i < nClients; i++) {
+ if (cp->status.connected == 0)
+ continue;
+ if (client[i].fd > maxSockFd)
+ maxSockFd = client[i].fd;
+ if (client[i].pmcd_fd > maxSockFd)
+ maxSockFd = client[i].pmcd_fd;
+ }
+ }
+ __pmSockAddrFree(cp->addr);
+ cp->addr = NULL;
+ cp->status.connected = 0;
+ cp->fd = -1;
+ cp->pmcd_fd = -1;
+ if (cp->pmcd_hostname != NULL) {
+ free(cp->pmcd_hostname);
+ cp->pmcd_hostname = NULL;
+ }
+}
diff --git a/src/pmproxy/pmproxy.c b/src/pmproxy/pmproxy.c
new file mode 100644
index 0000000..b359199
--- /dev/null
+++ b/src/pmproxy/pmproxy.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2012-2014 Red Hat.
+ * Copyright (c) 2002 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 "pmproxy.h"
+#include <sys/stat.h>
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#define MAXPENDING 5 /* maximum number of pending connections */
+#define FDNAMELEN 40 /* maximum length of a fd description */
+#define STRINGIFY(s) #s
+#define TO_STRING(s) STRINGIFY(s)
+
+static char *FdToString(int);
+
+static int timeToDie; /* For SIGINT handling */
+static char *logfile = "pmproxy.log"; /* log file name */
+static int run_daemon = 1; /* run as a daemon, see -f */
+static char *fatalfile = "/dev/tty";/* fatal messages at startup go here */
+static char *username;
+static char *certdb; /* certificate DB path (NSS) */
+static char *dbpassfile; /* certificate DB password file */
+static char *hostname;
+
+static void
+DontStart(void)
+{
+ FILE *tty;
+ FILE *log;
+
+ __pmNotifyErr(LOG_ERR, "pmproxy not started due to errors!\n");
+
+ if ((tty = fopen(fatalfile, "w")) != NULL) {
+ fflush(stderr);
+ fprintf(tty, "NOTE: pmproxy not started due to errors! ");
+ if ((log = fopen(logfile, "r")) != NULL) {
+ int c;
+ fprintf(tty, "Log file \"%s\" contains ...\n", logfile);
+ while ((c = fgetc(log)) != EOF)
+ fputc(c, tty);
+ fclose(log);
+ }
+ else
+ fprintf(tty, "Log file \"%s\" has vanished!\n", logfile);
+ fclose(tty);
+ }
+ exit(1);
+}
+
+static pmLongOptions longopts[] = {
+ PMAPI_OPTIONS_HEADER("General options"),
+ PMOPT_DEBUG,
+ PMOPT_HELP,
+ PMAPI_OPTIONS_HEADER("Service options"),
+ { "foreground", 0, 'f', 0, "run in the foreground" },
+ { "username", 1, 'U', "USER", "in daemon mode, run as named user [default pcp]" },
+ PMAPI_OPTIONS_HEADER("Configuration options"),
+ { "certdb", 1, 'C', "PATH", "path to NSS certificate database" },
+ { "passfile", 1, 'P', "PATH", "password file for certificate database access" },
+ { "", 1, 'L', "BYTES", "maximum size for PDUs from clients [default 65536]" },
+ PMAPI_OPTIONS_HEADER("Connection options"),
+ { "interface", 1, 'i', "ADDR", "accept connections on this IP address" },
+ { "port", 1, 'p', "N", "accept connections on this port" },
+ PMAPI_OPTIONS_HEADER("Diagnostic options"),
+ { "log", 1, 'l', "PATH", "redirect diagnostics and trace output" },
+ { "", 1, 'x', "PATH", "fatal messages at startup sent to file [default /dev/tty]" },
+ PMAPI_OPTIONS_END
+};
+
+static pmOptions opts = {
+ .short_options = "C:D:fi:l:L:p:P:U:x:?",
+ .long_options = longopts,
+};
+
+static void
+ParseOptions(int argc, char *argv[], int *nports)
+{
+ int c;
+ int sts;
+ int usage = 0;
+
+ while ((c = pmgetopt_r(argc, argv, &opts)) != EOF) {
+ switch (c) {
+
+ case 'C': /* path to NSS certificate database */
+ certdb = opts.optarg;
+ break;
+
+ case 'D': /* debug flag */
+ if ((sts = __pmParseDebug(opts.optarg)) < 0) {
+ pmprintf("%s: unrecognized debug flag specification (%s)\n",
+ pmProgname, opts.optarg);
+ opts.errors++;
+ } else {
+ pmDebug |= sts;
+ }
+ break;
+
+ case 'f': /* foreground, i.e. do _not_ run as a daemon */
+ run_daemon = 0;
+ break;
+
+ case 'i':
+ /* one (of possibly several) interfaces for client requests */
+ __pmServerAddInterface(opts.optarg);
+ break;
+
+ case 'l':
+ /* log file name */
+ logfile = opts.optarg;
+ break;
+
+ case 'L': /* Maximum size for PDUs from clients */
+ sts = (int)strtol(opts.optarg, NULL, 0);
+ if (sts <= 0) {
+ pmprintf("%s: -L requires a positive value\n", pmProgname);
+ opts.errors++;
+ } else {
+ __pmSetPDUCeiling(sts);
+ }
+ break;
+
+ case 'p':
+ if (__pmServerAddPorts(opts.optarg) < 0) {
+ pmprintf("%s: -p requires a positive numeric argument (%s)\n",
+ pmProgname, opts.optarg);
+ opts.errors++;
+ } else {
+ *nports += 1;
+ }
+ break;
+
+ case 'P': /* password file for certificate database access */
+ dbpassfile = opts.optarg;
+ break;
+
+ case 'U': /* run as user username */
+ username = opts.optarg;
+ break;
+
+ case 'x':
+ fatalfile = opts.optarg;
+ break;
+
+ case '?':
+ usage = 1;
+ break;
+
+ default:
+ opts.errors++;
+ break;
+ }
+ }
+
+ if (usage || opts.errors || opts.optind < argc) {
+ pmUsageMessage(&opts);
+ if (usage)
+ exit(0);
+ DontStart();
+ }
+}
+
+static void
+CleanupClient(ClientInfo *cp, int sts)
+{
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ int i;
+ for (i = 0; i < nClients; i++) {
+ if (cp == &client[i])
+ break;
+ }
+ fprintf(stderr, "CleanupClient: client[%d] fd=%d %s (%d)\n",
+ i, cp->fd, pmErrStr(sts), sts);
+ }
+#endif
+
+ DeleteClient(cp);
+}
+
+static int
+VerifyClient(ClientInfo *cp, __pmPDU *pb)
+{
+ int i, sts, flags = 0, sender = 0, credcount = 0;
+ __pmPDUHdr *header = (__pmPDUHdr *)pb;
+ __pmHashCtl attrs = { 0 }; /* TODO */
+ __pmCred *credlist;
+
+ /* first check that this is a credentials PDU */
+ if (header->type != PDU_CREDS)
+ return PM_ERR_IPC;
+
+ /* now decode it and if secure connection requested, set it up */
+ if ((sts = __pmDecodeCreds(pb, &sender, &credcount, &credlist)) < 0)
+ return sts;
+
+ for (i = 0; i < credcount; i++) {
+ if (credlist[i].c_type == CVERSION) {
+ __pmVersionCred *vcp = (__pmVersionCred *)&credlist[i];
+ flags = vcp->c_flags;
+ break;
+ }
+ }
+ if (credlist != NULL)
+ free(credlist);
+
+ /* need to ensure both the pmcd and client channel use flags */
+
+ if (sts >= 0 && flags)
+ sts = __pmSecureServerHandshake(cp->fd, flags, &attrs);
+
+ /* send credentials PDU through to pmcd now (order maintained) */
+ if (sts >= 0)
+ sts = __pmXmitPDU(cp->pmcd_fd, pb);
+
+ /*
+ * finally perform any additional handshaking needed with pmcd.
+ * Do not initialize NSS again.
+ */
+ if (sts >= 0 && flags)
+ sts = __pmSecureClientHandshake(cp->pmcd_fd,
+ flags | PDU_FLAG_NO_NSS_INIT,
+ hostname, &attrs);
+
+ return sts;
+}
+
+/* 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;
+ }
+ }
+}
+
+/* Called to shutdown pmproxy in an orderly manner */
+void
+Shutdown(void)
+{
+ int i;
+
+ for (i = 0; i < nClients; i++)
+ if (client[i].status.connected)
+ __pmCloseSocket(client[i].fd);
+ __pmServerCloseRequestPorts();
+ __pmSecureServerShutdown();
+ __pmNotifyErr(LOG_INFO, "pmproxy Shutdown\n");
+ fflush(stderr);
+}
+
+void
+SignalShutdown(void)
+{
+ __pmNotifyErr(LOG_INFO, "pmproxy caught SIGINT or SIGTERM\n");
+ Shutdown();
+ exit(0);
+}
+
+static void
+CheckNewClient(__pmFdSet * fdset, int rfd, int family)
+{
+ ClientInfo *cp;
+
+ if (__pmFD_ISSET(rfd, fdset)) {
+ if ((cp = AcceptNewClient(rfd)) == NULL)
+ /* failed to negotiate, already cleaned up */
+ return;
+
+ /* establish a new connection to pmcd */
+ if ((cp->pmcd_fd = __pmAuxConnectPMCDPort(cp->pmcd_hostname, cp->pmcd_port)) < 0) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_CONTEXT)
+ /* append to message started in AcceptNewClient() */
+ fprintf(stderr, " oops!\n"
+ "__pmAuxConnectPMCDPort(%s,%d) failed: %s\n",
+ cp->pmcd_hostname, cp->pmcd_port,
+ pmErrStr(-oserror()));
+#endif
+ CleanupClient(cp, -oserror());
+ }
+ else {
+ if (cp->pmcd_fd > maxSockFd)
+ maxSockFd = cp->pmcd_fd;
+ __pmFD_SET(cp->pmcd_fd, &sockFds);
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_CONTEXT)
+ /* append to message started in AcceptNewClient() */
+ fprintf(stderr, " fd=%d\n", cp->pmcd_fd);
+#endif
+ }
+ }
+}
+
+/* Loop, synchronously processing requests from clients. */
+static void
+ClientLoop(void)
+{
+ int i, sts;
+ int maxFd;
+ __pmFdSet readableFds;
+
+ for (;;) {
+ /* Figure out which file descriptors to wait for input on. Keep
+ * track of the highest numbered descriptor for the select call.
+ */
+ readableFds = sockFds;
+ maxFd = maxSockFd + 1;
+
+ sts = __pmSelectRead(maxFd, &readableFds, NULL);
+
+ if (sts > 0) {
+ if (pmDebug & DBG_TRACE_APPL0)
+ for (i = 0; i <= maxSockFd; i++)
+ if (__pmFD_ISSET(i, &readableFds))
+ fprintf(stderr, "__pmSelectRead(): from %s fd=%d\n",
+ FdToString(i), i);
+ __pmServerAddNewClients(&readableFds, CheckNewClient);
+ HandleInput(&readableFds);
+ }
+ else if (sts == -1 && neterror() != EINTR) {
+ __pmNotifyErr(LOG_ERR, "ClientLoop select: %s\n", netstrerror());
+ break;
+ }
+ if (timeToDie) {
+ SignalShutdown();
+ break;
+ }
+ }
+}
+
+#ifdef IS_MINGW
+static void
+SigIntProc(int s)
+{
+ SignalShutdown();
+}
+#else
+static void
+SigIntProc(int s)
+{
+ signal(SIGINT, SigIntProc);
+ signal(SIGTERM, SigIntProc);
+ timeToDie = 1;
+}
+#endif
+
+static void
+SigBad(int sig)
+{
+ if (pmDebug & DBG_TRACE_DESPERATE) {
+ __pmNotifyErr(LOG_ERR, "Unexpected signal %d ...\n", sig);
+ fprintf(stderr, "\nDumping to core ...\n");
+ fflush(stderr);
+ }
+ _exit(sig);
+}
+
+/*
+ * Hostname extracted and cached for later use during protocol negotiations
+ */
+static void
+GetProxyHostname(void)
+{
+ __pmHostEnt *hep;
+ char host[MAXHOSTNAMELEN];
+
+ if (gethostname(host, MAXHOSTNAMELEN) < 0) {
+ __pmNotifyErr(LOG_ERR, "%s: gethostname failure\n", pmProgname);
+ DontStart();
+ }
+ host[MAXHOSTNAMELEN-1] = '\0';
+
+ hep = __pmGetAddrInfo(host);
+ if (hep == NULL) {
+ __pmNotifyErr(LOG_ERR, "%s: __pmGetAddrInfo failure\n", pmProgname);
+ DontStart();
+ } else {
+ hostname = __pmHostEntGetName(hep);
+ if (!hostname) { /* no reverse DNS lookup for local hostname */
+ hostname = strdup(host);
+ if (!hostname) /* out of memory, we're having a bad day!?! */
+ __pmNoMem("PMPROXY.hostname", strlen(host), PM_FATAL_ERR);
+ }
+ __pmHostEntFree(hep);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int sts;
+ int nport = 0;
+ char *envstr;
+
+ umask(022);
+ __pmGetUsername(&username);
+ __pmSetInternalState(PM_STATE_PMCS);
+
+ if ((envstr = getenv("PMPROXY_PORT")) != NULL)
+ nport = __pmServerAddPorts(envstr);
+ ParseOptions(argc, argv, &nport);
+ if (nport == 0)
+ __pmServerAddPorts(TO_STRING(PROXY_PORT));
+ GetProxyHostname();
+
+ if (run_daemon) {
+ fflush(stderr);
+ StartDaemon(argc, argv);
+ __pmServerCreatePIDFile(PM_SERVER_PROXY_SPEC, 0);
+ }
+
+ __pmSetSignalHandler(SIGHUP, SIG_IGN);
+ __pmSetSignalHandler(SIGINT, SigIntProc);
+ __pmSetSignalHandler(SIGTERM, SigIntProc);
+ __pmSetSignalHandler(SIGBUS, SigBad);
+ __pmSetSignalHandler(SIGSEGV, SigBad);
+
+ /* Open request ports for client connections */
+ if ((sts = __pmServerOpenRequestPorts(&sockFds, MAXPENDING)) < 0)
+ DontStart();
+ maxReqPortFd = maxSockFd = sts;
+
+ __pmOpenLog(pmProgname, logfile, stderr, &sts);
+ /* close old stdout, and force stdout into same stream as stderr */
+ fflush(stdout);
+ close(fileno(stdout));
+ if (dup(fileno(stderr)) == -1) {
+ fprintf(stderr, "Warning: dup() failed: %s\n", pmErrStr(-oserror()));
+ }
+
+ fprintf(stderr, "pmproxy: PID = %" FMT_PID, getpid());
+ fprintf(stderr, ", PDU version = %u\n", PDU_VERSION);
+ __pmServerDumpRequestPorts(stderr);
+ fflush(stderr);
+
+ /* lose root privileges if we have them */
+ __pmSetProcessIdentity(username);
+
+ if (__pmSecureServerSetup(certdb, dbpassfile) < 0)
+ DontStart();
+
+ /* all the work is done here */
+ ClientLoop();
+
+ Shutdown();
+ exit(0);
+}
+
+/* Convert a file descriptor to a string describing what it is for. */
+static char *
+FdToString(int fd)
+{
+ static char fdStr[FDNAMELEN];
+ static char *stdFds[4] = {"*UNKNOWN FD*", "stdin", "stdout", "stderr"};
+ int i;
+
+ if (fd >= -1 && fd < 3)
+ return stdFds[fd + 1];
+ if (__pmServerRequestPortString(fd, fdStr, FDNAMELEN) != NULL)
+ return fdStr;
+ for (i = 0; i < nClients; i++) {
+ if (client[i].status.connected && fd == client[i].fd) {
+ sprintf(fdStr, "client[%d] client socket", i);
+ return fdStr;
+ }
+ if (client[i].status.connected && fd == client[i].pmcd_fd) {
+ sprintf(fdStr, "client[%d] pmcd socket", i);
+ return fdStr;
+ }
+ }
+ return stdFds[0];
+}
diff --git a/src/pmproxy/pmproxy.h b/src/pmproxy/pmproxy.h
new file mode 100644
index 0000000..df6005c
--- /dev/null
+++ b/src/pmproxy/pmproxy.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012-2013 Red Hat.
+ * Copyright (c) 2002 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.
+ */
+#ifndef _PROXY_H
+#define _PROXY_H
+
+#include "pmapi.h"
+#include "impl.h"
+
+/* The table of clients, used by pmproxy */
+typedef struct {
+ int fd; /* client socket descriptor */
+ int version; /* proxy-client protocol version */
+ struct { /* Status of connection to client */
+ unsigned int connected : 1; /* Client connected, socket level */
+ unsigned int allowed : 1; /* Creds seen, OK to talk to pmcd */
+ } status;
+ char *pmcd_hostname; /* PMCD hostname */
+ int pmcd_port; /* PMCD port */
+ int pmcd_fd; /* PMCD socket file descriptor */
+ __pmSockAddr *addr; /* address of client */
+} ClientInfo;
+
+extern ClientInfo *client; /* Array of clients */
+extern int nClients; /* Number of entries in array */
+extern int maxReqPortFd; /* highest request port fd */
+extern int maxSockFd; /* largest fd for a clients
+ * and pmcd connections */
+extern __pmFdSet sockFds; /* for select() */
+
+/* prototypes */
+extern ClientInfo *AcceptNewClient(int);
+extern void DeleteClient(ClientInfo *);
+extern void StartDaemon(int, char **);
+extern void Shutdown(void);
+
+#endif /* _PROXY_H */
diff --git a/src/pmproxy/pmproxy.options b/src/pmproxy/pmproxy.options
new file mode 100644
index 0000000..bd83e0c
--- /dev/null
+++ b/src/pmproxy/pmproxy.options
@@ -0,0 +1,31 @@
+# command-line options and environment variables for pmproxy
+# uncomment/edit lines as required
+
+# run in the foreground
+# -f
+
+# restrict PCP monitoring client connections to these interfaces
+# -i gateway-public-0.foo.bar.com
+# -i gateway-public-1.foo.bar.com
+
+# make log go someplace else
+# -l /some/place/else
+
+# maximum incoming PDU size (default 64KB)
+# -L 16384
+
+# assume identity of some user other than "pcp"
+# -U nobody
+
+# emergency messages before logfile created
+# -x /tmp/desperate.log
+
+# setting of environment variables for pmproxy
+
+# port for incomining connections from PCP monitoring clients
+# PMPROXY_PORT=44322
+
+# timeouts for interactions with pmcd on behalf of clients
+# PMCD_CONNECT_TIMEOUT=10
+# PMCD_RECONNECT_TIMEOUT=10,20,30
+# PMCD_REQUEST_TIMEOUT=10
diff --git a/src/pmproxy/pmproxy.service.in b/src/pmproxy/pmproxy.service.in
new file mode 100644
index 0000000..078f126
--- /dev/null
+++ b/src/pmproxy/pmproxy.service.in
@@ -0,0 +1,14 @@
+[Unit]
+Description=Proxy for Performance Metrics Collector Daemon
+Documentation=man:pmproxy(8)
+Wants=avahi-daemon.service
+After=network.target avahi-daemon.service
+
+[Service]
+Type=oneshot
+ExecStart=@path@/pmproxy start
+ExecStop=@path@/pmproxy stop
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/pmproxy/rc_pmproxy b/src/pmproxy/rc_pmproxy
new file mode 100644
index 0000000..5886219
--- /dev/null
+++ b/src/pmproxy/rc_pmproxy
@@ -0,0 +1,301 @@
+#! /bin/sh
+#
+# Copyright (c) 2005 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.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Start or Stop the Performance Co-Pilot (PCP) proxy daemon for pmcd
+#
+# The following is for chkconfig on RedHat based systems
+# chkconfig: 2345 95 05
+# description: pmproxy is the pmcd proxy daemon for the Performance Co-Pilot (PCP)
+#
+# The following is for insserv(1) based systems,
+# e.g. SuSE, where chkconfig is a perl script.
+### BEGIN INIT INFO
+# Provides: pmproxy
+# Required-Start: $remote_fs
+# Should-Start: $local_fs $network $syslog $time $pmcd
+# Required-Stop: $remote_fs
+# Should-Stop: $local_fs $network $syslog $pmcd
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Control pmproxy (the pmcd proxy daemon for PCP)
+# Description: Configure and control pmproxy (the pmcd proxy daemon for the Performance Co-Pilot)
+### END INIT INFO
+
+. $PCP_DIR/etc/pcp.env
+. $PCP_SHARE_DIR/lib/rc-proc.sh
+
+PMPROXY=$PCP_BINADM_DIR/pmproxy
+PMPROXYOPTS=$PCP_PMPROXYOPTIONS_PATH
+RUNDIR=$PCP_LOG_DIR/pmproxy
+pmprog=$PCP_RC_DIR/pmproxy
+prog=$PCP_RC_DIR/`basename $0`
+
+tmp=`mktemp -d /var/tmp/pcp.XXXXXXXXX` || exit 1
+status=1
+trap "rm -rf $tmp; exit \$status" 0 1 2 3 15
+
+if [ $pmprog = $prog ]
+then
+ VERBOSE_CTL=on
+else
+ VERBOSE_CTL=off
+fi
+
+case "$PCP_PLATFORM"
+in
+ mingw)
+ # nothing we can usefully do here, skip the test
+ #
+ ;;
+
+ *)
+ # standard Unix/Linux style test
+ #
+ ID=id
+ test -f /usr/xpg4/bin/id && ID=/usr/xpg4/bin/id
+
+ IAM=`$ID -u 2>/dev/null`
+ if [ -z "$IAM" ]
+ then
+ # do it the hardway
+ #
+ IAM=`$ID | sed -e 's/.*uid=//' -e 's/(.*//'`
+ fi
+ ;;
+esac
+
+_shutdown()
+{
+ # Is pmproxy running?
+ #
+ _get_pids_by_name pmproxy >$tmp/tmp
+ if [ ! -s $tmp/tmp ]
+ then
+ [ "$1" = verbose ] && echo "$pmprog: pmproxy not running"
+ return 0
+ fi
+
+ # Send pmproxy a SIGTERM, which is noted as a pending shutdown.
+ # When finished the currently active request, pmproxy will close any
+ # connections and then exit.
+ # Wait for pmproxy to terminate.
+ #
+ pmsignal -a -s TERM pmproxy > /dev/null 2>&1
+ $ECHO $PCP_ECHO_N "Waiting for pmproxy to terminate ...""$PCP_ECHO_C"
+ gone=0
+ for i in 1 2 3 4 5 6
+ do
+ sleep 3
+ _get_pids_by_name pmproxy >$tmp/tmp
+ if [ ! -s $tmp/tmp ]
+ then
+ gone=1
+ break
+ fi
+
+ # If pmproxy doesn't go in 15 seconds, SIGKILL and sleep 1 more time
+ # to allow any clients reading from pmproxy sockets to fail so that
+ # socket doesn't end up in TIME_WAIT or somesuch.
+ #
+ if [ $i = 5 ]
+ then
+ $ECHO
+ echo "Process ..."
+ $PCP_PS_PROG $PCP_PS_ALL_FLAGS >$tmp/ps
+ sed 1q $tmp/ps
+ for pid in `cat $tmp/tmp`
+ do
+ $PCP_AWK_PROG <$tmp/ps "\$2 == $pid { print }"
+ done
+ echo "$prog: Warning: Forcing pmproxy to terminate!"
+ pmsignal -a -s KILL pmproxy > /dev/null 2>&1
+ else
+ $ECHO $PCP_ECHO_N ".""$PCP_ECHO_C"
+ fi
+ done
+ if [ $gone != 1 ] # It just WON'T DIE, give up.
+ then
+ echo "Process ..."
+ cat $tmp/tmp
+ echo "$prog: Warning: pmproxy won't die!"
+ exit
+ fi
+ $RC_STATUS -v
+ $PCP_BINADM_DIR/pmpost "stop pmproxy from $pmprog"
+}
+
+_usage()
+{
+ echo "Usage: $pmprog [-v] {start|restart|condrestart|stop|status|reload|force-reload}"
+}
+
+while getopts v c
+do
+ case $c
+ in
+ v) # force verbose
+ VERBOSE_CTL=on
+ ;;
+
+ *)
+ _usage
+ exit 1
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+if [ $VERBOSE_CTL = on ]
+then # For a verbose startup and shutdown
+ ECHO=$PCP_ECHO_PROG
+else # For a quiet startup and shutdown
+ ECHO=:
+fi
+
+if [ "$IAM" != 0 -a "$1" != "status" ]
+then
+ if [ -n "$PCP_DIR" ]
+ then
+ : running in a non-default installation, do not need to be root
+ else
+ echo "$prog:"'
+Error: You must be root (uid 0) to start or stop the PCP pmproxy daemon.'
+ exit
+ fi
+fi
+
+# First reset status of this service
+$RC_RESET
+
+# Return values acc. to LSB for all commands but status:
+# 0 - success
+# 1 - misc error
+# 2 - invalid or excess args
+# 3 - unimplemented feature (e.g. reload)
+# 4 - insufficient privilege
+# 5 - program not installed
+# 6 - program not configured
+#
+# Note that starting an already running service, stopping
+# or restarting a not-running service as well as the restart
+# with force-reload (in case signalling is not supported) are
+# considered a success.
+case "$1" in
+
+ 'start'|'restart'|'condrestart'|'reload'|'force-reload')
+ if [ "$1" = "condrestart" ] && ! is_chkconfig_on pmproxy
+ then
+ status=0
+ exit
+ fi
+
+ _shutdown quietly
+
+ # pmproxy messages should go to stderr, not the GUI notifiers
+ #
+ unset PCP_STDERR
+
+ if [ -x $PMPROXY ]
+ then
+ if [ ! -f $PCP_PMPROXYCONF_PATH ]
+ then
+ echo "$prog:"'
+Error: pmproxy control file "$PCP_PMPROXYCONF_PATH" is missing, cannot start pmproxy.'
+ exit
+ fi
+ if [ ! -d "$RUNDIR" ]
+ then
+ mkdir -p -m 775 "$RUNDIR"
+ chown $PCP_USER:$PCP_GROUP "$RUNDIR"
+ fi
+ cd $RUNDIR
+
+ # salvage the previous versions of any pmproxy
+ #
+ if [ -f pmproxy.log ]
+ then
+ rm -f pmproxylog.log.prev
+ mv pmproxy.log pmproxy.log.prev
+ fi
+
+ $ECHO $PCP_ECHO_N "Starting pmproxy ..." "$PCP_ECHO_C"
+
+ # options file processing ...
+ # only consider lines which start with a hyphen
+ # get rid of the -f option
+ # ensure multiple lines concat onto 1 line
+ OPTS=`sed <$PMPROXYOPTS 2>/dev/null \
+ -e '/^[^-]/d' \
+ -e 's/^/ /' \
+ -e 's/$/ /' \
+ -e 's/ -f / /g' \
+ -e 's/^ //' \
+ -e 's/ $//' \
+ | tr '\012' ' ' `
+
+ # environment stuff
+ #
+ eval `sed -e 's/"/\\"/g' $PMPROXYOPTS \
+ | awk -F= '
+BEGIN { exports="" }
+/^[A-Z]/ && NF == 2 { exports=exports" "$1
+ printf "%s=${%s:-\"%s\"}\n", $1, $1, $2
+ }
+END { if (exports != "") print "export", exports }'`
+
+ $PMPROXY $OPTS
+ $RC_STATUS -v
+
+ $PCP_BINADM_DIR/pmpost "start pmproxy from $pmprog"
+
+ fi
+ status=0
+ ;;
+
+ 'stop')
+ _shutdown
+ status=0
+ ;;
+
+ 'status')
+ # NOTE: $RC_CHECKPROC returns LSB compliant status values.
+ $ECHO $PCP_ECHO_N "Checking for pmproxy:" "$PCP_ECHO_C"
+ if [ -r /etc/rc.status ]
+ then
+ # SuSE
+ $RC_CHECKPROC $PMPROXY
+ $RC_STATUS -v
+ status=$?
+ else
+ # not SuSE
+ $RC_CHECKPROC $PMPROXY
+ status=$?
+ if [ $status -eq 0 ]
+ then
+ $ECHO running
+ else
+ $ECHO stopped
+ fi
+ fi
+ ;;
+
+ *)
+ _usage
+ ;;
+esac
+
diff --git a/src/pmproxy/util.c b/src/pmproxy/util.c
new file mode 100644
index 0000000..c22ac8a
--- /dev/null
+++ b/src/pmproxy/util.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2002 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2009 Aconex. 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 "pmproxy.h"
+
+
+#ifdef IS_MINGW
+
+void
+StartDaemon(int argc, char **argv)
+{
+ PROCESS_INFORMATION piProcInfo;
+ STARTUPINFO siStartInfo;
+ LPTSTR cmdline = NULL;
+ int i, sz = 3; /* -f\0 */
+
+ for (i = 0; i < argc; i++)
+ sz += strlen(argv[i]) + 1;
+ if ((cmdline = malloc(sz)) == NULL) {
+ __pmNotifyErr(LOG_ERR, "StartDaemon: no memory");
+ exit(1);
+ }
+ for (sz = i = 0; i < argc; i++)
+ sz += sprintf(cmdline, "%s ", argv[i]);
+ sprintf(cmdline + sz, "-f");
+
+ ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
+ ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+
+ if (0 == CreateProcess(
+ NULL, cmdline,
+ NULL, NULL, /* process and thread attributes */
+ FALSE, /* inherit handles */
+ CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW | DETACHED_PROCESS,
+ NULL, /* environment (from parent) */
+ NULL, /* current directory */
+ &siStartInfo, /* STARTUPINFO pointer */
+ &piProcInfo)) { /* receives PROCESS_INFORMATION */
+ __pmNotifyErr(LOG_ERR, "StartDaemon: CreateProcess");
+ /* but keep going */
+ }
+ else {
+ /* parent, let her exit, but avoid ugly "Log finished" messages */
+ fclose(stderr);
+ exit(0);
+ }
+}
+
+#else
+
+/* Based on Stevens (Unix Network Programming, p.83) */
+void
+StartDaemon(int argc, char **argv)
+{
+ int childpid;
+
+ (void)argc; (void)argv;
+
+#if defined(HAVE_TERMIO_SIGNALS)
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+#endif
+
+ if ((childpid = fork()) < 0)
+ __pmNotifyErr(LOG_ERR, "StartDaemon: fork");
+ /* but keep going */
+ else if (childpid > 0) {
+ /* parent, let her exit, but avoid ugly "Log finished" messages */
+ fclose(stderr);
+ exit(0);
+ }
+
+ /* not a process group leader, lose controlling tty */
+ if (setsid() == -1)
+ __pmNotifyErr(LOG_WARNING, "StartDaemon: setsid");
+ /* but keep going */
+
+ close(0);
+ /* don't close other fd's -- we know that only good ones are open! */
+
+ /* don't chdir("/") -- we still need to open pmcd.log */
+}
+#endif