summaryrefslogtreecommitdiff
path: root/src/libpcp_gui
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/libpcp_gui
downloadpcp-debian/3.9.10.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/libpcp_gui')
-rw-r--r--src/libpcp_gui/GNUmakefile27
-rw-r--r--src/libpcp_gui/src/GNUmakefile77
-rw-r--r--src/libpcp_gui/src/exports19
-rw-r--r--src/libpcp_gui/src/record.c758
-rw-r--r--src/libpcp_gui/src/timeclient.c202
-rw-r--r--src/libpcp_gui/src/timestate.c307
6 files changed, 1390 insertions, 0 deletions
diff --git a/src/libpcp_gui/GNUmakefile b/src/libpcp_gui/GNUmakefile
new file mode 100644
index 0000000..830d2c9
--- /dev/null
+++ b/src/libpcp_gui/GNUmakefile
@@ -0,0 +1,27 @@
+#
+# Copyright (c) 2006 Aconex. All Rights Reserved.
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This library 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 Lesser General Public
+# License for more details.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/src/include/builddefs
+
+SUBDIRS = src
+
+default install: $(SUBDIRS)
+ $(SUBDIRS_MAKERULE)
+
+include $(BUILDRULES)
+
+default_pcp : default
+
+install_pcp : install
diff --git a/src/libpcp_gui/src/GNUmakefile b/src/libpcp_gui/src/GNUmakefile
new file mode 100644
index 0000000..91589f7
--- /dev/null
+++ b/src/libpcp_gui/src/GNUmakefile
@@ -0,0 +1,77 @@
+#
+# Copyright (c) 2013 Red Hat.
+# Copyright (c) 2006 Aconex. All Rights Reserved.
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This library 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 Lesser General Public
+# License for more details.
+#
+
+TOPDIR = ../../..
+include $(TOPDIR)/src/include/builddefs
+
+CFILES = timeclient.c timestate.c record.c
+LLDLIBS = -lpcp
+
+STATICLIBTARGET = libpcp_gui.a
+
+DSOVERSION = 2
+LIBTARGET = libpcp_gui.$(DSOSUFFIX).$(DSOVERSION)
+SYMTARGET = libpcp_gui.$(DSOSUFFIX) libpcp_gui.$(DSOSUFFIX).1
+
+VERSION_SCRIPT = exports
+
+ifeq "$(PACKAGE_DISTRIBUTION)" "debian"
+SYMTARGET = libpcp_gui.$(DSOSUFFIX)
+endif
+ifeq "$(TARGET_OS)" "darwin"
+LIBTARGET = libpcp_gui.$(DSOVERSION).$(DSOSUFFIX)
+endif
+ifeq "$(TARGET_OS)" "mingw"
+LIBTARGET = libpcp_gui.$(DSOSUFFIX)
+STATICLIBTARGET =
+SYMTARGET =
+endif
+ifeq "$(ENABLE_SHARED)" "no"
+LIBTARGET =
+SYMTARGET =
+endif
+
+LDIRT = $(LIBTARGET) $(SYMTARGET) $(STATICLIBTARGET)
+LSRCFILES = $(VERSION_SCRIPT)
+
+default: $(LIBTARGET) $(SYMTARGET) $(STATICLIBTARGET)
+
+ifneq ($(SYMTARGET),)
+$(SYMTARGET):
+ $(LN_S) -f $(LIBTARGET) $@
+endif
+
+include $(BUILDRULES)
+
+install: default
+ifneq ($(LIBTARGET),)
+ $(INSTALL) -m 755 $(LIBTARGET) $(PCP_LIB_DIR)/$(LIBTARGET)
+endif
+ifneq ($(SYMTARGET),)
+ for tt in $(SYMTARGET); do \
+ $(INSTALL) -S $(LIBTARGET) $(PCP_LIB_DIR)/$$tt || exit 1; \
+ done
+endif
+ifneq ($(STATICLIBTARGET),)
+ $(INSTALL) -m 755 $(STATICLIBTARGET) $(PCP_LIB_DIR)/$(STATICLIBTARGET)
+endif
+
+default_pcp: default
+
+install_pcp: install
+
+ifneq ($(LIBTARGET),)
+$(LIBTARGET): $(VERSION_SCRIPT)
+endif
diff --git a/src/libpcp_gui/src/exports b/src/libpcp_gui/src/exports
new file mode 100644
index 0000000..c7d2eef
--- /dev/null
+++ b/src/libpcp_gui/src/exports
@@ -0,0 +1,19 @@
+PCP_GUI_2.0 {
+ global:
+ pmRecordSetup;
+ pmRecordControl;
+ pmRecordAddHost;
+
+ pmTimeConnect;
+ pmTimeDisconnect;
+ pmTimeRecv;
+ pmTimeSendAck;
+ pmTimeShowDialog;
+ pmTimeStateAck;
+ pmTimeStateBounds;
+ pmTimeStateMode;
+ pmTimeStateSetup;
+ pmTimeStateVector;
+
+ local: *;
+};
diff --git a/src/libpcp_gui/src/record.c b/src/libpcp_gui/src/record.c
new file mode 100644
index 0000000..3a388e8
--- /dev/null
+++ b/src/libpcp_gui/src/record.c
@@ -0,0 +1,758 @@
+/*
+ * Copyright (c) 1995-2001,2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+#include "pmapi.h"
+#include "pmafm.h"
+#include "impl.h"
+#include <sys/stat.h>
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+/*
+ * some extended state, make sure these values are different to
+ * the PM_REC_* macros in <pmapi.h>
+ */
+#define PM_REC_BEGIN 81
+#define PM_REC_HOST 82
+
+static int state = PM_REC_OFF; /* where you are up to ... */
+static char *dir; /* directory containing Archive Folio files */
+static char *base; /* unique basename */
+static FILE *f_folio; /* Archive Folio goes here */
+static FILE *f_replay; /* current replay config file */
+static int _replay; /* can replay? */
+static char *_folio; /* remember the folio name */
+static char *_creator; /* remember the creator name */
+static int _seendefault; /* seen default host? */
+
+typedef struct _record {
+ struct _record *next;
+ int state;
+ char *host; /* host name */
+ int isdefault; /* is this the default host? */
+ pmRecordHost public; /* exposed to the caller */
+ char *base; /* archive base */
+ char *logfile; /* for -l ... not to be confused */
+ /* with public.logfile which is the */
+ /* full pathname */
+ char *config; /* for -c */
+ int argc;
+ char **argv;
+} record_t;
+
+static record_t *record;
+static int n_record;
+#ifndef IS_MINGW /* not yet ported */
+static int n_alive;
+#endif
+static char tbuf[MAXPATHLEN]; /* used for mktemp(), messages, ... */
+
+/*
+ * initialize, and return stdio stream for writing replay config
+ * (if any)
+ */
+FILE *
+pmRecordSetup(const char *folio, const char *creator, int replay)
+{
+ char *p;
+ char c;
+ time_t now;
+ int sts;
+ int fd = -1;
+ static char host[MAXHOSTNAMELEN];
+ char foliopath[MAXPATHLEN];
+ char *temp = NULL; /* for unlink() */
+ record_t *rp;
+ mode_t cur_umask;
+
+ if (state != PM_REC_OFF) {
+ /* already begun w/out end */
+ setoserror(EINVAL);
+ return NULL;
+ }
+
+ if (access(folio, F_OK) == 0) {
+ setoserror(EEXIST);
+ return NULL;
+ }
+
+ if (_folio != NULL)
+ free(_folio);
+ if ((_folio = strdup(folio)) == NULL)
+ return NULL;
+
+ if (_creator != NULL)
+ free(_creator);
+ if ((_creator = strdup(creator)) == NULL)
+ return NULL;
+
+ if ((f_folio = fopen(folio, "w")) == NULL)
+ return NULL;
+
+ dir = NULL;
+ base = NULL;
+
+ /*
+ * have folio file created ... get unique base string
+ */
+ tbuf[0] = '\0';
+ strcpy(foliopath, folio);
+ if ((p = strrchr(foliopath, '/')) != NULL) {
+ /* folio name contains a slash */
+ p++;
+ c = *p;
+ *p = '\0';
+ if (strcmp(foliopath, "./") != 0) {
+ if (dir != NULL)
+ free(dir);
+ if ((dir = strdup(foliopath)) == NULL)
+ goto failed;
+ strcpy(tbuf, dir);
+ strcat(tbuf, "/");
+ }
+ *p = c;
+ }
+ strcat(tbuf, "XXXXXX");
+#if HAVE_MKSTEMP
+ cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
+ if ((fd = mkstemp(tbuf)) < 0) {
+ umask(cur_umask);
+ goto failed;
+ }
+ umask(cur_umask);
+#else
+ if (mktemp(tbuf) == NULL)
+ goto failed;
+
+ cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
+ if ((fd = open(tbuf, O_CREAT | O_EXCL | O_RDWR, 0600)) < 0) {
+ umask(cur_umask);
+ goto failed;
+ }
+ umask(cur_umask);
+#endif
+ /*
+ * file named tbuf is never used, it is the basename for the real
+ * files we create. remember it so we can cleanup.
+ */
+ temp = strdup(tbuf);
+
+ if (dir == NULL)
+ p = tbuf;
+ else
+ p = strrchr(tbuf, '/') + 1;
+
+ if ((base = strdup(p)) == NULL)
+ goto failed;
+
+ /*
+ * folio preamble ...
+ */
+ fprintf(f_folio, "PCPFolio\nVersion: 1\n");
+ fprintf(f_folio, "# use pmafm(1) to process this PCP Archive Folio\n#\n");
+ time(&now);
+ (void)gethostname(host, MAXHOSTNAMELEN);
+ host[MAXHOSTNAMELEN-1] = '\0';
+ fprintf(f_folio, "Created: on %s at %s", host, ctime(&now));
+ fprintf(f_folio, "Creator: %s", creator);
+ if (replay)
+ fprintf(f_folio, " %s", base);
+ fprintf(f_folio, "\n# Host Basename\n#\n");
+
+ _replay = replay;
+ if (_replay) {
+ f_replay = fdopen(fd, "w");
+ if (f_replay == NULL)
+ goto failed;
+ }
+ else
+ f_replay = fopen("/dev/null", "r");
+
+ n_record = 0;
+ for (rp = record; rp != NULL; rp = rp->next) {
+ rp->state = PM_REC_BEGIN;
+ rp->isdefault = 0;
+ if (rp->host != NULL) {
+ free(rp->host);
+ rp->host = NULL;
+ }
+ if (rp->base != NULL) {
+ free(rp->base);
+ rp->base = NULL;
+ }
+ if (rp->logfile != NULL) {
+ free(rp->logfile);
+ rp->logfile = NULL;
+ }
+ if (rp->config != NULL) {
+ free(rp->config);
+ rp->config = NULL;
+ }
+ if (rp->argv != NULL) {
+ free(rp->argv);
+ rp->argv = NULL;
+ }
+ rp->argc = 0;
+ if (rp->public.f_config != NULL) {
+ fclose(rp->public.f_config);
+ rp->public.f_config = NULL;
+ }
+ if (rp->public.fd_ipc != -1) {
+ close(rp->public.fd_ipc);
+ rp->public.fd_ipc = -1;
+ }
+ if (rp->public.logfile != NULL) {
+ free(rp->public.logfile);
+ rp->public.logfile = NULL;
+ }
+ rp->public.pid = (pid_t)0;
+ rp->public.status = -1;
+ }
+
+ state = PM_REC_BEGIN;
+ _seendefault = 0;
+
+ if (temp) {
+ unlink(temp);
+ free(temp);
+ }
+ return f_replay;
+
+failed:
+ sts = oserror();
+ if (dir != NULL)
+ free(dir);
+ if (base != NULL)
+ free(base);
+ unlink(folio);
+ fclose(f_folio);
+ f_folio = NULL;
+ if (fd >= 0)
+ close(fd);
+ setoserror(sts);
+ if (temp) {
+ unlink(temp);
+ free(temp);
+ }
+ return NULL;
+}
+
+/*
+ * need to log another host in this folio ... must come here
+ * at least once, but may be more than once before the recording
+ * commences
+ */
+int
+pmRecordAddHost(const char *host, int isdefault, pmRecordHost **rhp)
+{
+ char *p;
+ int c;
+ int sts;
+ record_t *rp;
+
+ *rhp = NULL; /* in case of errors */
+
+ if (state != PM_REC_BEGIN && state != PM_REC_HOST)
+ /* botched order of calls ... */
+ return -EINVAL;
+
+ if (isdefault && _seendefault)
+ /* only one default allowed per session! */
+ return -EINVAL;
+
+ for (rp = record; rp != NULL; rp = rp->next) {
+ if (rp->state == PM_REC_BEGIN)
+ break;
+ }
+ if (rp == NULL) {
+ /* need another one */
+ if ((rp = (record_t *)malloc(sizeof(record_t))) == NULL)
+ return -oserror();
+ rp->next = record;
+ record = rp;
+ rp->isdefault = 0;
+ rp->host = NULL;
+ rp->base = NULL;
+ rp->logfile = NULL;
+ rp->config = NULL;
+ rp->argv = NULL;
+ rp->argc = 0;
+ rp->public.f_config = NULL;
+ rp->public.fd_ipc = -1;
+ rp->public.logfile = NULL;
+ rp->public.pid = (pid_t)0;
+ rp->public.status = -1;
+ }
+
+ rp->isdefault = isdefault;
+
+ if (dir != NULL)
+ strcpy(tbuf, dir);
+ else
+ tbuf[0] = '\0';
+ strcat(tbuf, base);
+ p = &tbuf[strlen(tbuf)];
+ strcat(tbuf, ".");
+ strcat(tbuf, host);
+ strcat(tbuf, ".config");
+ c = '\0';
+ if (access(tbuf, F_OK) == 0) {
+ p[0] = 'a';
+ p[1] = '.';
+ p[2] = '\0';
+ strcat(p, host);
+ strcat(p, ".config");
+ while (p[0] <= 'z') {
+ if (access(tbuf, F_OK) != 0) {
+ c = p[0];
+ break;
+ }
+ p[0]++;
+ }
+ if (c == '\0') {
+ setoserror(EEXIST);
+ goto failed;
+ }
+ }
+
+ if ((rp->host = strdup(host)) == NULL)
+ goto failed;
+
+ if ((rp->public.f_config = fopen(tbuf, "w")) == NULL)
+ goto failed;
+
+ if ((rp->base = malloc(strlen(base)+1+strlen(host)+3)) == NULL)
+ goto failed;
+ strcpy(rp->base, base);
+ p = &rp->base[strlen(rp->base)];
+ if (c != '\0') {
+ *p++ = c;
+ }
+ *p++ = '.';
+ *p = '\0';
+ strcat(p, host);
+
+ if ((rp->logfile = malloc(strlen(rp->base)+5)) == NULL)
+ goto failed;
+ strcpy(rp->logfile, rp->base);
+ strcat(rp->logfile, ".log");
+
+ if ((rp->config = malloc(strlen(rp->base)+8)) == NULL)
+ goto failed;
+ strcpy(rp->config, rp->base);
+ strcat(rp->config, ".config");
+
+ /* construct full pathname */
+ rp->public.logfile = malloc(MAXPATHLEN);
+ if (rp->public.logfile != NULL) {
+ int sep = __pmPathSeparator();
+ if (dir != NULL && __pmAbsolutePath(dir))
+ strcpy(rp->public.logfile, dir);
+ else {
+ if (getcwd(rp->public.logfile, MAXPATHLEN) == NULL)
+ goto failed;
+ }
+
+ sts = strlen(rp->public.logfile);
+ if (rp->public.logfile[sts - 1] != sep) {
+ rp->public.logfile[sts] = sep;
+ rp->public.logfile[sts+1] = '\0';
+ }
+ strcat(rp->public.logfile, rp->logfile);
+ }
+ else {
+ /* malloc failure ... */
+ goto failed;
+ }
+
+ n_record++;
+ state = rp->state = PM_REC_HOST;
+ *rhp = &rp->public;
+
+ return 0;
+
+failed:
+ sts = -oserror();
+ if (rp->public.f_config != NULL) {
+ unlink(tbuf);
+ fclose(rp->public.f_config);
+ rp->public.f_config = NULL;
+ }
+ if (rp->host != NULL)
+ free(rp->host);
+ if (rp->base != NULL)
+ free(rp->base);
+ if (rp->logfile != NULL)
+ free(rp->logfile);
+ if (rp->config != NULL)
+ free(rp->config);
+ *rhp = NULL;
+ return sts;
+}
+
+#ifndef IS_MINGW /* not yet ported */
+/*
+ * simple control protocol between here and pmlogger
+ * - only write from app to pmlogger
+ * - no ack
+ * - commands are
+ * V<number>\n - version
+ * F<folio>\n - folio name
+ * P<name>\n - launcher's name
+ * R[<msg>]\n - launcher knows how to replay
+ * D[<msg>]\n - detach from launcher
+ * Q[<msg>]\n - quit pmlogger
+ * ?[<msg>]\n - display session status
+ */
+static int
+xmit_to_logger(int fd, char tag, const char *msg)
+{
+ int sts;
+#ifdef HAVE_SIGPIPE
+ SIG_PF user_onpipe;
+#endif
+
+ if (fd < 0)
+ return PM_ERR_IPC;
+
+#ifdef HAVE_SIGPIPE
+ user_onpipe = signal(SIGPIPE, SIG_IGN);
+#endif
+ sts = (int)write(fd, &tag, 1);
+ if (sts != 1)
+ goto fail;
+
+ if (msg != NULL) {
+ int len = (int)strlen(msg);
+ sts = (int)write(fd, msg, len);
+ if (sts != len)
+ goto fail;
+ }
+
+ sts = (int)write(fd, "\n", 1);
+ if (sts != 1)
+ goto fail;
+
+#ifdef HAVE_SIGPIPE
+ signal(SIGPIPE, user_onpipe);
+#endif
+ return 0;
+
+fail:
+ if (oserror() == EPIPE)
+ sts = PM_ERR_IPC;
+ else
+ sts = -oserror();
+#ifdef HAVE_SIGPIPE
+ signal(SIGPIPE, user_onpipe);
+#endif
+ return sts;
+}
+#endif
+
+int
+pmRecordControl(pmRecordHost *rhp, int request, const char *msg)
+{
+#ifndef IS_MINGW /* not yet ported */
+ pid_t pid;
+ record_t *rp;
+ int sts;
+ int ok;
+ int cmd;
+ int mypipe[2];
+ static int maxseenfd = -1;
+
+ /*
+ * harvest old and smelly pmlogger instances
+ */
+ while ((pid = waitpid(-1, &sts, WNOHANG)) > 0) {
+ for (rp = record; rp != NULL; rp = rp->next) {
+ if (pid == rp->public.pid) {
+ rp->public.status = sts;
+ if (rp->public.fd_ipc != -1) {
+ close(rp->public.fd_ipc);
+ rp->public.fd_ipc = -1;
+ }
+ break;
+ }
+ }
+ }
+
+ sts = 0;
+
+ switch (request) {
+
+ case PM_REC_ON:
+ if (state != PM_REC_HOST || rhp != NULL) {
+ sts = -EINVAL;
+ break;
+ }
+
+ for (rp = record; rp != NULL; rp = rp->next) {
+ if (rp->state != PM_REC_HOST)
+ continue;
+ if (rp->isdefault) {
+ fprintf(f_folio, "%-15s %-23s %s\n", "Archive:", rp->host, rp->base);
+ break;
+ }
+ }
+
+ for (rp = record; rp != NULL; rp = rp->next) {
+ if (rp->state != PM_REC_HOST || rp->isdefault)
+ continue;
+ fprintf(f_folio, "%-15s %-23s %s\n", "Archive:", rp->host, rp->base);
+ }
+
+ fflush(stderr);
+ fflush(stdout);
+ if (_replay)
+ fflush(f_replay);
+ fflush(f_folio);
+
+ for (rp = record; rp != NULL; rp = rp->next) {
+ if (rp->state != PM_REC_HOST)
+ continue;
+ fclose(rp->public.f_config);
+ rp->public.f_config = NULL;
+ if (pipe1(mypipe) < 0) {
+ sts = -oserror();
+ break;
+ }
+ if (mypipe[1] > maxseenfd)
+ maxseenfd = mypipe[1];
+ if (mypipe[0] > maxseenfd)
+ maxseenfd = mypipe[0];
+
+ if ((rp->public.pid = fork()) == 0) {
+ /* pmlogger */
+ char loggerpath[MAXPATHLEN];
+ char fdnum[4];
+ int fd;
+ int i;
+
+ close(mypipe[1]);
+ snprintf(fdnum, sizeof(fdnum), "%d", mypipe[0]);
+ if (dir != NULL) {
+ /* trim trailing separator */
+ dir[strlen(dir)-1] = '\0';
+ if (chdir(dir) < 0) {
+ /* not good! */
+ exit(1);
+ }
+ }
+ /*
+ * leave stdin, tie stdout and stderr together, leave
+ * the ipc fd and close all other fds
+ */
+ dup2(2, 1); /* stdout -> stderr */
+ for (fd = 3; fd <= maxseenfd; fd++) {
+ if (fd != mypipe[0])
+ close(fd);
+ }
+ /*
+ * have:
+ * argv[0] ... argv[argc-1] from PM_REC_SETARG
+ * want:
+ * argv[0] "pmlogger"
+ * argv[1] ... argv[argc] from PM_REC_SETARG
+ * argv[argc+1], argv[argc+2] "-x" fdnum
+ * argv[argc+3], argv[argc+4] "-h" host
+ * argv[argc+5], argv[argc+6] "-l" log
+ * argv[argc+7], argv[argc+8] "-c" config
+ * argv[argc+9] basename
+ * argv[argc+10] NULL
+ */
+ rp->argv = (char **)realloc(rp->argv, (rp->argc+11)*sizeof(rp->argv[0]));
+ if (rp->argv == NULL) {
+ __pmNoMem("pmRecordControl: argv[]", (rp->argc+11)*sizeof(rp->argv[0]), PM_FATAL_ERR);
+ /*NOTREACHED*/
+ }
+ for (i = rp->argc; i > 0; i--)
+ rp->argv[i] = rp->argv[i-1];
+ rp->argv[0] = "pmlogger";
+ i = rp->argc+1;
+ rp->argv[i++] = "-x";
+ rp->argv[i++] = fdnum;
+ rp->argv[i++] = "-h";
+ rp->argv[i++] = rp->host;
+ rp->argv[i++] = "-l";
+ rp->argv[i++] = rp->logfile;
+ rp->argv[i++] = "-c";
+ rp->argv[i++] = rp->config;
+ rp->argv[i++] = rp->base;
+ rp->argv[i++] = NULL;
+#if DESPERATE
+fprintf(stderr, "Launching pmlogger:");
+for (i = 0; i < rp->argc+11; i++) fprintf(stderr, " %s", rp->argv[i]);
+fputc('\n', stderr);
+#endif
+ snprintf(loggerpath, sizeof(loggerpath), "%s%cpmlogger",
+ pmGetConfig("PCP_BINADM_DIR"), __pmPathSeparator());
+ execv(loggerpath, rp->argv);
+
+ /* this is really bad! */
+ exit(1);
+ }
+ else if (rp->public.pid < (pid_t)0) {
+ sts = -oserror();
+ break;
+ }
+ else {
+ /* the application launching pmlogger */
+ close(mypipe[0]);
+ rp->public.fd_ipc = mypipe[1];
+ /* send the protocol version */
+ ok = xmit_to_logger(rp->public.fd_ipc, 'V', "0");
+ if (ok < 0) {
+ /* remember last failure */
+ sts = ok;
+ rp->public.fd_ipc = -1;
+ continue;
+ }
+ /* send the folio name */
+ ok = xmit_to_logger(rp->public.fd_ipc, 'F', _folio);
+ if (ok < 0) {
+ /* remember last failure */
+ sts = ok;
+ rp->public.fd_ipc = -1;
+ continue;
+ }
+ /* send the my name */
+ ok = xmit_to_logger(rp->public.fd_ipc, 'P', _creator);
+ if (ok < 0) {
+ /* remember last failure */
+ sts = ok;
+ rp->public.fd_ipc = -1;
+ continue;
+ }
+ /* do we know how to replay? */
+ if (_replay) {
+ ok = xmit_to_logger(rp->public.fd_ipc, 'R', NULL);
+ if (ok < 0) {
+ /* remember last failure */
+ sts = ok;
+ rp->public.fd_ipc = -1;
+ continue;
+ }
+ }
+ }
+ rp->state = PM_REC_ON;
+ }
+
+ if (sts < 0) {
+ for (rp = record; rp != NULL; rp = rp->next) {
+ if (rp->public.fd_ipc >= 0) {
+ close(rp->public.fd_ipc);
+ rp->public.fd_ipc = -1;
+ }
+ }
+ }
+ else {
+ if (_replay) {
+ fclose(f_replay);
+ f_replay = NULL;
+ }
+ fclose(f_folio);
+ f_folio = NULL;
+ state = PM_REC_ON;
+ n_alive = n_record;
+ }
+ break;
+
+ case PM_REC_STATUS:
+ cmd = '?';
+ goto broadcast;
+
+ case PM_REC_OFF:
+ cmd = 'Q';
+ goto broadcast;
+
+ case PM_REC_DETACH:
+ cmd = 'D';
+
+broadcast:
+ if (state != PM_REC_ON) {
+ sts = -EINVAL;
+ break;
+ }
+
+ for (rp = record; rp != NULL; rp = rp->next) {
+ if (rhp != NULL && rhp != &rp->public)
+ continue;
+ if (rp->state != PM_REC_ON) {
+ if (rhp != NULL)
+ /* explicit pmlogger, should be "on" */
+ sts = -EINVAL;
+ continue;
+ }
+ if (rp->public.fd_ipc >= 0) {
+ ok = xmit_to_logger(rp->public.fd_ipc, cmd, msg);
+ if (ok < 0) {
+ /* remember last failure */
+ sts = ok;
+ rp->public.fd_ipc = -1;
+ }
+ }
+ else
+ sts = PM_ERR_IPC;
+
+ if (cmd != '?') {
+ n_alive--;
+ rp->state = PM_REC_OFF;
+ if (rp->public.fd_ipc != -1) {
+ close(rp->public.fd_ipc);
+ rp->public.fd_ipc = -1;
+ }
+ }
+ }
+
+ if (n_alive <= 0)
+ state = PM_REC_OFF;
+
+ break;
+
+ case PM_REC_SETARG:
+ if (state != PM_REC_HOST) {
+ sts = -EINVAL;
+ break;
+ }
+
+ for (rp = record; rp != NULL; rp = rp->next) {
+ if (rhp != NULL && rhp != &rp->public)
+ continue;
+ rp->argv = (char **)realloc(rp->argv, (rp->argc+1)*sizeof(rp->argv[0]));
+ if (rp->argv == NULL) {
+ sts = -oserror();
+ rp->argc = 0;
+ }
+ else {
+ rp->argv[rp->argc] = strdup(msg);
+ if (rp->argv[rp->argc] == NULL)
+ sts = -oserror();
+ else
+ rp->argc++;
+ }
+ }
+ break;
+
+ default:
+ sts = -EINVAL;
+ break;
+ }
+
+ return sts;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/src/libpcp_gui/src/timeclient.c b/src/libpcp_gui/src/timeclient.c
new file mode 100644
index 0000000..1046e3f
--- /dev/null
+++ b/src/libpcp_gui/src/timeclient.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2012-2013 Red Hat.
+ * Copyright (c) 2006 Aconex. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+#include "pmapi.h"
+#include "impl.h"
+#include "pmtime.h"
+
+static int
+pmServerExec(int fd, int livemode)
+{
+ char portname[32];
+ int port, in, out;
+ char *argv[] = { "pmtime", NULL, NULL };
+
+ if (livemode)
+ argv[1] = "-h"; /* -h for live hosts */
+ else
+ argv[1] = "-a"; /* -a for archives */
+
+ if (__pmProcessCreate(argv, &in, &out) < (pid_t)0) {
+ __pmCloseSocket(fd);
+ return -1;
+ }
+
+ if (read(in, &portname, sizeof(portname)) < 0)
+ port = -1;
+ else if (sscanf(portname, "port=%d", &port) != 1) {
+ setoserror(EPROTO);
+ port = -1;
+ }
+ close(in);
+ close(out);
+ if (port == -1)
+ __pmCloseSocket(fd);
+ return port;
+}
+
+static int
+pmConnectHandshake(int fd, int port, pmTime *pkt)
+{
+ __pmSockAddr *myaddr;
+ char buffer[4096];
+ pmTime *ack;
+ int sts;
+
+ /*
+ * Connect to pmtime - pmtime guaranteed started by now, due to the
+ * port number read(2) earlier, or -p option (so no race there).
+ */
+ if ((myaddr = __pmSockAddrAlloc()) == NULL) {
+ setoserror(ENOMEM);
+ goto error;
+ }
+
+ __pmSockAddrInit(myaddr, AF_INET, INADDR_LOOPBACK, port);
+ if ((sts = __pmConnect(fd, (void *)myaddr, __pmSockAddrSize())) < 0) {
+ setoserror(neterror());
+ goto error;
+ }
+ __pmSockAddrFree(myaddr);
+ myaddr = NULL;
+
+ /*
+ * Write the packet, then wait for an ACK.
+ */
+ sts = __pmSend(fd, (const void *)pkt, pkt->length, 0);
+ if (sts < 0) {
+ setoserror(neterror());
+ goto error;
+ } else if (sts != pkt->length) {
+ setoserror(EMSGSIZE);
+ goto error;
+ }
+ ack = (pmTime *)buffer;
+ sts = __pmRecv(fd, buffer, sizeof(buffer), 0);
+ if (sts < 0) {
+ setoserror(neterror());
+ goto error;
+ } else if (sts != ack->length) {
+ setoserror(EMSGSIZE);
+ goto error;
+ } else if (ack->command != PM_TCTL_ACK) {
+ setoserror(EPROTO);
+ goto error;
+ } else if (ack->source != pkt->source) {
+ setoserror(ENOSYS);
+ goto error;
+ }
+ return 0;
+
+error:
+ if (myaddr)
+ __pmSockAddrFree(myaddr);
+ __pmCloseSocket(fd);
+ return -1;
+}
+
+int
+pmTimeConnect(int port, pmTime *pkt)
+{
+ int fd;
+
+ if ((fd = __pmCreateSocket()) < 0)
+ return -1;
+ if (port < 0) {
+ if ((port = pmServerExec(fd, pkt->source != PM_SOURCE_ARCHIVE)) < 0)
+ return -2;
+ if (pmConnectHandshake(fd, port, pkt) < 0)
+ return -3;
+ } else { /* attempt to connect to the given port (once) */
+ if (pmConnectHandshake(fd, port, pkt) < 0)
+ return -4;
+ }
+ return fd;
+}
+
+int
+pmTimeDisconnect(int fd)
+{
+ if (fd < 0) {
+ setoserror(EINVAL);
+ return -1;
+ }
+ __pmCloseSocket(fd);
+ return 0;
+}
+
+int
+pmTimeSendAck(int fd, struct timeval *tv)
+{
+ pmTime data;
+ int sts;
+
+ memset(&data, 0, sizeof(data));
+ data.magic = PMTIME_MAGIC;
+ data.length = sizeof(data);
+ data.command = PM_TCTL_ACK;
+ data.position = *tv;
+ sts = __pmSend(fd, (const void *)&data, sizeof(data), 0);
+ if (sts < 0)
+ setoserror(neterror());
+ return sts;
+}
+
+int
+pmTimeShowDialog(int fd, int show)
+{
+ pmTime data;
+ int sts;
+
+ memset(&data, 0, sizeof(data));
+ data.magic = PMTIME_MAGIC;
+ data.length = sizeof(data);
+ data.command = show ? PM_TCTL_GUISHOW : PM_TCTL_GUIHIDE;
+ sts = __pmSend(fd, (const void *)&data, sizeof(data), 0);
+ if (sts >= 0 && sts != sizeof(data)) {
+ setoserror(EMSGSIZE);
+ sts = -1;
+ } else if (sts < 0)
+ setoserror(neterror());
+ return sts;
+}
+
+int
+pmTimeRecv(int fd, pmTime **datap)
+{
+ pmTime *k = *datap;
+ int sts, remains;
+
+ memset(k, 0, sizeof(pmTime));
+ sts = __pmRecv(fd, (void *)k, sizeof(pmTime), 0);
+ if (sts >= 0 && sts != sizeof(pmTime)) {
+ setoserror(EMSGSIZE);
+ sts = -1;
+ } else if (sts < 0) {
+ setoserror(neterror());
+ } else if (k->length > sizeof(pmTime)) { /* double dipping */
+ remains = k->length - sizeof(pmTime);
+ *datap = k = realloc(k, k->length);
+ sts = __pmRecv(fd, (char *)k + sizeof(pmTime), remains, 0);
+ if (sts >= 0 && sts != remains) {
+ setoserror(E2BIG);
+ sts = -1;
+ } else if (sts < 0) {
+ setoserror(neterror());
+ }
+ }
+ if (sts < 0)
+ return sts;
+ return k->command;
+}
diff --git a/src/libpcp_gui/src/timestate.c b/src/libpcp_gui/src/timestate.c
new file mode 100644
index 0000000..7314183
--- /dev/null
+++ b/src/libpcp_gui/src/timestate.c
@@ -0,0 +1,307 @@
+/*
+ * Time control functions for pmval
+ *
+ * Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2008-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 "pmtime.h"
+#include "pmapi.h"
+#include "impl.h"
+
+static enum {
+ START = -1,
+ STANDBY = 0,
+ FORW = 1,
+ BACK = 2,
+ MOVING = 3,
+ RESTART = 4,
+ ENDLOG = 5,
+} state;
+
+/*
+ * a : b for struct timevals ... <0 for a<b, ==0 for a==b, >0 for a>b
+ */
+static int
+__pmtimevalCmp(struct timeval *a, struct timeval *b)
+{
+ int res = (int)(a->tv_sec - b->tv_sec);
+
+ if (res == 0)
+ res = (int)(a->tv_usec - b->tv_usec);
+
+ return res;
+}
+
+static void timeControlExited()
+{
+ fprintf(stderr, "\n%s: Time Control dialog exited, goodbye.\n", pmProgname);
+}
+
+static void timeControlRewind()
+{
+ printf("\n[Time Control] Rewind/Reverse ...\n");
+}
+
+static void timeControlPosition(struct timeval position)
+{
+ printf("\n[Time Control] Repositioned in archive ...\n");
+}
+
+static void timeControlInterval(struct timeval delta)
+{
+ printf("new interval: %1.2f sec\n", __pmtimevalToReal(&delta));
+}
+
+static void timeControlResume()
+{
+ printf("\n[Time Control] Resume ...\n");
+}
+
+static void timeControlBounds()
+{
+ printf("\n[Time Control] End of Archive ...\n");
+}
+
+static void timeControlStepped(struct timeval delta)
+{
+}
+
+static void timeControlNewZone(char *timezone, char *label)
+{
+ int sts = pmNewZone(timezone);
+
+ if (sts < 0)
+ fprintf(stderr, "%s: Warning: cannot set timezone to \"%s\": %s\n",
+ pmProgname, timezone, pmErrStr(sts));
+ else
+ printf("new timezone: %s (%s)\n", timezone, label);
+}
+
+/*
+ * Get Extended Time Base interval and Units from a timeval
+ * in order to set archive mode.
+ */
+void
+pmTimeStateMode(int mode, struct timeval delta, struct timeval *position)
+{
+ const int SECS_IN_24_DAYS = 2073600;
+ int step, sts;
+
+ if (delta.tv_sec > SECS_IN_24_DAYS) {
+ step = delta.tv_sec;
+ mode |= PM_XTB_SET(PM_TIME_SEC);
+ } else {
+ step = delta.tv_sec * 1e3 + delta.tv_usec / 1e3;
+ mode |= PM_XTB_SET(PM_TIME_MSEC);
+ }
+
+ if ((sts = pmSetMode(mode, position, step)) < 0) {
+ fprintf(stderr, "%s: pmSetMode: %s\n", pmProgname, pmErrStr(sts));
+ exit(EXIT_FAILURE);
+ }
+}
+
+pmTime *
+pmTimeStateSetup(
+ pmTimeControls *timecontrols, int ctxt, int port,
+ struct timeval delta, struct timeval position,
+ struct timeval first, struct timeval last, char *tz, char *tz_label)
+{
+ pmTime *pmtime = malloc(sizeof(pmTime));
+ int fd, sts, tzlen;
+
+ pmtime->magic = PMTIME_MAGIC;
+ pmtime->length = sizeof(pmTime);
+ pmtime->command = PM_TCTL_SET;
+ pmtime->delta = delta;
+
+ if (ctxt == PM_CONTEXT_ARCHIVE) {
+ pmtime->source = PM_SOURCE_ARCHIVE;
+ pmtime->position = position;
+ pmtime->start = first;
+ pmtime->end = last;
+ } else {
+ pmtime->source = PM_SOURCE_HOST;
+ __pmtimevalNow(&pmtime->position);
+ }
+ if (tz == NULL) {
+ char tzbuf[PM_TZ_MAXLEN];
+ tz = __pmTimezone_r(tzbuf, sizeof(tzbuf));
+ if (ctxt == PM_CONTEXT_ARCHIVE) {
+ if ((sts = pmNewZone(tz)) < 0) {
+ fprintf(stderr, "%s: Cannot set timezone to \"%s\": %s\n",
+ pmProgname, tz, pmErrStr(sts));
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ tzlen = strlen(tz) + 1;
+ if (tz_label == NULL)
+ tz_label = "localhost";
+ pmtime->length += tzlen + strlen(tz_label) + 1;
+ pmtime = realloc(pmtime, pmtime->length);
+ if (!pmtime) {
+ fprintf(stderr, "%s: realloc: %s\n", pmProgname, osstrerror());
+ exit(EXIT_FAILURE);
+ }
+ strcpy(pmtime->data, tz);
+ strcpy(pmtime->data + tzlen, tz_label);
+ if ((fd = pmTimeConnect(port, pmtime)) < 0) {
+ fprintf(stderr, "%s: pmTimeConnect: %s\n", pmProgname, pmErrStr(fd));
+ exit(EXIT_FAILURE);
+ }
+
+ pmtime->length = sizeof(pmTime); /* reduce size to header only */
+ pmtime = realloc(pmtime, pmtime->length); /* cannot fail! */
+
+ /* setup default vectors */
+ timecontrols->resume = timeControlResume;
+ timecontrols->exited = timeControlExited;
+ timecontrols->rewind = timeControlRewind;
+ timecontrols->boundary = timeControlBounds;
+ timecontrols->position = timeControlPosition;
+ timecontrols->interval = timeControlInterval;
+ timecontrols->stepped = timeControlStepped;
+ timecontrols->newzone = timeControlNewZone;
+ timecontrols->context = ctxt;
+ timecontrols->showgui = 1;
+ timecontrols->delta = delta;
+ timecontrols->padding = 0;
+ timecontrols->fd = fd;
+ state = START;
+
+ return pmtime;
+}
+
+void
+pmTimeStateBounds(pmTimeControls *control, pmTime *pmtime)
+{
+ pmTimeStateAck(control, pmtime);
+ if (state != ENDLOG)
+ control->boundary();
+ state = ENDLOG;
+}
+
+void
+pmTimeStateAck(pmTimeControls *control, pmTime *pmtime)
+{
+ int sts = pmTimeSendAck(control->fd, &pmtime->position);
+
+ if (sts < 0) {
+ if (sts == -EPIPE)
+ control->exited();
+ else
+ fprintf(stderr, "\n%s: pmTimeSendAck: %s\n",
+ pmProgname, pmErrStr(sts));
+ exit(EXIT_FAILURE);
+ }
+}
+
+int
+pmTimeStateVector(pmTimeControls *control, pmTime *pmtime)
+{
+ int cmd, fetch = 0;
+
+ if (control->showgui) {
+ pmTimeShowDialog(control->fd, 1);
+ control->showgui = 0;
+ }
+
+ do {
+ cmd = pmTimeRecv(control->fd, &pmtime);
+ if (cmd < 0) {
+ control->exited();
+ exit(EXIT_FAILURE);
+ }
+
+ switch (pmtime->command) {
+ case PM_TCTL_SET:
+ if (state == ENDLOG)
+ state = STANDBY;
+ else if (state == FORW)
+ state = RESTART;
+ break;
+
+ case PM_TCTL_STEP:
+ if (pmtime->state == PM_STATE_BACKWARD) {
+ if (state != BACK) {
+ control->rewind();
+ state = BACK;
+ }
+ }
+ else if (state != FORW && state != ENDLOG) {
+ if (control->context == PM_CONTEXT_ARCHIVE) {
+ if (state == STANDBY)
+ control->resume();
+ else if (state != START)
+ control->position(pmtime->position);
+ }
+ else if (state != START)
+ control->resume();
+ if (state != START &&
+ __pmtimevalCmp(&pmtime->delta, &control->delta) != 0)
+ control->interval(pmtime->delta);
+
+ if (control->context == PM_CONTEXT_ARCHIVE)
+ pmTimeStateMode(PM_MODE_INTERP,
+ pmtime->delta, &pmtime->position);
+ control->delta = pmtime->delta;
+ control->stepped(pmtime->delta);
+ state = FORW;
+ }
+
+ if (state == BACK || state == ENDLOG) {
+ /*
+ * for EOL and reverse travel, no fetch, so ACK here
+ */
+ pmTimeStateAck(control, pmtime);
+ break;
+ }
+ fetch = 1;
+ break;
+
+ case PM_TCTL_TZ:
+ control->newzone(pmtime->data,
+ pmtime->data + strlen(pmtime->data) + 1);
+ break;
+
+ case PM_TCTL_VCRMODE:
+ case PM_TCTL_VCRMODE_DRAG:
+ /* something has changed ... suppress reporting */
+ if (pmtime->command == PM_TCTL_VCRMODE_DRAG)
+ state = MOVING;
+ else if (pmtime->state == PM_STATE_STOP)
+ state = STANDBY;
+ else
+ state = RESTART;
+ break;
+
+ /*
+ * safely and silently ignore these
+ */
+ case PM_TCTL_GUISHOW:
+ case PM_TCTL_GUIHIDE:
+ case PM_TCTL_BOUNDS:
+ case PM_TCTL_ACK:
+ break;
+
+ default:
+ if (pmDebug & DBG_TRACE_TIMECONTROL)
+ fprintf(stderr, "pmTimeRecv: cmd %x?\n", cmd);
+ break;
+ }
+ } while (!fetch);
+
+ return 0;
+}