summaryrefslogtreecommitdiff
path: root/src/pmgadgets
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmgadgets')
-rw-r--r--src/pmgadgets/GNUmakefile71
-rw-r--r--src/pmgadgets/global.h27
-rw-r--r--src/pmgadgets/lex.l133
-rw-r--r--src/pmgadgets/main.cpp56
-rw-r--r--src/pmgadgets/parse.cpp1339
-rw-r--r--src/pmgadgets/pmgadgets-args.sh381
-rw-r--r--src/pmgadgets/pmgadgets.cpp55
-rw-r--r--src/pmgadgets/pmgadgets.h40
-rw-r--r--src/pmgadgets/pmgadgets.info.in18
-rw-r--r--src/pmgadgets/pmgadgets.pro23
-rw-r--r--src/pmgadgets/pmgadgets.qrc6
-rw-r--r--src/pmgadgets/pmgadgets.sh.IN2
-rwxr-xr-xsrc/pmgadgets/pmgcisco.sh251
-rwxr-xr-xsrc/pmgadgets/pmgcluster.sh281
-rwxr-xr-xsrc/pmgadgets/pmgshping.sh242
-rwxr-xr-xsrc/pmgadgets/pmgsys.py380
-rw-r--r--src/pmgadgets/tokens.h60
17 files changed, 3365 insertions, 0 deletions
diff --git a/src/pmgadgets/GNUmakefile b/src/pmgadgets/GNUmakefile
new file mode 100644
index 0000000..833bff7
--- /dev/null
+++ b/src/pmgadgets/GNUmakefile
@@ -0,0 +1,71 @@
+TOPDIR = ../..
+COMMAND = pmgadgets
+PROJECT = $(COMMAND).pro
+include $(TOPDIR)/src/include/builddefs
+
+WRAPPER = $(COMMAND).sh
+QRCFILE = $(COMMAND).qrc
+ICNFILE = $(COMMAND).icns
+XMLFILE = $(COMMAND).info
+HEADERS = pmgadgets.h tokens.h
+SOURCES = $(HEADERS:.h=.cpp) main.cpp parse.cpp
+CONFFILES = $(PROJECT)
+LDIRT = $(COMMAND) $(ICNFILE) $(WRAPPER) $(SCRIPTS) $(XMLFILE) *.yy.c images
+
+default: build-me
+
+include $(BUILDRULES)
+
+ifeq "$(ENABLE_QT)" "true"
+build-me:: images wrappers
+ $(QTMAKE)
+ $(LNMAKE)
+
+build-me:: $(SUBDIRS)
+ $(SUBDIRS_MAKERULE)
+
+ifeq ($(WINDOW),mac)
+PKG_MAC_DIR = /Library/PCP/$(COMMAND).app/Contents
+PKG_SUB_DIR = $(PKG_MAC_DIR)/MacOS
+wrappers: $(WRAPPER) $(SCRIPTS)
+else
+PKG_SUB_DIR = $(PKG_BIN_DIR)
+wrappers: $(WRAPPER) $(SCRIPTS)
+endif
+
+$(WRAPPER): $(WRAPPER).IN
+ @ $(SED) -e '/\# .*/b' -e 's;PKG_BIN_DIR;$(PKG_SUB_DIR);g' < $< > $@
+
+install: default
+ $(INSTALL) -m 755 -d $(PKG_BIN_DIR)
+ifneq ($(WINDOW),mac)
+ $(INSTALL) -m 755 $(BINARY) $(PKG_BIN_DIR)/$(COMMAND)
+endif
+ifeq ($(WINDOW),mac)
+ $(INSTALL) -m 755 $(WRAPPER) $(PKG_BIN_DIR)/$(COMMAND)
+ $(call INSTALL_DIRECTORY_HIERARCHY,$(PKG_MAC_DIR),/Library)
+ $(INSTALL) -m 644 $(XMLFILE) $(PKG_MAC_DIR)/Info.plist
+ $(INSTALL) -m 644 $(MACBUILD)/PkgInfo $(PKG_MAC_DIR)/PkgInfo
+ $(INSTALL) -m 755 -d $(PKG_MAC_DIR)/MacOS
+ $(call INSTALL_QT_FRAMEWORKS,$(BINARY))
+ $(INSTALL) -m 755 $(BINARY) $(PKG_MAC_DIR)/MacOS/$(COMMAND)
+ rm $(BINARY)
+ $(INSTALL) -m 755 -d $(PKG_MAC_DIR)/Resources
+ $(INSTALL) -m 644 $(ICNFILE) $(PKG_MAC_DIR)/Resources/$(ICNFILE)
+ $(call INSTALL_QT_RESOURCES,$(PKG_MAC_DIR)/Resources)
+endif
+
+else
+build-me:
+install:
+endif
+
+default_pcp: default
+
+install_pcp: install
+
+images: $(ICNFILE)
+ $(LN_S) $(TOPDIR)/images images
+
+$(ICNFILE):
+ $(LN_S) $(TOPDIR)/images/$(ICNFILE) $(ICNFILE)
diff --git a/src/pmgadgets/global.h b/src/pmgadgets/global.h
new file mode 100644
index 0000000..269d22b
--- /dev/null
+++ b/src/pmgadgets/global.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 1996-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 _GLOBAL_H
+#define _GLOBAL_H
+
+// Global data from resources/command line options
+struct AppData {
+ int zoom; // Zoom factor
+ double delta; // Update interval (seconds)
+ char * defaultFont; // Default font for label gadgets
+};
+
+extern AppData appData; // Global options/resources
+
+#endif /* _GLOBAL_H */
diff --git a/src/pmgadgets/lex.l b/src/pmgadgets/lex.l
new file mode 100644
index 0000000..52047b5
--- /dev/null
+++ b/src/pmgadgets/lex.l
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1996-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 <stdlib.h>
+#include <string.h>
+#include "tokens.h"
+
+extern unsigned lineCount;
+
+int dostring();
+
+#ifndef FLEX_SCANNER
+#ifdef output
+#undef output
+#define output(c)
+#endif
+#else
+#define YY_NO_UNPUT
+#endif
+
+%}
+
+%%
+
+[-]?[0-9]+ {
+ /*
+ * bigger than, 999,999,999 (or smaller than
+ * -99,999,999), let's force it to be a real, to
+ avoid overflow issues
+ */
+ if (strlen(yytext) > 9) {
+ tokenRealVal = atof(yytext);
+ return(TOK_REAL);
+ }
+ tokenIntVal = atoi(yytext);
+ return(TOK_INTEGER);
+ }
+[-]?[0-9]+\.[0-9]+ {
+ tokenRealVal = atof(yytext);
+ return(TOK_REAL);
+ }
+
+\( return TOK_LPAREN;
+\) return TOK_RPAREN;
+\[ return TOK_LBRACKET;
+\] return TOK_RBRACKET;
+\: return TOK_COLON;
+
+_line return TOK_LINE;
+_label return TOK_LABEL;
+_bar return TOK_BAR;
+_multibar return TOK_MULTIBAR;
+_bargraph return TOK_BARGRAPH;
+_led return TOK_LED;
+
+_legend return TOK_LEGEND;
+_colour return TOK_COLOUR;
+_color return TOK_COLOUR;
+_colourlist return TOK_COLOURLIST;
+_colorlist return TOK_COLOURLIST;
+_actions return TOK_ACTIONLIST;
+
+_update return TOK_UPDATE;
+_history return TOK_HISTORY;
+_noborder return TOK_NOBORDER;
+_metric return TOK_METRIC;
+_horizontal return TOK_HORIZONTAL;
+_vertical return TOK_VERTICAL;
+_metrics return TOK_METRICS;
+_min return TOK_MIN;
+_mainimum return TOK_MIN;
+_max return TOK_MAX;
+_maximum return TOK_MAX;
+_default return TOK_DEFAULT;
+_fixed return TOK_FIXED;
+
+_[a-zA-Z0-9_.] return TOK_BAD_RES_WORD;
+
+\# {
+ while (input() != '\n')
+ ;
+ nLines++;
+ }
+
+\"[^\"\n][^\"\n]*\" return dostring();
+
+\n nLines++;
+
+[A-Za-z][A-Za-z0-9_.\-]* {
+ tokenStringVal = strdup(yytext);
+ return TOK_IDENTIFIER;
+ }
+
+[0-9\-]+[A-Za-z_.\-]+[A-Za-z0-9.\-]* {
+ tokenStringVal = strdup(yytext);
+ return TOK_IDENTIFIER;
+ }
+
+[ \t]* { }
+%%
+
+int
+yywrap(void)
+{
+ return 1;
+}
+
+int
+dostring(void)
+{
+ size_t szResult;
+ char* result;
+
+ szResult = yyleng - 1; /* ignore 1st '"', 2nd clobbered by '\0' */
+ result = (char*)malloc(szResult);
+ strncpy(result, yytext + 1, szResult - 1);
+ result[szResult - 1] = '\0';
+ tokenStringVal = result;
+ return TOK_STRING;
+}
diff --git a/src/pmgadgets/main.cpp b/src/pmgadgets/main.cpp
new file mode 100644
index 0000000..e4e6f0f
--- /dev/null
+++ b/src/pmgadgets/main.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013, Red Hat.
+ *
+ * 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 <QApplication>
+#include "pmgadgets.h"
+#include "global.h"
+
+AppData appData; // Global options/resources
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ PmGadgets gadgets;
+ return gadgets.exec();
+}
+
+#if 0
+#include <QDesktopWidget>
+#include <QCursor>
+static char usage[] =
+ "Usage: pmgadgets [options] [message...]\n\n"
+ "Options:\n"
+ " -? | -help display this usage message\n"
+ " -header title set window title\n"
+ " -noslider do not display a text box slider\n"
+ " -noframe do not display a frame around the text box\n";
+
+ char *option;
+ char *filename = NULL;
+ char *defaultname = NULL;
+
+ int errflag = 0;
+ if (errflag) {
+ fprintf(stderr, "%s", usage);
+ exit(1);
+ }
+
+ if (nearmouseflag)
+ grid.move(QCursor::pos());
+ else if (centerflag) {
+ int x = (a.desktop()->screenGeometry().width() / 2) - (q.width() / 2);
+ int y = (a.desktop()->screenGeometry().height() / 2) - (q.height() / 2);
+ grid.move(x > 0 ? x : 0, y > 0 ? y : 0);
+ }
+#endif
diff --git a/src/pmgadgets/parse.cpp b/src/pmgadgets/parse.cpp
new file mode 100644
index 0000000..923baef
--- /dev/null
+++ b/src/pmgadgets/parse.cpp
@@ -0,0 +1,1339 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 1996 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 <pcp/pmapi.h>
+#include <pcp/impl.h>
+#include <setjmp.h>
+#include <values.h>
+#include "global.h"
+#include "tokens.h"
+
+#include "qed.h"
+#include "qed_bar.h"
+#include "qed_led.h"
+#include "qed_line.h"
+#include "qed_label.h"
+#include "qed_legend.h"
+#include "qed_gadget.h"
+#include "qed_colorlist.h"
+#include "qed_actionlist.h"
+#include <qnumeric.h>
+
+extern "C" {
+int yylex();
+}
+
+// Variables manipulated by the lexical analyser
+//
+unsigned nLines = 1;
+int tokenIntVal;
+double tokenRealVal;
+char *tokenStringVal;
+
+// Parser variables
+//
+static int token;
+static int eofOK = 1;
+static unsigned nErrors;
+static unsigned nWarnings;
+
+static QList<QedColorList*> colorLists;
+static QList<QedLegend*> legends;
+static QList<QedActionList*> actionLists;
+static QList<QedGadget*> gadgets;
+static QWidget *parent;
+
+class MetricData
+{
+public:
+ MetricData(char* h, char* m, char* i) { host = h; metric = m; inst = i; }
+ char *host; // NULL => localhost
+ char *metric; // metric name
+ char *inst; // instance name (may be NULL)
+};
+
+int AddMetrics(double a, QList<MetricData*>&b, QedGadget *c) { return 0; /* TODO */ }
+
+typedef struct {
+ int id;
+ const char* string;
+} TokenList;
+
+static TokenList tokenStrings[] = {
+ { TOK_LINE, "line" },
+ { TOK_LABEL, "label" },
+ { TOK_BAR, "bar" },
+ { TOK_MULTIBAR, "multibar" },
+ { TOK_BARGRAPH, "bargraph" },
+ { TOK_LED, "led" },
+
+ { TOK_LEGEND, "legend" },
+ { TOK_COLOURLIST, "colourlist" },
+ { TOK_ACTIONLIST, "actionlist" },
+
+ { TOK_BAD_RES_WORD, "<bad reserved word>" },
+ { TOK_UPDATE, "update" },
+ { TOK_METRIC, "metric" },
+ { TOK_HORIZONTAL, "horizontal" },
+ { TOK_VERTICAL, "vertical" },
+ { TOK_METRICS, "metrics" },
+ { TOK_MIN, "min" },
+ { TOK_MAX, "max" },
+ { TOK_DEFAULT, "default" },
+ { TOK_FIXED, "fixed" },
+ { TOK_COLOUR, "colour" },
+ { TOK_HISTORY, "history" },
+ { TOK_NOBORDER, "noborder" },
+
+ { TOK_IDENTIFIER, "<identifier>" },
+ { TOK_INTEGER, "<integer>" },
+ { TOK_REAL, "<real>" },
+ { TOK_STRING, "<string>" },
+ { TOK_LPAREN, "(" },
+ { TOK_RPAREN, ")" },
+ { TOK_LBRACKET, "[" },
+ { TOK_RBRACKET, "]" },
+ { TOK_COLON, ":" },
+
+ { TOK_EOF, "EOF" },
+ { 0, "raw EOF" },
+};
+const unsigned nTokenStrings = sizeof(tokenStrings) / sizeof(tokenStrings[0]);
+
+static const char*
+TokenToString(int tok)
+{
+ for (unsigned i = 0; i < nTokenStrings; i++)
+ if (tokenStrings[i].id == tok)
+ return tokenStrings[i].string;
+ return "???";
+}
+
+static jmp_buf scannerEofEnv;
+static int stashedToken = -1;
+
+static int
+NextToken()
+{
+ static int atEOF = 0;
+
+ if (atEOF)
+ return token;
+
+ if (stashedToken < 0) {
+ if ((token = yylex()) == 0) {
+ atEOF = 1;
+ token = TOK_EOF;
+ if (!eofOK) {
+ nErrors++;
+ pmprintf("Error, line %d: unexpected EOF, giving up\n", nLines);
+ longjmp(scannerEofEnv, 0);
+ }
+ }
+ }
+ else {
+ token = stashedToken;
+ stashedToken = -1;
+ }
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "input: %s (%d)\n", TokenToString(token), token);
+ return token;
+}
+
+static void
+FindNewStatement()
+{
+ eofOK = 1;
+ for(;;)
+ switch (token) {
+ case TOK_EOF:
+ case TOK_LINE:
+ case TOK_LABEL:
+ case TOK_BAR:
+ case TOK_MULTIBAR:
+ case TOK_BARGRAPH:
+ case TOK_LED:
+ case TOK_LEGEND:
+ case TOK_COLOURLIST:
+ return;
+
+ default:
+ NextToken();
+ break;
+ }
+}
+
+static int
+ParseUpdate(double& update, int eofMayFollow)
+{
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_INTEGER)
+ update = tokenIntVal;
+ else if (token == TOK_REAL)
+ update = tokenRealVal;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: update interval must be a number\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParseHistory(int& history, int eofMayFollow)
+{
+ int err = 0;
+
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_INTEGER) {
+ if ((history = tokenIntVal) < 0)
+ err = 1;
+ }
+ else
+ err = 1;
+
+ if (err) {
+ nErrors++;
+ pmprintf("Error, line %d: history must be a positive integer\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParsePosition(int& x, int& y, int eofMayFollow)
+{
+ eofOK = 0;
+ if (token == TOK_INTEGER)
+ x = tokenIntVal * appData.zoom;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: X coordinate must be an integer\n", nLines);
+ return -1;
+ }
+ NextToken();
+ if (token == TOK_INTEGER)
+ y = tokenIntVal * appData.zoom;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: Y coordinate must be an integer\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParseSize(unsigned& w, unsigned& h, int eofMayFollow)
+{
+ eofOK = 0;
+ if (token == TOK_INTEGER && tokenIntVal > 0)
+ w = tokenIntVal * appData.zoom;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: width must be a positive integer\n", nLines);
+ return -1;
+ }
+ NextToken();
+ if (token == TOK_INTEGER && tokenIntVal > 0)
+ h = tokenIntVal * appData.zoom;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: height must be a positive integer\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParseGeometry(int& x, int& y, unsigned& w, unsigned& h, int eofMayFollow)
+{
+ int sts;
+
+ eofOK = 0;
+ if ((sts = ParsePosition(x, y, 0)) < 0)
+ return sts;
+ sts = ParseSize(w, h, eofMayFollow);
+ return sts;
+}
+
+static int
+ParseActionListActions(QedActionList *alp, int eofMayFollow)
+{
+ unsigned int n = 0;
+
+ eofOK = 0;
+ NextToken();
+ while (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ if (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ alp->addName(tokenStringVal);
+ NextToken();
+ }
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: action name expected\n", nLines);
+ return -1;
+ }
+ if (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ alp->addAction(tokenStringVal);
+ NextToken();
+ }
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: action expected\n", nLines);
+ return -1;
+ }
+ if (token == TOK_DEFAULT) {
+ if (alp->defaultPos() == -1)
+ alp->setDefaultPos(n);
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: only one default action permitted in list\n", nLines);
+ }
+ NextToken();
+ }
+ n++;
+ }
+ eofOK = eofMayFollow;
+ if (token == TOK_RPAREN)
+ NextToken();
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: `)' expected at end of action list\n", nLines);
+ return -1;
+ }
+ return 0;
+}
+
+typedef enum {
+ AL_NEED_NAME = 1,
+ AL_MAY_BE_REFERENCE = 2
+} ActionListFlags;
+
+// This parses 3 types of action list:
+//
+// _actions name (...) definition of action list
+// _actions name reference to defined action list
+// _actions (...) anonymous action list
+//
+static int
+ParseActionList(
+ int flags, // AL_* flags above
+ int eofMayFollow)
+{
+ char *name;
+ char anon[64];
+ int ai, sts;
+ int existingActions = -1;
+ QedActionList *alp;
+ static int count;
+
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_IDENTIFIER) {
+ name = tokenStringVal;
+ for (ai = 0; ai < actionLists.size(); ai++) {
+ alp = actionLists.at(ai);
+ if (strcmp(alp->identity(), name) == 0)
+ break;
+ }
+ if (ai != actionLists.size())
+ existingActions = ai;
+ else
+ alp = new QedActionList(name);
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ else {
+ if (flags & AL_NEED_NAME) {
+ nErrors++;
+ pmprintf("Error, line %d: name expected for actions list\n", nLines);
+ return -1;
+ }
+ snprintf(anon, sizeof(anon), "anonymous#%d", count++);
+ alp = new QedActionList(anon);
+ name = anon;
+ }
+
+ sts = 0;
+ if (token == TOK_LPAREN) {
+ // This is an action list definition (the name may be omitted)
+ //
+ eofOK = 0;
+ if (existingActions != -1) {
+ nErrors++;
+ pmprintf("Error, line %d: an actions list named \"%s\" already exists\n",
+ nLines, name);
+ return -1;
+ }
+ sts = ParseActionListActions(alp, eofMayFollow);
+ }
+ else {
+ // This should be a reference to an already defined action list
+ //
+ if (name == NULL) {
+ nErrors++;
+ pmprintf("Error, line %d: name or ( expected for actions list\n", nLines);
+ return -1;
+ }
+ if (!(flags & AL_MAY_BE_REFERENCE)) {
+ nErrors++;
+ pmprintf("Error, line %d: `(' expected for actions list definition\n", nLines);
+ return -1;
+ }
+ if (existingActions == -1) {
+ nErrors++;
+ pmprintf("Error, line %d: no action list named %s defined\n",
+ nLines, name);
+ return -1;
+ }
+ }
+
+ if (sts >= 0 && existingActions == -1)
+ actionLists.append(alp);
+
+ return sts;
+}
+
+static int
+ParseLine(int eofMayFollow)
+{
+ int x, y, sts;
+ unsigned w, h;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParseGeometry(x, y, w, h, eofMayFollow);
+ if (sts < 0)
+ return sts;
+
+ if (token == TOK_UPDATE) {
+ nErrors++;
+ pmprintf("Error, line %d: lines may not have an update interval\n",
+ nLines);
+ return -1;
+ }
+
+ // Parse optional actions list
+ //
+ if (token == TOK_ACTIONLIST) {
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ QedLine *l = new QedLine(parent, x, y, w, h);
+ if (l) {
+ gadgets.append(l);
+ } else {
+ perror("no memory for line gadget");
+ exit(1);
+ }
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ l->dump(stderr);
+ return 0;
+}
+
+static int
+ParseLabel(int eofMayFollow)
+{
+ int x, y, sts;
+ char* text;
+ char* font = appData.defaultFont;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParsePosition(x, y, 0);
+ if (sts < 0)
+ return sts;
+
+ if (token == TOK_UPDATE) {
+ nErrors++;
+ pmprintf("Error, line %d: labels may not have an update interval\n",
+ nLines);
+ return -1;
+ }
+
+ if (token != TOK_STRING && token != TOK_IDENTIFIER) {
+ nErrors++;
+ pmprintf("Error, line %d: label string expected\n", nLines);
+ return -1;
+ }
+ text = tokenStringVal;
+ eofOK = eofMayFollow;
+ NextToken();
+ if (token == TOK_STRING || token == TOK_IDENTIFIER) {
+ font = tokenStringVal;
+ NextToken();
+ }
+
+ if (token == TOK_ACTIONLIST)
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+
+ if (sts >= 0) {
+ sts = 0;
+ // note: constructor specifies baseline pos
+ QedLabel* l = new QedLabel(parent, x, y, text, font);
+ if (l) {
+ gadgets.append(l);
+ } else {
+ perror("no memory for label gadget");
+ exit(1);
+ }
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ l->dump(stderr);
+ }
+
+ if (text)
+ free(text);
+ if (font && font != appData.defaultFont)
+ free(font);
+ return sts;
+}
+
+// A metric spec is one of
+//
+// <metric>
+// <metric>[<inst>]
+// <host>:<metric>
+// <host>:<metric>[<inst>]
+//
+static int
+ParseMetric(MetricData** resultPtr, int eofMayFollow)
+{
+ char* host;
+ char* metric;
+ char* inst;
+ char* hostOrMetric = NULL;
+
+ eofOK = 0;
+ if (token == TOK_IDENTIFIER)
+ hostOrMetric = tokenStringVal;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: performance metric expected\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ if (token == TOK_COLON) { // Have "<host>:<metric>"
+ host = hostOrMetric;
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_IDENTIFIER)
+ metric = tokenStringVal;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: performance metric expected\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ else { // Optional "<host>:" omitted
+ host = NULL;
+ metric = hostOrMetric;
+ }
+ if (token == TOK_LBRACKET) {
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_IDENTIFIER || token == TOK_STRING)
+ inst = tokenStringVal;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: performance metric instance expected\n",
+ nLines);
+ return -1;
+ }
+ NextToken();
+ if (token != TOK_RBRACKET) {
+ nErrors++;
+ pmprintf("Error, line %d: `]' for performance metric instance missing\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ else
+ inst = NULL;
+
+ *resultPtr = new MetricData(host, metric, inst);
+ if (*resultPtr == NULL) {
+ perror("No memory for metric spec");
+ exit(1);
+ }
+ return 0;
+}
+
+static int
+ParseColor(char** colourName, int eofMayFollow)
+{
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ *colourName = strdup(tokenStringVal);
+ if (*colourName == NULL) {
+ perror("No memory for color name");
+ exit(1);
+ }
+ }
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: color name missing\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParseBar(int eofMayFollow)
+{
+ int x, y, sts;
+ unsigned w, h;
+ MetricData* metricData;
+ double min = qQNaN(), max = qQNaN();
+ int isVertical = 0;
+ double update = appData.delta;
+ char* colour = NULL;
+ int fixMax = 0;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParseGeometry(x, y, w, h, eofMayFollow);
+ if (sts < 0)
+ return sts;
+
+ if (token == TOK_UPDATE) {
+ sts = ParseUpdate(update, 0);
+ if (sts < 0)
+ return sts;
+ }
+
+ if (token != TOK_METRIC) {
+ nErrors++;
+ pmprintf("Error, line %d: _metric expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ sts = ParseMetric(&metricData, eofMayFollow);
+ if (sts < 0)
+ return sts;
+
+ while (token == TOK_HORIZONTAL ||
+ token == TOK_VERTICAL ||
+ token == TOK_FIXED ||
+ token == TOK_MIN ||
+ token == TOK_MAX) {
+
+ if (token == TOK_FIXED) {
+ eofOK = 0;
+ NextToken();
+ if (token != TOK_MAX) {
+ nErrors++;
+ pmprintf("Error, line %d: _fixed _max expected\n", nLines);
+ return -1;
+ }
+ fixMax = 1;
+ }
+
+ if (token == TOK_HORIZONTAL) {
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ else if (token == TOK_VERTICAL) {
+ eofOK = eofMayFollow;
+ NextToken();
+ isVertical = 1;
+ }
+ else if (token == TOK_MIN) {
+ eofOK = 0;
+ NextToken();
+ if (token != TOK_INTEGER && token != TOK_REAL) {
+ nErrors++;
+ pmprintf("Error, line %d: minimum must be a number\n", nLines);
+ return -1;
+ }
+ min = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal;
+
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ else if (token == TOK_MAX) {
+ eofOK = 0;
+ NextToken();
+ if (token != TOK_INTEGER && token != TOK_REAL) {
+ nErrors++;
+ pmprintf("Error, line %d: maximum must be a number\n", nLines);
+ return -1;
+ }
+ max = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal;
+
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ }
+
+ if (token == TOK_COLOUR) {
+ sts = ParseColor(&colour, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ // Parse optional actions list
+ //
+ if (token == TOK_ACTIONLIST) {
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ QedBar *b = new QedBar(parent, x, y, w, h);
+ QList<MetricData*> mlp;
+ if (b) {
+ if (min != qQNaN())
+ b->setMinimum(min);
+ if (max != qQNaN())
+ b->setMaximum(max);
+ if (isVertical)
+ b->setOrientation(Qt::Vertical);
+ if (fixMax)
+ b->setScaleRange(0);
+ gadgets.append(b);
+ mlp.append(metricData);
+ } else {
+ perror("no memory for bar gadget");
+ exit(1);
+ }
+ if (colour)
+ b->setColor(colour);
+
+ if (AddMetrics(update, mlp, b))
+ nWarnings++;
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ b->dump(stderr);
+ return 0;
+}
+
+static int
+ParseMultibar(int eofMayFollow)
+{
+ int x, y, sts, ci;
+ unsigned w, h;
+ MetricData* metricData;
+ int isVertical = 0;
+ int noborder = 0;
+ int history = 0;
+ double update = appData.delta;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParseGeometry(x, y, w, h, eofMayFollow);
+
+ if (token == TOK_UPDATE) {
+ sts = ParseUpdate(update, 0);
+ if (sts < 0)
+ return sts;
+ }
+
+ if (token == TOK_NOBORDER) {
+ noborder = 1;
+ NextToken();
+ }
+
+ if (token == TOK_HISTORY) {
+ sts = ParseHistory(history, 0);
+ if (sts < 0)
+ return sts;
+ }
+
+ if (token != TOK_METRICS) {
+ nErrors++;
+ pmprintf("Error, line %d: _metrics (list) expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ if (token != TOK_LPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `(' expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ QList<MetricData*> mlp;
+ do {
+ sts = ParseMetric(&metricData, 0);
+ if (sts < 0)
+ return sts;
+ mlp.append(metricData);
+ } while (token == TOK_IDENTIFIER);
+
+ if (token != TOK_RPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `)' expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ // By default a multibar has a floating maximum initially set to a small
+ // value. If a max of zero is specified, the multibar is a utilisation.
+ // The optional _fixed keyword before the maximum can be used to specify
+ // clipping.
+ //
+ int clip = 0;
+ double max = DBL_MAX;
+ if (token == TOK_FIXED) {
+ clip = 1;
+ NextToken();
+ if (token != TOK_MAX) {
+ nErrors++;
+ pmprintf("Error, line %d: _maximum expected\n", nLines);
+ return -1;
+ }
+ }
+ if (token == TOK_MAX) {
+ NextToken();
+ if (token != TOK_INTEGER && token != TOK_REAL) {
+ nErrors++;
+ pmprintf("Error, line %d: a maximum must be a number\n", nLines);
+ return -1;
+ }
+ max = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal;
+ if (max < 0.0 || (clip && max == 0.0)) {
+ nErrors++;
+ pmprintf("Error, line %d: a fixed maximum must be positive\n",
+ nLines);
+ return -1;
+ }
+ NextToken();
+ }
+
+ if (token != TOK_COLOURLIST) {
+ nErrors++;
+ pmprintf("Error, line %d: _colourlist expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ if (token != TOK_IDENTIFIER) {
+ nErrors++;
+ pmprintf("Error, line %d: colourlist name expected\n", nLines);
+ return -1;
+ }
+
+ for (ci = 0; ci < colorLists.size(); ci++) {
+ if (strcmp(colorLists.at(ci)->identity(), tokenStringVal) == 0)
+ break;
+ }
+ if (ci == colorLists.size()) {
+ nErrors++;
+ pmprintf("Error, line %d: no colourlist named %s exists\n",
+ nLines, tokenStringVal);
+ return -1;
+ }
+
+ QedColorList *colorList = colorLists.at(ci);
+ unsigned int nColours = colorList->length();
+ unsigned int nMetrics = mlp.length();
+
+ if (nColours != nMetrics) {
+ nErrors++;
+ pmprintf("Error, line %d: %d colours don't match %d metrics\n",
+ nLines, nColours, nMetrics);
+ return -1;
+ }
+
+ eofOK = eofMayFollow;
+ NextToken();
+
+ while (token == TOK_HORIZONTAL || token == TOK_VERTICAL) {
+ isVertical = (token == TOK_VERTICAL);
+ NextToken();
+ }
+
+ // Parse optional actions list
+ //
+ if (token == TOK_ACTIONLIST) {
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ QedMultiBar* m = new QedMultiBar(parent, x, y, w, h, colorList, history);
+ if (m) {
+ if (isVertical)
+ m->setOrientation(Qt::Vertical);
+ if (max > 0.0)
+ m->setMaximum(max, clip);
+ m->setOutline(!noborder);
+ gadgets.append(m);
+ } else {
+ perror("no memory to allocate multibar gadget");
+ exit(1);
+ }
+
+ if (AddMetrics(update, mlp, m))
+ nWarnings++;
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ m->dump(stderr);
+ return 0;
+}
+
+static int
+ParseBarGraph(int eofMayFollow)
+{
+ int x, y, sts;
+ unsigned w, h;
+ MetricData* metricData;
+ double min = qQNaN(), max = qQNaN();
+ double update = appData.delta;
+ int fixMax = 0;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParseGeometry(x, y, w, h, eofMayFollow);
+ if (sts < 0)
+ return sts;
+
+ if (token == TOK_UPDATE) {
+ sts = ParseUpdate(update, 0);
+ if (sts < 0)
+ return sts;
+ }
+
+ if (token != TOK_METRIC) {
+ nErrors++;
+ pmprintf("Error, line %d: _metric expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ // There's only ever one metric for a bargraph, use chunkSize = 1
+ QList<MetricData*> mlp;
+ sts = ParseMetric(&metricData, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ mlp.append(metricData);
+
+ while (token == TOK_MIN || token == TOK_MAX || token == TOK_FIXED) {
+
+ eofOK = 0;
+ if (token == TOK_FIXED) {
+ NextToken();
+ if (token != TOK_MAX) {
+ nErrors++;
+ pmprintf("Error, line %d: _fixed _max expected\n", nLines);
+ return -1;
+ }
+ fixMax = 1;
+ }
+
+ if (token == TOK_MIN) {
+ NextToken();
+ if (token != TOK_INTEGER && token != TOK_REAL) {
+ nErrors++;
+ pmprintf("Error, line %d: minimum must be a number\n", nLines);
+ return -1;
+ }
+ min = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal;
+ }
+ else {
+ NextToken();
+ if (token != TOK_INTEGER && token != TOK_REAL) {
+ nErrors++;
+ pmprintf("Error, line %d: maximum must be a number\n", nLines);
+ return -1;
+ }
+ max = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+
+ // Parse optional actions list
+ //
+ if (token == TOK_ACTIONLIST) {
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ QedBarGraph* b = new QedBarGraph(parent, x, y, w, h, w - 1);
+ if (b) {
+ if (min != qQNaN())
+ b->setMinimum(min);
+ if (max != qQNaN())
+ b->setMaximum(max);
+ if (fixMax)
+ b->clipRange();
+ gadgets.append(b);
+ } else {
+ perror("no memory for bargraph gadget");
+ exit(1);
+ }
+
+ if (AddMetrics(update, mlp, b))
+ nWarnings++;
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ b->dump(stderr);
+ return 0;
+}
+
+static int
+ParseLed(int eofMayFollow)
+{
+ int x, y, sts, li;
+ unsigned w, h;
+ MetricData* metricData;
+ double update = appData.delta;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParseGeometry(x, y, w, h, eofMayFollow);
+ if (sts < 0)
+ return sts;
+
+ if (token == TOK_UPDATE) {
+ sts = ParseUpdate(update, 0);
+ if (sts < 0)
+ return sts;
+ }
+
+ if (token != TOK_METRIC) {
+ nErrors++;
+ pmprintf("Error, line %d: _metric expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ QList<MetricData*> mlp;
+ sts = ParseMetric(&metricData, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ mlp.append(metricData);
+
+ if (token != TOK_LEGEND) {
+ nErrors++;
+ pmprintf("Error, line %d: _legend expected for _led\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ if (token != TOK_IDENTIFIER) {
+ nErrors++;
+ pmprintf("Error, line %d: legend name expected\n", nLines);
+ return -1;
+ }
+
+ for (li = 0; li < legends.size(); li++) {
+ if (strcmp(legends.at(li)->identity(), tokenStringVal) == 0)
+ break;
+ }
+ if (li == colorLists.size()) {
+ nErrors++;
+ pmprintf("Error, line %d: no legend named %s exists\n",
+ nLines, tokenStringVal);
+ return -1;
+ }
+ QedLegend *legend = legends.at(li);
+
+ eofOK = eofMayFollow;
+ NextToken();
+
+ // Parse optional actions list
+ //
+ if (token == TOK_ACTIONLIST) {
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ QedRoundLED* l = new QedRoundLED(parent, x, y, w, h, legend);
+ if (l) {
+ gadgets.append(l);
+ } else {
+ perror("no memory for led gadget");
+ exit(1);
+ }
+
+ if (AddMetrics(update, mlp, l))
+ nWarnings++;
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ l->dump(stderr);
+ return 0;
+}
+
+static int
+ParseLegend(int eofMayFollow)
+{
+ eofOK = 0;
+
+ NextToken();
+ if (token != TOK_IDENTIFIER) {
+ nErrors++;
+ pmprintf("Error, line %d: name expected for legend\n", nLines);
+ return -1;
+ }
+ QedLegend* legend = new QedLegend(tokenStringVal);
+ if (legend == NULL) {
+ nErrors++;
+ fprintf(stderr,
+ "Error, line %d: out of memory allocating legend\n",
+ nLines);
+ return -1;
+ }
+ legends.append(legend);
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "legend:\n name \"%s\"\n", tokenStringVal);
+
+ NextToken();
+ if (token != TOK_LPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `(' expected for legend\n", nLines);
+ delete legend;
+ return -1;
+ }
+ NextToken();
+
+ while (token == TOK_INTEGER ||
+ token == TOK_REAL ||
+ token == TOK_DEFAULT) {
+ double val = 0.0;
+ int prevToken;
+ if (token != TOK_DEFAULT)
+ val = (token == TOK_REAL) ? tokenRealVal : tokenIntVal;
+ prevToken = token;
+ NextToken();
+ if (token != TOK_IDENTIFIER && token != TOK_STRING) {
+ nErrors++;
+ pmprintf("Error, line %d: colour name expected in legend\n", nLines);
+ delete legend;
+ return -1;
+ }
+ if (prevToken != TOK_DEFAULT)
+ legend->addThreshold(val, tokenStringVal);
+ else
+ legend->setDefaultColor(tokenStringVal);
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, " %.2f = %s\n", val, tokenStringVal);
+ NextToken();
+ }
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ putc('\n', stderr);
+
+ if (token != TOK_RPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `)' or threshold expected for legend\n",
+ nLines);
+ delete legend;
+ return -1;
+ }
+
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParseColorList(int eofMayFollow)
+{
+ eofOK = 0;
+ NextToken();
+ if (token != TOK_IDENTIFIER) {
+ nErrors++;
+ pmprintf("Error, line %d: name expected for colourlist\n", nLines);
+ return -1;
+ }
+ char *name = tokenStringVal;
+ NextToken();
+
+ if (token != TOK_LPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `(' expected for colourlist\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "colourlist: name \"%s\"\n", name);
+
+ QedColorList *colorList = new QedColorList(name);
+ if (colorList == NULL) {
+ nErrors++;
+ fprintf(stderr,
+ "Error, line %d: out of memory allocating colorList\n",
+ nLines);
+ return -1;
+ }
+ while (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ colorList->addColor(tokenStringVal);
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, " %s\n", tokenStringVal);
+ NextToken();
+ }
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ putc('\n', stderr);
+
+ if (token != TOK_RPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `)' expected for colourlist\n", nLines);
+ return -1;
+ }
+ colorLists.append(colorList);
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+// Config files are typically created by programs rather than specified by the
+// user. In the config file header, after the version number, such programs
+// should put their argv so that the desktop can restart pmgadgets
+// (indirectly) by re-running the program that created the config file.
+//
+QStringList configArgv;
+
+void ParseVersion()
+{
+ if ((token == TOK_IDENTIFIER || token == TOK_STRING) &&
+ strcmp(tokenStringVal, "pmgadgets") == 0) {
+ eofOK = 0;
+ NextToken();
+ if (token != TOK_INTEGER) {
+ pmprintf("Error, line %d: bad pmgadgets config file version\n",
+ nLines);
+ pmflush();
+ exit(1);
+ }
+ if (tokenIntVal > 1) {
+ pmprintf("Error, line %d: you need a newer version pmgadgets to run this\n",
+ nLines);
+ pmflush();
+ exit(1);
+ }
+ else if (tokenIntVal < 1) {
+ pmprintf("Error, line %d: ludicrous pmgadgets config file version\n",
+ nLines);
+ pmflush();
+ exit(1);
+ }
+ eofOK = 1;
+ NextToken();
+ while (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ configArgv.append(strdup(tokenStringVal));
+ NextToken();
+ }
+ }
+}
+
+int
+Parse(void)
+{
+ int sts;
+ NextToken();
+ setjmp(scannerEofEnv); // come back here on unexpected EOF
+
+ ParseVersion();
+ while (token != TOK_EOF) {
+ eofOK = 0;
+ switch (token) {
+ case TOK_LINE:
+ sts = ParseLine(1);
+ break;
+
+ case TOK_LABEL:
+ sts = ParseLabel(1);
+ break;
+
+ case TOK_BAR:
+ sts = ParseBar(1);
+ break;
+
+ case TOK_MULTIBAR:
+ sts = ParseMultibar(1);
+ break;
+
+ case TOK_BARGRAPH:
+ sts = ParseBarGraph(1);
+ break;
+
+ case TOK_LED:
+ sts = ParseLed(1);
+ break;
+
+ case TOK_LEGEND:
+ sts = ParseLegend(1);
+ break;
+
+ case TOK_COLOURLIST:
+ sts = ParseColorList(1);
+ break;
+
+ case TOK_ACTIONLIST:
+ sts = ParseActionList(AL_NEED_NAME, 1);
+ break;
+
+ default:
+ sts = -1;
+ nErrors++;
+ pmprintf("Error, line %u: Bad gadget type\n", nLines);
+ break;
+ }
+ if (sts == -1) {
+ eofOK = 1;
+ FindNewStatement();
+ }
+ const unsigned maxErrors = 10;
+ if (nErrors > maxErrors) {
+ pmprintf("Too many errors, giving up!\n");
+ break;
+ }
+ }
+ if (nErrors) {
+ pmprintf("%s: configuration file has errors "
+ "(%d lines parsed, %d errors)\n", pmProgname, nLines, nErrors);
+ pmflush();
+ return -1;
+ }
+ if (nWarnings)
+ pmflush();
+ return 0;
+}
diff --git a/src/pmgadgets/pmgadgets-args.sh b/src/pmgadgets/pmgadgets-args.sh
new file mode 100644
index 0000000..78c6fbc
--- /dev/null
+++ b/src/pmgadgets/pmgadgets-args.sh
@@ -0,0 +1,381 @@
+#
+# Copyright (c) 2014 Red Hat.
+# Copyright (c) 1996-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.
+#
+
+#
+# Note on error handling
+#
+# We assume this is sourced from a shell script that uses $status to pass
+# the exit status back to the parent.
+#
+
+# source the PCP configuration environment variables
+. $PCP_DIR/etc/pcp.env
+
+prog=`basename $0`
+
+#
+# Standard usage and command-line argument parsing for pmgadgets front ends.
+# This file should be included by pmgadgets front end scripts to present a
+# consistent interface. See pmgadgets(1) for more information.
+#
+
+#
+# The front end scripts should call _pmgadgets_usage after their own usage
+# information in a subroutine called _usage. The _usage subroutine may be
+# called by either _pmgadgets_note or _pmgadgets_args.
+#
+
+_pmgadgets_usage()
+{
+ echo '
+ -C check configuration file and exit
+ -h host metrics source is PMCD on host
+ -n pmnsfile use an alternative PMNS
+ -t interval sample interval [default 2.0 seconds]
+ -z set reporting timezone to local time of metrics source
+ -Z timezone set reporting timezone
+
+ -zoom factor make the gadgets bigger by a factor of 1, 2, 3 or 4
+ -infofont fontname use fontname for text in info dialogs
+ -defaultfont fontname use fontname for label gadgets
+
+ -display display-string
+ -geometry geometry-string
+ -name name-string
+ -title title-string'
+}
+
+#
+# One of the first actions of a front end script should be to call
+# _pmgadgets_args. It sets the following variables:
+#
+# host the host specified with -h.
+# interval the update interval specified by the user, 0 indicates it
+# was not specified. The caller must pass this on to pmgadgets,
+# unlike $host which is included in $args.
+# args The list of args that pmgadgets will comprehend and use.
+# otherArgs The arguments pmgadgets will not understand and should be
+# handled by the front end script.
+# titleArg The title the user prefers. If empty, the title should be
+# provided by the front end script.
+# prog The name of the program.
+# namespace The namespace (including the flag) if specified, else empty
+# eg "-n foo"
+# msource The metrics source, whether live or an archive, include the
+# flag. eg "-h blah"
+#
+
+_pmgadgets_args()
+{
+
+host=""
+args=""
+otherArgs=""
+titleArg=""
+namespace=""
+interval=0
+msource=""
+
+if [ $# -eq 1 -a '$1' = '-\?' ]
+then
+ _usage
+ status=0
+ exit
+fi
+
+while [ $# -gt 0 ]
+do
+ case $1
+ in
+ -g*|-d*|-fg|-bg|-name)
+ # assume an X11 argument
+ if [ $# -lt 2 ]
+ then
+ _pmgadgets_note Usage-Error error "$prog: X-11 option $1 requires one argument"
+ #NOTREACHED
+ fi
+ args="$args $1 '$2'"
+ shift
+ ;;
+
+ -title)
+ # assume an X11 argument
+ if [ $# -lt 2 ]
+ then
+ echo "$prog: $1 requires one argument"
+ _pmgadgets_note Usage-Error error "$prog: Option $1 requires one argument"
+ #NOTREACHED
+ fi
+ titleArg="$2"
+ shift
+ ;;
+
+ -D|-Z|-delta|-zoom|-infofont|-defaultfont)
+ if [ $# -lt 2 ]
+ then
+ _pmgadgets_note Usage-Error error "$prog: Option $1 requires one argument"
+ #NOTREACHED
+ fi
+ args="$args $1 '$2'"
+ shift
+ ;;
+
+ -D*|-Z*|-z|-C)
+ args="$args $1"
+ ;;
+
+ -h)
+ if [ "X$host" != X ]
+ then
+ _pmgadgets_note Usage-Error error "$prog: Only one -h option may be specified"
+ #NOTREACHED
+ fi
+ if [ $# -lt 2 ]
+ then
+ _pmgadgets_note Usage-Error error "$prog: Option $1 requires one argument"
+ #NOTREACHED
+ fi
+ host=$2
+ args="$args -h $2"
+ msource="-h $host"
+ shift
+ ;;
+
+ -n)
+ if [ $# -lt 2 ]
+ then
+ _pmgadgets_note Usage-Error error "$prog: Option $1 requires one argument"
+ #NOTREACHED
+ fi
+ namespace="-n $2"
+ args="$args -n $2"
+ shift
+ ;;
+
+ -t)
+ if [ $# -lt 2 ]
+ then
+ _pmgadgets_note Usage-Error error "$prog: Option $1 requires one argument"
+ #NOTREACHED
+ fi
+ interval=$2
+ shift
+ ;;
+
+ *)
+ otherArgs="$otherArgs $1"
+ ;;
+
+ esac
+ shift
+done
+
+if [ -z "$host" ]
+then
+ host=`pmhostname`
+ msource="-h $host"
+fi
+}
+
+# standard fatal error reporting
+# Usage: _pmgadgets_error message goes in here
+# _pmgadgets_error -f file
+#
+_pmgadgets_error()
+{
+ _pmgadgets_note Error error $*
+}
+
+# standard warning
+# Usage: _pmgadgets_warning message goes in here
+# _pmgadgets_warning -f file
+#
+_pmgadgets_warning()
+{
+ _pmgadgets_note Warning warning $*
+}
+
+# standard info
+# Usage: _pmgadgets_info message goes in here
+# _pmgadgets_info -f file
+#
+_pmgadgets_info()
+{
+ _pmgadgets_note Info info $*
+}
+
+# generic notifier
+# Usage: _pmgadgets_note tag icon args ...
+#
+_pmgadgets_note()
+{
+ tag=$1; shift
+ icon=$1; shift
+ button=""
+ [ $tag = Error ] && button="-B Quit"
+ [ $tag = Usage-Error ] && button="-B Quit -b Usage"
+
+ [ X"$PCP_STDERR" = XDISPLAY -a -z "$DISPLAY" ] && unset PCP_STDERR
+
+ if [ $# -eq 2 -a "X$1" = X-f ]
+ then
+ case "$PCP_STDERR"
+ in
+ DISPLAY)
+ ans=`$PCP_XCONFIRM_PROG -icon $icon -file $2 -useslider -header "$tag $prog" $button 2>&1`
+ ;;
+ '')
+ echo "$tag:" >&2
+ cat $2 >&2
+ ans=Quit
+ ;;
+ *)
+ echo "$tag:" >>$PCP_STDERR
+ cat $2 >>$PCP_STDERR
+ ans=Quit
+ ;;
+ esac
+ else
+ case "$PCP_STDERR"
+ in
+ DISPLAY)
+ ans=`$PCP_XCONFIRM_PROG -icon $icon -t "$*" -noframe -header "$tag $prog" $button 2>&1`
+ ;;
+ '')
+ echo "$tag: $*" >&2
+ ans=Quit
+ ;;
+ *)
+ echo "$tag: $*" >>$PCP_STDERR
+ ans=Quit
+ ;;
+ esac
+ fi
+
+ if [ $tag = Usage-Error ]
+ then
+ [ $ans = Usage ] && _usage
+ tag=Error
+ fi
+
+ if [ $tag = Error ]
+ then
+ status=1
+ exit
+ fi
+}
+
+# Fetch metrics
+#
+# $1 - pmprobe flag
+# $2 - metric name
+# number - number of values
+# $tmp/pmgadgets_result - values
+#
+_pmgadgets_fetch()
+{
+ if pmprobe $namespace $msource $1 $2 > $tmp/pmgadgets_fetch 2>&1
+ then
+ tr < $tmp/pmgadgets_fetch ' ' '\012' \
+ | tee $tmp/pmgadgets_output \
+ | sed \
+ -e 's/"//g' \
+ -e '1,2d' \
+ > $tmp/pmgadgets_result
+
+ number=`sed -n -e 2p < $tmp/pmgadgets_output`
+ [ $number -le 0 ] && return 1
+ else
+ number=0
+ rm -f $tmp/pmgadgets_output $tmp/pmgadgets_result
+ return 1
+ fi
+
+ return 0
+}
+
+# Fetch the metric values
+#
+# $1 - metric name
+# number - number of values
+# $tmp/pmgadgets_result - values
+#
+_pmgadgets_fetch_values()
+{
+ _pmgadgets_fetch -v $1
+ return $?
+}
+
+# Fetch the metric instance list
+#
+# $1 - metric name
+# number - number of instances
+# $tmp/pmgadgets_result - instances
+#
+_pmgadgets_fetch_indom()
+{
+ _pmgadgets_fetch -I $1
+ return $?
+}
+
+# Convert pmprobe/pminfo error message into something useful and
+# consistent
+#
+_pmgadgets_fetch_mesg()
+{
+ $PCP_AWK_PROG '
+$1 == "pmprobe:" { $1 = "'$prog':"; print; exit }
+$1 == "pminfo:" { $1 = "'$prog':"; print; exit }
+$1 == "Error:" { $1 = ":";
+ printf("%s: %s%s\n", "'$prog'", metric, $0); exit }
+$1 == "inst" { exit }
+NF == 1 { metric = $1; next }
+NF == 0 { next }
+NF == 2 && $2 == "0" { printf("%s: %s: No values available\n", "'$prog'", $1); exit}
+ { $2 = ":"; print "'$prog': " $0; exit}' \
+ | sed "s/ : /: /" \
+ | fmt
+}
+
+# Generate error metric for failed fetch
+#
+_pmgadgets_fetch_fail()
+{
+ cat $tmp/pmgadgets_fetch | _pmgadgets_fetch_mesg >> $tmp/msg
+ echo "$prog: Failed to $1 from host \"$host\"" | fmt >> $tmp/msg
+ _pmgadgets_error -f $tmp/msg
+ # NOTREACHED
+}
+
+# Generate warning message for failed fetch
+#
+_pmgadgets_fetch_warn()
+{
+ cat $tmp/pmgadgets_fetch | _pmgadgets_fetch_mesg >> $tmp/msg
+ echo "$prog: Failed to $1 from host \"$host\"" | fmt >> $tmp/msg
+ _pmgadgets_warning -f $tmp/msg
+}
+
+# Check that $OPTARG for option $1 is a positive integer
+# ...note the creative use of unary - to prevent leading signs
+#
+_pmgadgets_unsigned()
+{
+ if [ "X-$OPTARG" != "X`expr 0 + -$OPTARG 2>/dev/null`" ]
+ then
+ _pmgadgets_error "$prog: -$1 option must have a positive integral argument"
+ # NOTREACHED
+ fi
+}
diff --git a/src/pmgadgets/pmgadgets.cpp b/src/pmgadgets/pmgadgets.cpp
new file mode 100644
index 0000000..f0a1977
--- /dev/null
+++ b/src/pmgadgets/pmgadgets.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013, Red Hat.
+ *
+ * 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 <QtGui>
+#include "pmgadgets.h"
+#include "qed_led.h"
+#include "qed_line.h"
+
+PmGadgets::PmGadgets(QWidget *parent) : QDialog(parent)
+{
+ QedRoundLED *redLED = new QedRoundLED(this, Qt::red);
+ QedSquareLED *blueLED = new QedSquareLED(this, Qt::blue);
+ QedRoundLED *greenLED = new QedRoundLED(this, Qt::green);
+ QedSquareLED *grayLED = new QedSquareLED(this, Qt::darkGray);
+ QedLine *horizontal = new QedLine(this, 2, Qt::Horizontal);
+
+ my.mainLayout = new QGridLayout;
+ my.mainLayout->addWidget(redLED, 0, 0);
+ my.mainLayout->addWidget(blueLED, 0, 1);
+ my.mainLayout->addWidget(greenLED, 0, 2);
+ my.mainLayout->addWidget(grayLED, 1, 0);
+ my.mainLayout->addWidget(horizontal, 1, 1);
+
+ setLayout(my.mainLayout);
+ setWindowsFlags();
+}
+
+void PmGadgets::setWindowsFlags(void)
+{
+ Qt::WindowFlags flags = windowFlags();
+ // removal
+ flags &= ~Qt::WindowMinimizeButtonHint;
+ flags &= ~Qt::WindowCloseButtonHint;
+ // addition
+ flags |= Qt::WindowStaysOnTopHint;
+ //flags |= Qt::FramelessWindowHint;
+ setWindowFlags(flags);
+}
+
+void PmGadgets::help()
+{
+ QMessageBox::information(this, tr("PmGadgets Help"),
+ tr("Performance Co-Pilot graphical gadgets and great gizmos."));
+}
diff --git a/src/pmgadgets/pmgadgets.h b/src/pmgadgets/pmgadgets.h
new file mode 100644
index 0000000..8ecc3f9
--- /dev/null
+++ b/src/pmgadgets/pmgadgets.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013, Red Hat.
+ *
+ * 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 PMGADGETS_H
+#define PMGADGETS_H
+
+#include <QGridLayout>
+#include <QDialog>
+#include <QList>
+
+class PmGadgets : public QDialog
+{
+ Q_OBJECT
+
+public:
+ PmGadgets(QWidget *parent = 0);
+
+private slots:
+ void help();
+
+private:
+ void setWindowsFlags();
+
+ struct {
+ QList<QWidget *> widgets;
+ QGridLayout *mainLayout;
+ } my;
+};
+
+#endif // PMGADGETS_H
diff --git a/src/pmgadgets/pmgadgets.info.in b/src/pmgadgets/pmgadgets.info.in
new file mode 100644
index 0000000..5ace93b
--- /dev/null
+++ b/src/pmgadgets/pmgadgets.info.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+ <key>CFBundleIconFile</key>
+ <string>pmgadgets.icns</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleGetInfoString</key>
+ <string>@pkg_version@</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleExecutable</key>
+ <string>pmgadgets</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.redhat.pmgadgets</string>
+</dict>
+</plist>
diff --git a/src/pmgadgets/pmgadgets.pro b/src/pmgadgets/pmgadgets.pro
new file mode 100644
index 0000000..72c53b1
--- /dev/null
+++ b/src/pmgadgets/pmgadgets.pro
@@ -0,0 +1,23 @@
+TEMPLATE = app
+LANGUAGE = C++
+HEADERS = pmgadgets.h tokens.h
+SOURCES = pmgadgets.cpp main.cpp parse.cpp
+FLEXSOURCES = lex.l
+ICON = pmgadgets.icns
+RESOURCES = pmgadgets.qrc
+CONFIG += qt warn_on
+INCLUDEPATH += ../include ../libpcp_qed/src
+LIBS += -L../libpcp_qed/src -L../libpcp_qmc/src/$$DESTDIR
+LIBS += -lpcp -lpcp_qed
+win32:LIBS += -lwsock32
+QT += network
+QMAKE_INFO_PLIST = pmgadgets.info
+QMAKE_EXTRA_COMPILERS += flex
+QMAKE_CXXFLAGS += $$(PCP_CFLAGS)
+
+flex.commands = flex ${QMAKE_FILE_IN}
+flex.input = FLEXSOURCES
+flex.output = lex.yy.c
+flex.variable_out = SOURCES
+flex.depends = tokens.h
+flex.name = flex
diff --git a/src/pmgadgets/pmgadgets.qrc b/src/pmgadgets/pmgadgets.qrc
new file mode 100644
index 0000000..3e34d21
--- /dev/null
+++ b/src/pmgadgets/pmgadgets.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>images/aboutpcp.png</file>
+ <file>images/pmtime.png</file>
+</qresource>
+</RCC>
diff --git a/src/pmgadgets/pmgadgets.sh.IN b/src/pmgadgets/pmgadgets.sh.IN
new file mode 100644
index 0000000..1e02c63
--- /dev/null
+++ b/src/pmgadgets/pmgadgets.sh.IN
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec PKG_MAC_DIR/MacOS/pmgadgets "$@"
diff --git a/src/pmgadgets/pmgcisco.sh b/src/pmgadgets/pmgcisco.sh
new file mode 100755
index 0000000..8d57d80
--- /dev/null
+++ b/src/pmgadgets/pmgcisco.sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 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.
+#
+# source the PCP configuration environment variables
+. $PCP_DIR/etc/pcp.env
+
+status=1
+tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1
+trap "rm -fr $tmp; exit \$status" 0 1 2 3 15
+
+# usage - print out the usage of program
+#
+_usage()
+{
+ echo >$tmp/msg "Usage: $prog [options] [pmgadgets options]"
+ echo >>$tmp/msg '
+options:
+ -t interval sample interval [default 120.0 seconds]
+ -V verbose/diagnostic output
+ -x pixels width of each bar graph [default 31]
+ -y pixels height of each bar graph [default 31]
+
+pmgadgets(1) options:'
+ _pmgadgets_usage >>$tmp/msg
+ echo >>$tmp/msg
+ _pmgadgets_info -f $tmp/msg
+}
+
+# default variables
+#
+verbose=false
+plot_y=2
+bar_y=31
+bar_x=31
+
+# keep the original command line for restart after logout/login
+#
+echo -n "pmgadgets 1 \"pmgcisco\"" >$tmp/conf
+for arg
+do
+ echo -n " \"$arg\"" >>$tmp/conf
+done
+echo "" >> $tmp/conf
+
+# get arguments
+#
+. $PCP_SHARE_DIR/lib/pmgadgets-args
+
+_pmgadgets_args "$@"
+[ "$interval" = 0 ] && interval=120sec
+args="$args -t $interval"
+
+if [ -n "$otherArgs" ]
+then
+ while getopts "Vx:y:?" c $otherArgs
+ do
+ case $c
+ in
+ V)
+ verbose=true
+ ;;
+ x) # -x pixels (width of bar graph)
+ bar_x=$OPTARG
+ ;;
+ y) # -y pixels (height of bar graph)
+ bar_y=$OPTARG
+ ;;
+ ?)
+ _usage
+ status=1
+ exit
+ ;;
+ esac
+ done
+
+ set -- $otherArgs
+ shift `expr $OPTIND - 1`
+
+ if [ $# -gt 0 ]
+ then
+ _usage
+ status=1
+ exit
+ fi
+fi
+
+# check on metric availability
+#
+if _pmgadgets_fetch_values cisco.bandwidth
+then
+ if [ ! -s $tmp/pmgadgets_result -o "$number" -lt 1 ]
+ then
+ _pmgadgets_fetch_fail "get Cisco metrics"
+ #NOTREACHED
+ fi
+else
+ _pmgadgets_fetch_fail "get Cisco metrics"
+ #NOTREACHED
+fi
+
+# output the config file
+#
+pminfo $msource -f cisco.bandwidth \
+| sed -n \
+ -e 's/:/ /' \
+ -e '/ value /{
+s/"] value / /
+s/.*"//
+p
+}' \
+| sort >$tmp/data
+
+# $tmp/data has lines of the form <hostname> <interface> <bandwidth>
+# try to truncate the <hostname> by dropping components from the
+# right hand end, but retain uniqueness ... the truncated name is used
+# for the label gadget
+#
+sed -e 's/ .*//' <$tmp/data | sort -u >$tmp/hostnames
+trim=1
+rm -f $tmp/done
+while true
+do
+ $PCP_AWK_PROG -F . <$tmp/hostnames >$tmp/tmp '
+NR == 1 { last_part=NF-'$trim'+1
+ if (last_part < 2) { fail=1; exit }
+ want=$last_part
+ }
+NF >= last_part { if ($last_part != want) { fail=2; exit }
+ next
+ }
+ { fail=3; exit }
+END { # print "fail=" fail
+ if (fail) print "" >"'$tmp/done'"
+ }'
+ [ -f $tmp/done ] && break
+ trim=`expr $trim + 1`
+done
+
+$PCP_AWK_PROG -F . <$tmp/hostnames >$tmp/map '
+ { printf "%s",$0
+ for (i=1; i<=NF-'$trim'+1; i++) {
+ if (i == 1) printf "\t%s",$i
+ else printf ".%s",$i
+ }
+ print ""
+ }'
+
+# this produces <hostname> <shortname> <interface> <bandwidth>
+#
+join $tmp/map $tmp/data \
+| $PCP_AWK_PROG >>$tmp/conf '
+
+BEGIN { y = 15; step_y = '$bar_y'+31; step_x = '$bar_x'+16 }
+ { if (!seen[$4]) {
+ print "_legend link" $4 " ("
+ print " _default \"#608080\""
+ print " " int(0.10*$4) " green"
+ print " " int(0.70*$4) " yellow"
+ print " " int(0.80*$4) " orange"
+ print " " int(0.90*$4) " red"
+ print ")"
+ print ""
+ seen[$4] = 1
+ }
+
+ print "_label",5,y,"\"" $2 ":" $3 "\" \"7x13bold\""
+
+ print "_label",5,y+11,"\"in\" \"7x13\""
+ print "_led",5+'$bar_x'-6,y+4,8,8
+ print "_metric cisco.bytes_in[\"" $1 ":" $3 "\"]"
+ print "_legend link" $4
+
+ print "_bargraph",5,y+14,'$bar_x,$bar_y'
+ print "_metric cisco.bytes_in[\"" $1 ":" $3 "\"]"
+ print "_max " $4
+
+ print "_label",step_x+5,y+11,"\"out\" \"7x13\""
+ print "_led",step_x+5+'$bar_x'-6,y+4,8,8
+ print "_metric cisco.bytes_out[\"" $1 ":" $3 "\"]"
+ print "_legend link" $4
+
+ print "_bargraph",step_x+5,y+14,'$bar_x,$bar_y'
+ print "_metric cisco.bytes_out[\"" $1 ":" $3 "\"]"
+ print "_max " $4
+
+ print ""
+ y += step_y
+ }
+END { value='"`echo $interval | sed -e 's/[^0-9.].*/; scale=\"&\"/' | tr A-Z a-z`"'
+ # tt is total time for plot in seconds, each bar is 1 pixel wide
+ tt = value * ('$bar_x' - 1)
+ if (scale ~ /^min/ || scale == "m") tt *= 60
+ else if (scale ~ /^hour/ || scale == "h") tt *= 3600
+ else if (scale ~ /^day/ || scale == "d") tt *= 24*3600
+ printf "_label 5 %d \"History: ",y
+ if (tt > 24*3600) {
+ xxx = int(tt/24*3600)
+ printf "%dday",xxx
+ if (xxx > 1) printf "s"
+ rem = int(0.5+(tt%(24*3600)/3600))
+ if (rem > 1)
+ printf " %dhours",rem
+ else if (rem == 1)
+ printf " 1hour"
+ }
+ else if (tt > 3600) {
+ xxx = int(tt/3600)
+ printf "%dhour",xxx
+ if (xxx > 1) printf "s"
+ rem = int(0.5+(tt%(3600)/60))
+ if (rem > 1)
+ printf " %dmin",rem
+ else if (rem == 1)
+ printf " 1min"
+ }
+ else if (tt > 60) {
+ xxx = int(tt/60)
+ printf "%dmin",xxx
+ if (xxx > 1) printf "s"
+ rem = int(0.5+tt%60)
+ if (rem > 1)
+ printf " %dsecs",rem
+ else if (rem == 1)
+ printf " 1sec"
+ }
+ else {
+ if (tt > 1)
+ printf "%dsecs",tt
+ else if (tt == 1)
+ printf "1sec"
+ }
+ print "\""
+ }'
+
+$verbose && cat $tmp/conf
+eval pmgadgets <$tmp/conf $args
+
+status=$?
+exit
diff --git a/src/pmgadgets/pmgcluster.sh b/src/pmgadgets/pmgcluster.sh
new file mode 100755
index 0000000..ddd2229
--- /dev/null
+++ b/src/pmgadgets/pmgcluster.sh
@@ -0,0 +1,281 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 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.
+#
+
+# This script uses pmgsys to layout gadgets for a series of hosts and then
+# combines the resulting config files into one big one and invokes pmgadgets
+# on the result.
+
+# source the PCP configuration environment variables
+. $PCP_DIR/etc/pcp.env
+
+status=1
+tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1
+trap "rm -fr $tmp; exit \$status" 0 1 2 3 15
+verbose="false"
+prog=`basename $0`
+nhosts=0
+hosts=""
+
+# usage - print out the usage of program
+#
+_usage()
+{
+ echo >$tmp/msg "Usage: $prog [options] [pmgadgets options] [host ...]"
+ echo >>$tmp/msg '
+
+Default hosts are specified in /etc/nodes (or /etc/ace/nodes).
+
+options:
+ -H nodes file specifying nodes in cluster
+ [default $PCP_CLUSTER_CONFIG or /etc/nodes]
+ -r rows number of rows for layout
+ -l suppress host name labels
+ -L label title label for pmgagdets layout
+ -V verbose (print pmgadgets configuration)
+
+pmgadgets(1) options:'
+ _pmgadgets_usage | sed -e '/.*-h host.*/d' >>$tmp/msg
+ _pmgadgets_info -f $tmp/msg
+}
+
+# keep the original command line for restart after logout/login
+#
+echo -n "pmgadgets 1 \"pmgcluster\"" >$tmp/conf
+for arg
+do
+ echo -n " \"$arg\"" >>$tmp/conf
+done
+echo >>$tmp/conf
+
+# get arguments
+#
+. $PCP_SHARE_DIR/lib/pmgadgets-args
+
+
+# Have to pre-parse the options for -L coz getopts can't handle multiword
+# strings when using $otherArgs
+#
+if [ -z "$PCP_CLUSTER_CONFIG" ]
+then
+ nodesfile=/etc/nodes
+ [ ! -f "$nodesfile" ] && nodesfile=/etc/ace/nodes
+else
+ nodesfile="$PCP_CLUSTER_CONFIG"
+ if [ ! -f "$nodesfile" ]
+ then
+ echo "Error: \"$nodesfile\" specified in \$PCP_CLUSTER_CONFIG: file not found"
+ _usage
+ status=1
+ exit
+ fi
+fi
+
+while [ $# -gt 0 ]
+do
+ case $1
+ in
+ -h) hosts="$2"
+ nhosts=1
+ shift
+ ;;
+ -L) titlelabel="$2"
+ shift
+ ;;
+ *) pmgargs="$pmgargs $1"
+ ;;
+ esac
+ shift
+done
+set -- $pmgargs
+
+_pmgadgets_args "$@"
+
+if [ -n "$otherArgs" ]
+then
+ while getopts "H:r:lV?" c $otherArgs
+ do
+ case $c
+ in
+ H) nodesfile=$OPTARG
+ if [ ! -f "$nodesfile" ]
+ then
+ echo "$prog Error: \"$nodesfile\" for -H file not found"
+ _usage
+ status=1
+ exit
+ fi
+ ;;
+ r) rows=$OPTARG
+ ;;
+ l) pmgsysargs="$pmgsysargs -l"
+ ;;
+ V) verbose="true"
+ ;;
+ ?) _usage
+ status=1
+ exit
+ ;;
+ esac
+ done
+
+ set -- $otherArgs
+ shift `expr $OPTIND - 1`
+else
+ set --
+fi
+
+if [ "$interval" != "0" ]
+then
+ args="$args -t $interval"
+fi
+
+if [ ! -z "$titleArg" ]
+then
+ args="$args -title $titleArg"
+fi
+
+ytitle=0
+if [ ! -z "$titlelabel" ]
+then
+ ytitle=16
+ echo "_label 6 13 \"$titlelabel\" \"7x13bold\"" >> $tmp/conf
+fi
+
+if [ "$nhosts" -eq 0 ]
+then
+ nhosts=$#
+ if [ $nhosts -eq 0 ]
+ then
+ if [ -f "$nodesfile" ]
+ then
+ hosts=`sed -e 's/[# ].*$//' $nodesfile`
+ nhosts=`echo $hosts | wc -w`
+ fi
+ else
+ hosts=$*
+ fi
+fi
+
+if [ $nhosts -eq 0 ]
+then
+ _usage
+ status=1
+ exit
+fi
+
+
+ox=0
+oy=$ytitle
+row=0
+col=0
+ygap=20
+xgap=50
+xmax=0
+ymax=0
+hostnum=0
+
+if [ -z "$rows" ]
+then
+ rows=`echo "sqrt($nhosts)" | bc`
+fi
+
+cols=`expr $nhosts / $rows`
+
+for host in $hosts
+do
+ if ! pmgsys $pmgsysargs -C -V -h $host > $tmp/pmgsys 2> $tmp/pmgsys.err
+ then
+ sed -e "s/pmgsys/$prog/g" $tmp/pmgsys.err >&2
+ continue
+ fi
+
+ sed -e 's/^pmgadgets/# pmgadgets/' \
+ -e "/.*_update.*/d" \
+ -e "s/-C//g" \
+ -e "s/cpuActions/"$hostnum"_cpuActions/g" \
+ -e "s/loadActions/"$hostnum"_loadActions/g" \
+ -e "s/netActions/"$hostnum"_netActions/g" \
+ -e "s/diskActions/"$hostnum"_diskActions/g" \
+ -e "s/diskLegend/"$hostnum"_diskLegend/g" \
+ -e "s/cpuColours/"$hostnum"_cpuColours/g" \
+ -e "s/netColours/"$hostnum"_netColours/g" \
+ -e "s/kernel\./"$host":kernel./g" \
+ -e "s/disk\./"$host":disk./g" \
+ -e "s/mem\./"$host":mem./g" \
+ -e "s/swap\./"$host":swap./g" \
+ -e "s/network\./"$host":network./g" \
+ $tmp/pmgsys \
+ | $PCP_AWK_PROG '
+ /^_/ && $2 ~ /^[0-9]+$/ && $3 ~ /^[0-9]+$/ {
+ printf "%s %d %d ", $1, $2 + '$ox', $3 + '$oy'
+ for (i=4; i <= NF; i++)
+ printf "%s ", $i
+ printf "\n"
+ next
+ }
+ {
+ print
+ }' | tee $tmp/$host >> $tmp/conf
+
+ #
+ # set xmax and ymax for this host
+ #
+ eval `$PCP_AWK_PROG '
+ /^_/ && $2 ~ /^[0-9]+$/ && $3 ~ /^[0-9]+$/ {
+ if (xmax < $2)
+ xmax = $2
+ if (ymax < $3)
+ ymax = $3
+ }
+ END {
+ printf "host_xmax=%d; ymax=%d", xmax, ymax
+ }' $tmp/$host`
+
+ [ $host_xmax -gt $xmax ] && xmax=$host_xmax
+
+ row=`expr $row + 1`
+ if [ $row -eq $rows ]
+ then
+ row=0
+ oy=$ytitle
+ ox=`expr $xmax + $xgap`
+ col=`expr $col + 1`
+ else
+ oy=`expr $ymax + $ygap`
+ fi
+
+ rm -f $tmp/$host
+ hostnum=`expr $hostnum + 1`
+done
+
+rm -f $tmp/pmgsys $tmp/pmgsys.err
+
+if $verbose
+then
+ cat $tmp/conf
+fi
+
+if [ $hostnum -le 0 ]
+then
+ echo "$prog: unable to monitor any hosts" >&2
+ status=1
+ exit
+fi
+
+eval pmgadgets $args <$tmp/conf
+
+status=$?
+exit
diff --git a/src/pmgadgets/pmgshping.sh b/src/pmgadgets/pmgshping.sh
new file mode 100755
index 0000000..455b979
--- /dev/null
+++ b/src/pmgadgets/pmgshping.sh
@@ -0,0 +1,242 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 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.
+#
+
+# source the PCP configuration environment variables
+. $PCP_DIR/etc/pcp.env
+
+status=1
+tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1
+trap "rm -fr $tmp; exit \$status" 0 1 2 3 15
+
+# usage - print out the usage of program
+#
+_usage()
+{
+ echo >$tmp/msg "Usage: $prog [options] [pmgadgets options]"
+ echo >>$tmp/msg '
+options:
+ -e use espping metrics, instead of shping
+ -G gap set distance between columns (pixels)
+ -V verbose/diagnostic output
+
+pmgadgets(1) options:'
+ _pmgadgets_usage >>$tmp/msg
+ echo >>$tmp/msg
+ _pmgadgets_info -f $tmp/msg
+}
+
+# keep the original command line for restart after logout/login
+#
+echo -n "pmgadgets 1 \"pmgshping\"" >$tmp/conf
+for arg
+do
+ echo -n " \"$arg\"" >>$tmp/conf
+done
+echo "" >>$tmp/conf
+
+# get arguments
+#
+. $PCP_SHARE_DIR/lib/pmgadgets-args
+
+# default variables
+#
+gap=""
+ping=shping
+verbose=false
+
+_pmgadgets_args "$@"
+
+if [ -n "$otherArgs" ]
+then
+ while getopts "eG:V?" c $otherArgs
+ do
+ case $c
+ in
+ e)
+ ping=espping
+ ;;
+ G)
+ gap=$OPTARG
+ ;;
+ V)
+ verbose=true
+ ;;
+ ?)
+ _usage
+ status=1
+ exit
+ ;;
+ esac
+ done
+
+ set -- $otherArgs
+ shift `expr $OPTIND - 1`
+
+ if [ $# -gt 0 ]
+ then
+ _usage
+ status=1
+ exit
+ fi
+fi
+
+# check on metric availability
+#
+if _pmgadgets_fetch_indom ${ping}.time.real
+then
+ if [ ! -s $tmp/pmgadgets_result -o "$number" -lt 1 ]
+ then
+ _pmgadgets_fetch_fail "get $ping metrics"
+ #NOTREACHED
+ fi
+ # save number of instances of ${ping}.time.real to be used below
+ #
+ mynumber=$number
+else
+ _pmgadgets_fetch_fail "get $ping metrics"
+ #NOTREACHED
+fi
+
+cp $tmp/pmgadgets_result $tmp/info
+
+# default is update at the same frequency as the $ping PMDA
+# runs the commands
+#
+if [ $interval = 0 ]
+then
+ result=''
+ if _pmgadgets_fetch_values ${ping}.control.cycletime
+ then
+ if [ ! -s $tmp/pmgadgets_result -o "$number" -lt 1 ]
+ then
+ _pmgadgets_fetch_fail "get $ping cycle time"
+ #NOTREACHED
+ fi
+ else
+ _pmgadgets_fetch_fail "get $ping cycle time"
+ #NOTREACHED
+ fi
+
+ interval=`cat $tmp/pmgadgets_result`
+ if [ -z "$interval" ]
+ then
+ # if you can't get this, probably doomed, but use 10sec as a fallback
+ #
+ interval=10sec
+ else
+ interval="${interval}sec"
+ fi
+fi
+args="$args -t $interval"
+
+# $ping PMDA may not be installed here ... fake out $ping.RealTime
+# config file for pmchart and use stacked bar instead of line plot
+# because the time interval may be quite long
+pmchart_config=$tmp/config
+cat >$tmp/config <<End-of-File
+#pmchart
+Version 2.0 host dynamic
+Chart Title "Elapsed Time for $ping Commands" Style stacking
+ Plot Color #-cycle Host * Metric $ping.time.real Matching .*
+End-of-File
+
+# output the config file
+#
+start_x=8 # starting x co-ord
+start_y=8 # starting y co-ord
+diam=10 # LED diameter
+if [ -z "$gap" ]
+then
+ gap=50 # column width
+ [ "$ping" = "espping" ] && gap=150 # pmdaespping instance names are long
+fi
+
+pmchart_opts="-t $interval -c $pmchart_config"
+[ ! -z "$host" ] && pmchart_opts="$pmchart_opts -h $host"
+[ ! -z "$namespace" ] && pmchart_opts="$pmchart_opts $namespace"
+
+cat <<End-of-File >>$tmp/conf
+_actions Actions (
+"pmchart" "/usr/bin/X11/xconfirm -icon info -t 'Values plotted by pmchart will not appear' -t 'until after the first time interval ($interval)' -B Dismiss >/dev/null & pmchart $pmchart_opts"
+)
+
+_legend Status (
+ _default "#608080"
+ 1 yellow
+ 2 orange
+ 3 red
+ 4 purple
+)
+
+_legend Response (
+ _default "#608080"
+ 0 green3
+ 1000 yellow
+ 5000 orange
+ 10000 red
+)
+End-of-File
+
+# get instance information from metrics
+#
+for tag in `cat $tmp/info`
+do
+ echo "$tag"
+done \
+| $PCP_AWK_PROG -v ping=$ping >>$tmp/conf '
+BEGIN { x = '$start_x'; y = '$start_y'; diam = '$diam'; gap = '$gap'; left = 1
+ printf "_label %d %d \"%s\"\n",x,y+8,"'$host'"
+ y += 12
+ printf "_label %d %d \"%s\"\n",x,y+8,"S"
+ x = x + diam + 2
+ printf "_label %d %d \"%s\"\n",x,y+8,"Response"
+ if ('$mynumber' > 1) {
+ x = x + diam + 2
+ x = x + gap
+ printf "_label %d %d \"%s\"\n",x,y+8,"S"
+ x = x + diam + 2
+ printf "_label %d %d \"%s\"\n",x,y+8,"Response"
+ }
+ y += 12
+ }
+ {
+ if (left) {
+ print ""
+ x = '$start_x'
+ }
+ else
+ x = x + gap
+ print "_led " x " " y " " diam " " diam
+ print " _metric " ping".status[\"" $1 "\"]"
+ print " _legend Status"
+ x = x + diam + 2
+ print "_led " x " " y " " diam " " diam
+ print " _metric " ping ".time.real[\"" $1 "\"]"
+ print " _legend Response"
+ print " _actions Actions"
+ x = x + diam + 2
+ printf "_label %d %d \"%s\"\n",x,y+diam-2,$1
+ if (!left) {
+ y = y + diam + 2
+ }
+ left = 1-left
+ }'
+
+$verbose && cat $tmp/conf
+eval pmgadgets <$tmp/conf $args
+
+status=$?
+exit
diff --git a/src/pmgadgets/pmgsys.py b/src/pmgadgets/pmgsys.py
new file mode 100755
index 0000000..5f8a1df
--- /dev/null
+++ b/src/pmgadgets/pmgsys.py
@@ -0,0 +1,380 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2014 Red Hat.
+#
+# 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.
+#
+
+""" Rewrite of pmgsys (originally a C++ application) in python
+
+ Needs to handle the following options at some point:
+ -h (host), -cpudelta (interval), -l (label), -v (verbose),
+ -c (config), -zoom (factor), ... rest through to pmgadgets.
+"""
+
+from sys import argv
+from pcp import pmapi
+from math import sqrt
+from cpmapi import PM_TYPE_U32
+
+PCPARGS = "" # for pmgadgets-launched child processes to use
+
+# magic numbers, from original pmgsys algorithms
+CPUDELTA = 0.5
+LOADDELTA = 5
+FONTASCENT = 7
+DOLABEL = 1
+HSPACE = 5
+VSPACE = 2
+CPUWIDTH = 30
+CPUHEIGHT = 4
+LOADWIDTH = CPUWIDTH
+LOADHEIGHT = 4 * CPUHEIGHT + 3 * VSPACE
+MEMWIDTH = CPUHEIGHT
+MEMHEIGHT = LOADHEIGHT
+DISKSIZE = 6
+NETWIDTH = CPUWIDTH
+NETHEIGHT = CPUHEIGHT
+
+class Machine(object):
+ def __init__(self, context):
+ # pcp context
+ self.context = context
+ # hardware bits
+ self.ncpu = 0
+ self.ndisk = 0
+ self.niface = 0
+ self.ndiskmaps = 0
+ self.memory = 0
+ # external names
+ self.cpus = []
+ self.disks = []
+ self.parts = []
+ self.ifaces = []
+ self.diskmaps = []
+
+ def get_hinv(self):
+ """ Extract counts of CPUs, disks, interfaces and memory size
+ """
+ hinv = ('hinv.ncpu', 'hinv.ndisk', 'hinv.ninterface', 'hinv.physmem')
+ pmids = self.context.pmLookupName(hinv)
+ descs = self.context.pmLookupDescs(pmids)
+ result = self.context.pmFetch(pmids)
+ hardware = [0, 0, 0, 0]
+ for x in xrange(4):
+ atom = self.context.pmExtractValue(
+ result.contents.get_valfmt(x),
+ result.contents.get_vlist(x, 0),
+ descs[x].contents.type, PM_TYPE_U32)
+ hardware[x] = atom.ul
+ context.pmFreeResult(result)
+ self.ncpu = hardware[0]
+ self.ndisk = hardware[1]
+ self.niface = hardware[2]
+ self.memory = hardware[3]
+
+ def get_names(self):
+ """ Extract names of CPUs, disks and network interfaces.
+ """
+ inst = ('kernel.percpu.cpu.user', # expand CPU names
+ 'disk.dev.total', # expand disk names
+ 'disk.partitions.total', # expand disk partition names
+ 'network.interface.total.bytes') # expand network interface names
+ pmids = self.context.pmLookupName(inst)
+ descs = self.context.pmLookupDescs(pmids)
+
+ (inst, self.cpus) = self.context.pmGetInDom(descs[0])
+ (inst, self.disks) = self.context.pmGetInDom(descs[1])
+ (inst, self.parts) = self.context.pmGetInDom(descs[2])
+ (inst, self.ifaces) = self.context.pmGetInDom(descs[3])
+
+ def details(self):
+ print "CPUs: ", self.ncpu
+ print "CPU names: ", self.cpus
+ print "Disks: ", self.ndisk
+ print "Disk names: ", self.disks
+ print "Partition names: ", self.parts
+ print "Interfaces: ", self.niface
+ print "Interface names: ", self.ifaces
+ print "Memory: ", self.memory
+
+ def get_diskmaps(self):
+ """ Produce disk -> partition mappings
+ This means grouping "sda1 sda2 sda3 sdb1" into two mappings
+ - "sda" -> (sda1, sda2, sda3) and "sdb" -> (sdb1); done via
+ the disk.dev.total and disk.partitions.total metrics.
+ (original: controller -> disk mappings, but ENODATA)
+ """
+ return 0
+
+ def inventory(self):
+ """ Wrap calls to getting counts and subsystem names
+ """
+ self.get_hinv()
+ self.get_names()
+ self.get_diskmaps()
+
+ def gadgetize(self):
+ """ Generate a pmgadgets configuration for this host
+ """
+ print "pmgadgets 1", # follow with command line
+ for arg in argv:
+ print "\"%s\"" % (arg),
+ print
+
+ rows = 1
+ ctiles = int((self.ncpu - 1) / 4 + 1) # always at least one cpu
+ ntiles = int((self.niface - 1) / 4 + 1)
+ if ctiles > 3:
+ cr = int(math.sqrt(ctiles))
+ if cr > rows:
+ rows = cr
+ if ntiles > 3:
+ nr = int(math.sqrt(ntiles))
+ if nr > rows:
+ rows = nr
+
+ baseY = VSPACE
+ if DOLABEL == 1:
+ y = FONTASCENT + VSPACE
+ hostname = self.context.pmGetContextHostName()
+ print "_label %d %d \"%s\"" % (HSPACE, y, hostname)
+ baseY += y
+ baseX = maxX = HSPACE
+ maxY = baseY
+
+ print "_actions cpuActions ("
+ print " \"pmchart\"\t\t\"pmchart -c CPU%s\"" % (PCPARGS)
+ print " \"mpvis *\"\t\t\"mpvis%s\" _default" % (PCPARGS)
+ # original had IRIX gr_top and gr_osview tools next;
+ # perhaps some fine day we could implement these as
+ # pmgadgets front-end tools (certainly the latter)
+ print ")"
+
+ y = baseY + FONTASCENT
+ x = baseX
+ print "_label %d %d \"CPU\"" % (x, y)
+ print " _actions cpuActions\n"
+ # original: "these should match the colours in mpvis"
+ print "_colourlist cpuColours (blue3 red3 yellow3 cyan3 green3)"
+
+ # place the CPU bars
+ y += VSPACE
+ ccols = int((ctiles + rows - 1) / rows)
+
+ cpu = 0
+ for rc in range(0, self.ncpu):
+ for ct in range(0, ccols):
+ tc = 0
+ while (cpu < self.ncpu and tc < 4):
+ print "_multibar %d %d %d %d" % (x, y, CPUWIDTH, CPUHEIGHT)
+ print(" _update %f" % (CPUDELTA)).rstrip('0').rstrip('.')
+ print " _metrics ("
+ print "\tkernel.percpu.cpu.user[\"%s\"]" % (self.cpus[cpu])
+ print "\tkernel.percpu.cpu.sys[\"%s\"]" % (self.cpus[cpu])
+ print "\tkernel.percpu.cpu.intr[\"%s\"]" % (self.cpus[cpu])
+ print "\tkernel.percpu.cpu.wait.total[\"%s\"]" % (self.cpus[cpu])
+ print "\tkernel.percpu.cpu.idle[\"%s\"]" % (self.cpus[cpu])
+ print " )"
+ print " _maximum 0.0\n"
+ print " _colourlist cpuColours"
+ print " _actions cpuActions\n"
+ cpu += 1
+ tc += 1
+ y += VSPACE + CPUHEIGHT
+
+ if maxY < y:
+ maxY = y
+ y -= (VSPACE + CPUHEIGHT) * tc
+ x += HSPACE + CPUWIDTH
+
+ y += (CPUHEIGHT + VSPACE) * 4 + VSPACE
+ if (maxX < x):
+ maxX = x
+ x = baseX
+
+ baseX += (HSPACE + CPUWIDTH) * ccols
+
+ # The load gadget and its label
+ print "_actions loadActions ("
+ print " \"pmchart *\"",
+ print "\t\"pmchart -c LoadAvg%s\" _default" % (PCPARGS)
+ # original had IRIX gr_top here
+ print ")"
+ print "_label %d %d \"Load\"" % (baseX, baseY + FONTASCENT)
+ print " _actions loadActions"
+ print
+
+ y = VSPACE + baseY + FONTASCENT
+
+ i = y + LOADHEIGHT
+ if (i > maxY):
+ maxY = i
+
+ print "_bargraph %d %d %d %d" % (baseX, y, LOADWIDTH, LOADHEIGHT)
+ print(" _update %f" % (LOADDELTA)).rstrip('0').rstrip('.')
+ print " _metric kernel.all.load[\"1 minute\"]"
+ print " _max 1.0"
+ print " _actions loadActions"
+
+ # For more than one row, stack LoadAvg on top of Memory.
+ #
+ # Move baseX just after the right hand side of the memory, so
+ # we don't have to do anything special for the netifs. For the
+ # sake of argument, consider total width occupied by memory
+ # gauges equal to total width of loadavg graph
+ if (rows > 1):
+ y += LOADHEIGHT + VSPACE
+ baseX += LOADWIDTH + HSPACE
+ else:
+ y += baseY
+ baseX += LOADWIDTH * 2 + HSPACE * 2
+
+ # The memory gadgets and their label (platform-specific!)
+ x = baseX - LOADWIDTH - HSPACE
+ y += FONTASCENT
+ print "_label %d %d \"Mem\"\n" % (x, y)
+ print "_colourlist memColours (cyan1 red yellow green)\n"
+ y += VSPACE
+ print "_multibar %d %d %d %d" % (x, y, MEMWIDTH, MEMHEIGHT)
+ print " _update 0.5"
+ print " _metrics ("
+ print "\tmem.util.cached"
+ print "\tmem.util.bufmem"
+ print "\tmem.util.other"
+ print "\tmem.util.free"
+ print " )"
+ print " _colourlist memColours"
+ x += HSPACE + MEMWIDTH
+ print "_bar %d %d %d %d" % (x, y, MEMWIDTH, MEMHEIGHT)
+ print " _metric swap.pagesout"
+ print " _vertical"
+
+ # Check for the max horizontal offset
+ i = y + MEMHEIGHT
+ if (i > maxY):
+ maxY = i
+
+ # The network bars and their label
+ print "_colourlist netColours (aquamarine orange)"
+ print "_actions netActions ("
+ print " \"pmchart-packets *\"",
+ print "\t\"pmchart -c NetPackets%s\" _default" % (PCPARGS)
+ print " \"pmchart-bytes\"",
+ print "\t\t\"pmchart -c NetBytes%s\"" % (PCPARGS)
+ # original had netstat within an xterm here, next
+ print ")"
+
+ y = baseY + FONTASCENT
+ x = baseX
+
+ print "_label %d %d \"Net\"" % (x, y)
+ print " _actions netActions\n"
+
+ y += VSPACE
+
+ ncols = int((ntiles + rows - 1) / rows)
+
+ ni = 0
+ while ni < self.niface:
+ for nt in range(0, ncols):
+ tc = 0
+ while ni < self.niface and tc < 4:
+ print "_multibar %d %d %d %d" % (x, y, NETWIDTH, NETHEIGHT)
+ print " _metrics ("
+ print "\tnetwork.interface.in.bytes[\"%s\"]" % (self.ifaces[ni])
+ print "\tnetwork.interface.out.bytes[\"%s\"]" % (self.ifaces[ni])
+ print " )"
+ print "_colourlist netColours"
+ print " _actions netActions"
+ y += NETHEIGHT + VSPACE
+
+ tc += 1
+ ni += 1
+
+ if maxY < y:
+ maxY = y
+ y -= (NETHEIGHT + VSPACE) * tc
+ x += NETWIDTH + HSPACE
+ if maxX < x:
+ maxX = x
+ y += (NETHEIGHT + VSPACE) * 4 + VSPACE
+ x = baseX
+ print
+
+ # Disks
+ dir = 1
+
+ print "_actions diskActions ("
+ print " \"pmchart\"",
+ print "\t\t\"pmchart -c Disk%s\"" % (PCPARGS)
+ print " \"dkvis *\"",
+ print "\t\t\"dkvis%s\" _default" % (PCPARGS)
+ print ")"
+
+ x = HSPACE
+ y = maxY + FONTASCENT + 2 * VSPACE
+ print "_label %d %d \"Disk\"" % (x, y)
+ print " _actions diskActions\n"
+ print "_legend diskLegend ("
+ print " _default green3"
+ print " 15 yellow"
+ print " 40 orange"
+ print " 75 red"
+ print ")"
+
+ x += CPUWIDTH + HSPACE
+ # this only works if FONTASCENT >= ledSize
+ y -= DISKSIZE
+ thickness = 4
+ halfDiskSize = int((DISKSIZE - thickness) / 2)
+
+ for i in range(0, self.ndiskmaps):
+ mapping = self.diskmaps[i]
+ for j in range(0, len(mappings)):
+ if j > 0:
+ if oldX < x: # moved to right
+ lx = x - VSPACE - 1
+ ly = y + halfDiskSize
+ lw = VSPACE + 2
+ lh = thickness
+ elif oldX > x: # moved to left
+ lx = oldX - VSPACE - 1
+ ly = y + halfDiskSize
+ lw = VSPACE + 2
+ lh = thickness
+ else: # moved down
+ lx = x + halfDiskSize
+ ly = oldY + DISKSIZE - 1
+ lw = thickness
+ lh = VSPACE + 2
+ print "_line %d %d %d %d" % (lx, ly, lw, lh)
+ print "_led %d %d %d %d" % (x, y, DISKSIZE, DISKSIZE)
+ print " _metric disk.dev.total[\"%s\"]" % (mapping.name())
+ print " _legend diskLegend"
+ print " _actions diskActions"
+ oldX = x
+ oldY = y
+ xStep = dir * (DISKSIZE + VSPACE) # use VSPACE (tighter packing)
+ x += xStep
+ if x > maxX - DISKSIZE or x <= HSPACE:
+ x -= xStep
+ y += DISKSIZE + VSPACE
+ dir = -dir
+
+
+if __name__ == '__main__':
+ context = pmapi.pmContext()
+ machine = Machine(context)
+ machine.inventory()
+ # machine.details()
+ machine.gadgetize()
+
diff --git a/src/pmgadgets/tokens.h b/src/pmgadgets/tokens.h
new file mode 100644
index 0000000..b299bf8
--- /dev/null
+++ b/src/pmgadgets/tokens.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1996 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.
+ */
+
+/* gadget tokens */
+#define TOK_LINE 1
+#define TOK_LABEL 2
+#define TOK_BAR 3
+#define TOK_MULTIBAR 4
+#define TOK_BARGRAPH 5
+#define TOK_LED 6
+
+/* gadget building block tokens */
+#define TOK_LEGEND 100
+#define TOK_COLOURLIST 101
+#define TOK_ACTIONLIST 102
+
+/* other reserved words' tokens */
+#define TOK_BAD_RES_WORD 200
+#define TOK_UPDATE 201
+#define TOK_METRIC 202
+#define TOK_HORIZONTAL 203
+#define TOK_VERTICAL 204
+#define TOK_METRICS 205
+#define TOK_MIN 206
+#define TOK_MAX 207
+#define TOK_DEFAULT 208
+#define TOK_FIXED 209
+#define TOK_COLOUR 210
+#define TOK_HISTORY 211
+#define TOK_NOBORDER 212
+
+/* other lexical symbols' tokens */
+#define TOK_IDENTIFIER 300
+#define TOK_INTEGER 301
+#define TOK_REAL 302
+#define TOK_STRING 303
+#define TOK_LPAREN 304
+#define TOK_RPAREN 305
+#define TOK_LBRACKET 306
+#define TOK_RBRACKET 307
+#define TOK_COLON 308
+
+/* end of file */
+#define TOK_EOF 666
+
+extern unsigned nLines;
+extern int tokenIntVal;
+extern double tokenRealVal;
+extern char* tokenStringVal;