summaryrefslogtreecommitdiff
path: root/src/pmieconf/rules.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmieconf/rules.c')
-rw-r--r--src/pmieconf/rules.c2335
1 files changed, 2335 insertions, 0 deletions
diff --git a/src/pmieconf/rules.c b/src/pmieconf/rules.c
new file mode 100644
index 0000000..cb0c9a0
--- /dev/null
+++ b/src/pmieconf/rules.c
@@ -0,0 +1,2335 @@
+/*
+ * rules.c - rule description parsing routines (rules & pmie config)
+ *
+ * Copyright (c) 1998-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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <ctype.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include "pmapi.h"
+#include "impl.h"
+#include "rules.h"
+#include "stats.h"
+
+#define SEP __pmPathSeparator()
+
+
+#define PMIE_FILE "pmieconf-pmie"
+#define PMIE_VERSION "1" /* local configurations file format version */
+#define RULES_FILE "pmieconf-rules"
+#define RULES_VERSION "1" /* rule description file format version */
+
+#define START_STRING \
+ "// --- START GENERATED SECTION (do not change this section) ---\n"
+#define END_STRING \
+ "// --- END GENERATED SECTION (changes below will be preserved) ---\n"
+#define TOKEN_LENGTH 2048 /* max length of input token, incl string */
+#define LINE_LENGTH 4096
+
+#if !defined(sgi)
+#define PROC_DIR "/proc"
+#else
+#define PROC_DIR "/proc/pinfo"
+#endif
+
+char errmsg[512]; /* error message buffer */
+char rulepath[MAXPATHLEN+1]; /* root of rules files */
+char pmiefile[MAXPATHLEN+1]; /* pmie configuration file */
+char token[TOKEN_LENGTH+1];
+
+rule_t *rulelist; /* global list of rules */
+unsigned int rulecount; /* # rule list elements */
+rule_t *globals; /* list of atoms with global scope */
+
+#define GLOBAL_LEN 7
+static char global_name[] = "global"; /* GLOBAL_LEN chars long */
+static char global_data[] = "generic variables applied to all rules";
+static char global_help[] = \
+ "The global variables are used by all rules, but their values can be\n"
+ "overridden at the level of an individual rule or group of rules.";
+static char yes[] = "yes";
+static char no[] = "no";
+
+static char *filename; /* file currently being parsed */
+static unsigned int linenum; /* input line number */
+
+symbol_t types[] = {
+ { TYPE_STRING, "string" }, /* predicate data types */
+ { TYPE_DOUBLE, "double" },
+ { TYPE_INTEGER, "integer" },
+ { TYPE_UNSIGNED, "unsigned" },
+ { TYPE_PERCENT, "percent" },
+ { TYPE_HOSTLIST, "hostlist" },
+ { TYPE_INSTLIST, "instlist" },
+ { TYPE_PRINT, "print" }, /* action types */
+ { TYPE_SHELL, "shell" },
+ { TYPE_ALARM, "alarm" },
+ { TYPE_SYSLOG, "syslog" },
+ { TYPE_RULE, "rule" }, /* fundamental type */
+};
+int numtypes = (sizeof(types)/sizeof(types[0]));
+
+symbol_t attribs[] = {
+ { ATTRIB_HELP, "help" },
+ { ATTRIB_MODIFY, "modify" },
+ { ATTRIB_ENABLED, "enabled" },
+ { ATTRIB_DISPLAY, "display" },
+ { ATTRIB_DEFAULT, "default" },
+ { ATTRIB_DEFAULT, "summary" }, /* alias for "default" */
+ { ATTRIB_VERSION, "version" }, /* applies to rules only */
+ { ATTRIB_PREDICATE, "predicate" }, /* applies to rules only */
+ { ATTRIB_ENUMERATE, "enumerate" }, /* applies to rules only */
+};
+int numattribs = (sizeof(attribs)/sizeof(attribs[0]));
+
+/* pmiefile variables */
+static int gotpath; /* state flag - has realpath been run */
+static char *save_area; /* holds text to restore on write */
+static int sa_size; /* current size of save area */
+static int sa_mark = 1; /* number used chars in save area, 1 for \0 */
+static dep_t *dlist; /* list of depreciated rules */
+static int dcount; /* number of entries in dlist */
+static char drulestring[] = "rule definition no longer exists";
+static char dverstring[] = "rule version no longer supported";
+
+/* io-related stuff */
+extern int resized(void);
+
+char *get_pmiefile(void) { return &pmiefile[0]; }
+char *get_rules(void) { return &rulepath[0]; }
+
+char *
+get_aname(rule_t *r, atom_t *a)
+{
+ if (r == globals)
+ return &a->name[GLOBAL_LEN]; /* lose "globals." at the start */
+ return a->name;
+}
+
+
+/*
+ * #### error reporting routines ###
+ */
+
+static void
+alloc_error(size_t request)
+{
+ if (linenum == 0) /* parsing user input, not a file */
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for requested operation.\n"
+ " requested: %u bytes", (unsigned int)request);
+ else
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for parsing file.\n"
+ " requested: %u bytes", (unsigned int)request);
+}
+
+static void
+parse_error(char *expected, char *found)
+{
+ if (linenum == 0) /* parsing user input, not a file */
+ snprintf(errmsg, sizeof(errmsg), "input is invalid - expected %.60s, got \"%.60s\"",
+ expected, found);
+ else
+ snprintf(errmsg, sizeof(errmsg), "file parsing error.\n"
+ " line number: %u (\"%s\")\n"
+ " expected: %.60s\n"
+ " found: %.60s", linenum, filename, expected, found);
+}
+
+/* report attribute format error */
+static void
+type_error(char *attrib, char *expected)
+{
+ snprintf(errmsg, sizeof(errmsg), "%s's value is invalid.\n"
+ " It should %s.", attrib, expected);
+}
+
+
+/*
+ * #### search routines ###
+ */
+
+char *
+find_rule(char *name, rule_t **rule)
+{
+ int i;
+
+ for (i = 0; i < rulecount; i++) {
+ if (strcmp(rulelist[i].self.name, name) == 0) {
+ *rule = &rulelist[i];
+ return NULL;
+ }
+ }
+ snprintf(errmsg, sizeof(errmsg), "rule named \"%s\" does not exist", name);
+ return errmsg;
+}
+
+/* is global attribute 'atom' overridden by a local in 'rule's atom list */
+int
+is_overridden(rule_t *rule, atom_t *atom)
+{
+ atom_t *aptr;
+
+ for (aptr = rule->self.next; aptr != NULL; aptr = aptr->next)
+ if (strcmp(get_aname(globals, atom), get_aname(rule, aptr)) == 0)
+ return 1;
+ return 0;
+}
+
+/* tests whether a rule is in the fullname group, if so returns 0 */
+int
+rule_match(char *fullname, char *rulename)
+{
+ char *s;
+
+ /* if fullname == rulename, then obvious match */
+ if (strcmp(fullname, rulename) == 0)
+ return 1;
+ /* fullname may be a group, so match against rulename's groups */
+ s = strcpy(token, rulename); /* reuse the token buffer */
+ while ((s = strrchr(s, '.')) != NULL) {
+ s[0] = '\0';
+ if (strcmp(token, fullname) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/* find rule or set of rules in given rule or group name */
+char *
+lookup_rules(char *name, rule_t ***rlist, unsigned int *count, int all)
+{
+ size_t size;
+ rule_t **rptr = NULL;
+ unsigned int i;
+ unsigned int matches = 0;
+
+ /* search through the rulelist and build up rlist & count */
+ for (i = 0; i < rulecount; i++) {
+ /* don't match globals if we've been asked for "all" */
+ if ((all && i > 0) || rule_match(name, rulelist[i].self.name)) {
+ size = (1 + matches) * sizeof(rule_t *);
+ if ((rptr = (rule_t **)realloc(rptr, size)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for rule search"
+ " (needed %u bytes)\n", (unsigned int)size);
+ return errmsg;
+ }
+ rptr[matches] = &rulelist[i];
+ matches++;
+ }
+ }
+ if (matches == 0) {
+ snprintf(errmsg, sizeof(errmsg), "no group or rule names match \"%s\"", name);
+ return errmsg;
+ }
+ *rlist = rptr; /* rlist must be freed by caller */
+ *count = matches;
+ return NULL;
+}
+
+
+/*
+ * #### memory management routines ###
+ */
+
+static char *
+alloc_string(size_t size)
+{
+ char *p;
+
+ if ((p = (char *)malloc(size)) == NULL)
+ alloc_error(size);
+ return p;
+}
+
+atom_t *
+alloc_atom(rule_t *r, atom_t atom, int global)
+{
+ atom_t *aptr;
+ atom_t *tmp;
+
+ /* create some space and copy in the atom data we have already */
+ if ((aptr = (atom_t *)malloc(sizeof(atom_t))) == NULL) {
+ alloc_error(sizeof(atom_t));
+ return NULL;
+ }
+ *aptr = atom;
+ aptr->next = NULL; /* want contents of this atom, but not rest of list */
+ if (global) { /* applies to all rules */
+ r = rulelist;
+ aptr->global = 1;
+ }
+
+ aptr->next = NULL; /* want contents of this atom, but not rest of list */
+
+ /* stick into the list of atoms associated with this rule */
+ if (r->self.next == NULL)
+ r->self.next = aptr; /* insert at head of list */
+ else {
+ for (tmp = r->self.next; tmp->next != NULL; tmp = tmp->next);
+ tmp->next = aptr; /* append at tail of list */
+ }
+
+ return aptr;
+}
+
+rule_t *
+alloc_rule(rule_t rule)
+{
+ size_t size;
+ rule_t *rptr;
+
+ /* first check that name is unique */
+ if (find_rule(rule.self.name, &rptr) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "rule name \"%s\" has already been used, duplicate name"
+ " found in:\n\t\"%.60s\", line %u.", rule.self.name, filename, linenum);
+ return NULL;
+ }
+ size = (rulecount+1) * sizeof(rule_t);
+ if ((rulelist = globals = (rule_t *)realloc(rulelist, size)) == NULL) {
+ alloc_error(size);
+ return NULL;
+ }
+ rptr = &rulelist[rulecount];
+ *rptr = rule;
+ rulecount++;
+ return rptr;
+}
+
+
+/*
+ * #### misc parsing routines ###
+ */
+
+/* given string contains no isgraph chars? */
+int
+empty_string(char *s)
+{
+ char *str = s;
+ while (*str != '\0') {
+ if (isgraph((int)*str))
+ return 0;
+ str++;
+ }
+ return 1;
+}
+
+
+/* lookup keyword, returns symbol identifier or -1 if not there */
+int
+map_symbol(symbol_t *table, int tsize, char *symbol)
+{
+ int i;
+
+ for (i = 0; i < tsize; i++) {
+ if (strcmp(symbol, table[i].symbol) == 0)
+ return table[i].symbol_id;
+ }
+ return -1;
+}
+
+/* lookup symbol identifier, returns keyword or NULL if not there */
+char *
+map_identifier(symbol_t *table, int tsize, int symbol_id)
+{
+ int i;
+
+ for (i = 0; i < tsize; i++) {
+ if (symbol_id == table[i].symbol_id)
+ return table[i].symbol;
+ }
+ return NULL;
+}
+
+
+/* parse yes/no attribute value; returns 0 no, 1 yes, -1 error */
+int
+map_boolean(char *token)
+{
+ if (token[0] == 'y')
+ return 1;
+ if (token[0] == 'n')
+ return 0;
+ parse_error("yes or no", token);
+ return -1;
+}
+
+
+/* scan token from string, return 1 ok, 0 no more, -1 error */
+int
+string_token(char **scan, char *token)
+{
+ char *s = *scan;
+ char *t = token;
+
+ while (! isgraph((int)*s) || *s == ',') {
+ if (*s == '\0')
+ return 0;
+ s++;
+ }
+
+ if (*s == '\'') { /* quoted token */
+ *t++ = *s++;
+ while (*s != '\'') {
+ if (*s == '\\')
+ s++;
+ if (*s == '\0')
+ return -1;
+ *t++ = *s++;
+ }
+ *t++ = *s++;
+ }
+ else { /* ordinary token */
+ while (isgraph((int)*s) && *s != ',')
+ *t++ = *s++;
+ }
+
+ *t = '\0';
+ *scan = s;
+ return 1;
+}
+
+
+/* check proposed value for type, returns NULL/failure message */
+char *
+validate(int type, char *name, char *value)
+{
+ int x;
+ char *s;
+ double d;
+ /*
+ * Below we don't care about the value from strtol() and strtoul()
+ * we're interested in updating the pointer "s". The messiness is
+ * thanks to gcc and glibc ... strtol() amd strtoul() are marked
+ * __attribute__((warn_unused_result)) ... to avoid warnings on all
+ * platforms, assign to dummy variables that are explicitly marked
+ * unused.
+ */
+ long l __attribute__((unused));
+ unsigned long ul __attribute__((unused));
+
+ switch (type) {
+ case TYPE_RULE:
+ case TYPE_STRING:
+ break;
+ case TYPE_SHELL:
+ case TYPE_PRINT:
+ case TYPE_ALARM:
+ case TYPE_SYSLOG:
+ if (map_boolean(value) < 0)
+ return errmsg;
+ break;
+ case TYPE_DOUBLE:
+ d = strtod(value, &s);
+ if (*s != '\0') {
+ type_error(name, "be a real number");
+ return errmsg;
+ }
+ break;
+ case TYPE_INTEGER:
+ l = strtol(value, &s, 10);
+ if (*s != '\0') {
+ type_error(name, "be an integer number");
+ return errmsg;
+ }
+ break;
+ case TYPE_UNSIGNED:
+ ul = strtoul(value, &s, 10);
+ if (*s != '\0') {
+ type_error(name, "be a positive integer number");
+ return errmsg;
+ }
+ break;
+ case TYPE_PERCENT:
+ if ((s = strrchr(value, '%')) != NULL) /* % as final char is OK */
+ *s = '\0';
+ d = strtod(value, &s);
+ if (*s != '\0' || d < 0.0 || d > 100.0) {
+ type_error(name, "be a percentage between 0.0 and 100.0");
+ return errmsg;
+ }
+ break;
+ case TYPE_HOSTLIST:
+ case TYPE_INSTLIST:
+ if ((s = alloc_string(strlen(value)+1)) == NULL)
+ return errmsg;
+ while ((x = string_token(&value, s)) > 0)
+ ;
+ if (x < 0) {
+ type_error(name, "include a closing single quote");
+ return errmsg;
+ }
+ free(s);
+ break;
+ }
+ return NULL;
+}
+
+
+/*
+ * printable string form of atoms value, returns NULL terminated string
+ * pp (pretty print) argument valued 1 means use format appropriate for
+ * a user interface
+ */
+
+char *
+value_string(atom_t *atom, int pp)
+{
+ int key;
+ int i = 0;
+ int start = 1;
+ int quoted = 0;
+ char *s;
+
+ switch (atom->type) {
+ case TYPE_RULE:
+ case TYPE_STRING:
+ if (pp) {
+ snprintf(token, sizeof(token), "\"%s\"", atom->data);
+ return token;
+ }
+ return atom->data;
+ case TYPE_PRINT:
+ case TYPE_SHELL:
+ case TYPE_ALARM:
+ case TYPE_SYSLOG:
+ return atom->enabled? yes : no;
+ case TYPE_HOSTLIST:
+ case TYPE_INSTLIST:
+ if (pp) token[i++] = '"';
+ if (atom->type == TYPE_HOSTLIST) key = ':';
+ else key = '#';
+ for (s = atom->data; *s != '\0'; s++) {
+ if (!isspace((int)*s)) {
+ if (start && !pp) {
+ token[i++] = key;
+ token[i++] = '\'';
+ if (*s != '\'')
+ quoted = 0;
+ else if (!quoted) {
+ quoted = 1;
+ start = 0;
+ continue;
+ }
+ }
+ else if (*s == '\'' && !start && !pp)
+ quoted = 0;
+ start = 0;
+ }
+ else if (!pp && !quoted) {
+ quoted = 0;
+ if (i > 0 && token[i-1] != '\'')
+ token[i++] = '\'';
+ start = 1;
+ }
+ token[i++] = *s;
+ }
+ if (!pp && i > 0 && token[i-1] != '\'')
+ token[i++] = '\'';
+ else if (pp) token[i++] = '"';
+ token[i++] = '\0';
+ return token;
+ case TYPE_DOUBLE:
+ snprintf(token, sizeof(token), "%g", strtod(atom->data, &s));
+ return token;
+ case TYPE_INTEGER:
+ snprintf(token, sizeof(token), "%ld", strtol(atom->data, &s, 10));
+ return token;
+ case TYPE_UNSIGNED:
+ snprintf(token, sizeof(token), "%lu", strtoul(atom->data, &s, 10));
+ return token;
+ case TYPE_PERCENT:
+ snprintf(token, sizeof(token), "%g%c", strtod(atom->data, &s), pp? '%':'\0');
+ return token;
+ }
+ return NULL;
+}
+
+
+/* #### rules file parsing routines #### */
+
+
+/* returns attrib number or -1 if not an attribute */
+int
+is_attribute(char *aname)
+{
+ return map_symbol(attribs, numattribs, aname);
+}
+
+/* returns attrib value as a string, or NULL on error */
+char *
+get_attribute(char *attrib, atom_t *atom)
+{
+ char *value = NULL;
+
+ switch (map_symbol(attribs, numattribs, attrib)) {
+ case ATTRIB_HELP:
+ value = atom->help;
+ break;
+ case ATTRIB_MODIFY:
+ if (atom->modify) value = yes;
+ else value = no;
+ break;
+ case ATTRIB_ENABLED:
+ if (atom->enabled) value = yes;
+ else value = no;
+ break;
+ case ATTRIB_DISPLAY:
+ if (atom->display) value = yes;
+ else value = no;
+ break;
+ case ATTRIB_DEFAULT:
+ if (IS_ACTION(atom->type)) {
+ if (atom->enabled) value = yes;
+ else value = no;
+ }
+ else
+ value = atom->data;
+ break;
+ }
+ return value;
+}
+
+
+/*
+ * #### sorting routines ###
+ */
+
+static int
+compare_rules(const void *a, const void *b)
+{
+ rule_t *ra = (rule_t *)a;
+ rule_t *rb = (rule_t *)b;
+ return strcmp(ra->self.name, rb->self.name);
+}
+
+void
+sort_rules(void)
+{
+ /* start at second array element so that 'globals' is skipped */
+ qsort(&rulelist[1], rulecount-1, sizeof(rule_t), compare_rules);
+}
+
+
+/* revert to default rules file values for a single atom (enabled/data/both) */
+static char *
+atom_defaults(atom_t *a, atom_t *p, char *param)
+{
+ int sts = map_symbol(attribs, numattribs, param);
+
+ if (sts != -1) { /* an attribute - is it valid? */
+ if (sts == ATTRIB_ENABLED) {
+ if (a->global) { /* this was a global atom promoted to local */
+ if (p) p->next = a->next;
+ free(a->name);
+ free(a);
+ a = NULL;
+ }
+ else {
+ a->enabled = a->denabled; /* reset enabled flag */
+ a->changed = 0;
+ }
+ return NULL;
+ }
+ snprintf(errmsg, sizeof(errmsg), "variable \"%s\" is inappropriate for this "
+ "operation", param);
+ return errmsg;
+ }
+ else {
+ if (a->global) { /* this was a global atom promoted to local */
+ if (p) p->next = a->next;
+ free(a->name);
+ free(a);
+ a = NULL;
+ }
+ else {
+ if (strcmp(a->data, a->ddata) != 0) { /* need to alloc mem? */
+ free(a->data);
+ if ((a->data = strdup(a->ddata)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to set defaults");
+ return errmsg;
+ }
+ }
+ a->enabled = a->denabled;
+ a->changed = 0;
+ }
+ return NULL;
+ }
+}
+
+/* revert to default rules values for a rule or attribute (enabled/data/both) */
+char *
+rule_defaults(rule_t *rule, char *param)
+{
+ atom_t *aptr;
+ atom_t *prev = NULL;
+
+ if (param == NULL) { /* for this rule, reset all attributes */
+ for (aptr = &rule->self; aptr != NULL; aptr = aptr->next) {
+ atom_defaults(aptr, prev, aptr->name);
+ prev = aptr;
+ }
+ }
+ else { /* find the associated atom, and just reset that */
+ if (map_symbol(attribs, numattribs, param) != -1) {
+ rule->self.enabled = rule->self.denabled; /* reset enabled flag */
+ rule->self.changed = 0;
+ return NULL;
+ }
+ for (aptr = &rule->self; aptr != NULL; aptr = aptr->next) {
+ if (strcmp(get_aname(rule, aptr), param) == 0)
+ return atom_defaults(aptr, prev, param);
+ prev = aptr;
+ }
+ }
+ return NULL;
+}
+
+/* set an attribute field in an atom; returns NULL/failure message */
+static char *
+set_attribute(rule_t *r, atom_t *atom, int attrib, char *value, int changed)
+{
+ char *s;
+ int sts;
+
+ switch(attrib) {
+ case ATTRIB_HELP:
+ if (empty_string(value)) {
+ parse_error("non-empty string for help", value);
+ return errmsg;
+ }
+ if ((s = alloc_string(strlen(value)+1)) == NULL)
+ return errmsg;
+ atom->help = strcpy(s, value);
+ break;
+ case ATTRIB_MODIFY:
+ if ((sts = map_boolean(value)) < 0)
+ return errmsg;
+ atom->modify = sts;
+ break;
+ case ATTRIB_ENABLED:
+ if ((sts = map_boolean(value)) < 0)
+ return errmsg;
+ if (!changed) /* initially, set enabled to default */
+ atom->denabled = sts;
+ atom->enabled = sts;
+ break;
+ case ATTRIB_DISPLAY:
+ if ((sts = map_boolean(value)) < 0)
+ return errmsg;
+ atom->display = sts;
+ break;
+ case ATTRIB_DEFAULT:
+ if (IS_ACTION(atom->type) && changed) {
+ if ((sts = map_boolean(value)) < 0)
+ return errmsg;
+ atom->enabled = sts;
+ }
+ else { /* actions from rules file (string) handled here too... */
+ if (!IS_ACTION(atom->type) &&
+ (validate(atom->type, get_aname(r, atom), value) != NULL))
+ return errmsg;
+ sts = strlen(value)+1;
+ if ((s = alloc_string(sts)) == NULL)
+ return errmsg;
+ atom->data = strcpy(s, value);
+ if (!changed) { /* initially, set the default as well */
+ if ((s = alloc_string(sts)) == NULL) {
+ free(atom->data);
+ atom->data = NULL;
+ return errmsg;
+ }
+ atom->ddata = strcpy(s, value);
+ }
+ }
+ break;
+ }
+ if (changed)
+ atom->changed = 1;
+ return NULL;
+}
+
+/* set a parameter field in a rule; returns NULL/failure message */
+char *
+value_change(rule_t *rule, char *param, char *value)
+{
+ int sts;
+ atom_t *aptr;
+
+ if ((sts = map_symbol(attribs, numattribs, param)) != -1)
+ return set_attribute(rule, &rule->self, sts, value, 1);
+ else {
+ for (aptr = rule->self.next; aptr != NULL; aptr = aptr->next) {
+ if (strcmp(get_aname(rule, aptr), param) == 0)
+ return set_attribute(rule, aptr, ATTRIB_DEFAULT, value, 1);
+ }
+ /* if found in globals, promote the global to customised local.. */
+ for (aptr = globals->self.next; aptr != NULL; aptr = aptr->next) {
+ if (strcmp(get_aname(globals, aptr), param) == 0) {
+ if ((aptr = alloc_atom(rule, *aptr, 0)) == NULL)
+ return errmsg;
+ if ((aptr->name = strdup(get_aname(globals, aptr))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to change value");
+ return errmsg;
+ }
+ return set_attribute(globals, aptr, ATTRIB_DEFAULT, value, 1);
+ }
+ }
+ }
+ snprintf(errmsg, sizeof(errmsg), "variable \"%s\" is undefined for rule %s",
+ param, rule->self.name);
+ return errmsg;
+}
+
+static char *
+append_string(char *s, char *append, int len)
+{
+ size_t size = (strlen(s) + len + 1);
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - \"%s\" + (%d)\"%s\" = (%d chars)\n",
+ s, len, append, size);
+#endif
+ if ((s = (char *)realloc(s, size)) == NULL)
+ return NULL;
+ strncat(s, append, len);
+ s[size-1] = '\0';
+ return s;
+}
+
+/* fix up value strings by doing variable expansion */
+char *
+dollar_expand(rule_t *rule, char *string, int pp)
+{
+ atom_t *aptr;
+ char *tmp, *r;
+ char *sptr;
+ char *s;
+ char *mark = NULL;
+ char localbuf[TOKEN_LENGTH];
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - dollar_expand %s in %s\n", string, rule->self.name);
+#endif
+ if ((s = (char *)malloc(sizeof(char))) == NULL)
+ return NULL;
+ *s = '\0';
+
+ for (sptr = string; *sptr != '\0'; sptr++) {
+ if (*sptr == '\\' && *(sptr+1) == '$') { /* skip escaped $ */
+ if ((s = append_string(s, sptr+1, 1)) == NULL)
+ return NULL;
+ sptr++; /* move passed the escaped char */
+ continue;
+ }
+ if (*sptr == '$') {
+ if (mark == NULL) /* start of an expansion section */
+ mark = sptr+1;
+ else { /* end of an expansion section */
+ /* look through atom list & if not there search globally */
+ strncpy(localbuf, mark, sptr - mark);
+ localbuf[sptr - mark] = '\0';
+ mark = NULL;
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - expand localbuf: %s\n", localbuf);
+#endif
+ if ((tmp = get_attribute(localbuf, &rule->self)) == NULL) {
+ for (aptr = &rule->self; tmp == NULL && aptr != NULL; aptr = aptr->next)
+ if (strcmp(get_aname(rule, aptr), localbuf) == 0)
+ tmp = value_string(aptr, pp);
+ for (aptr = globals->self.next; tmp == NULL && aptr != NULL; aptr = aptr->next)
+ if (strcmp(get_aname(globals, aptr), localbuf) == 0)
+ tmp = value_string(aptr, pp);
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - expanded localbuf? %s\n", tmp);
+#endif
+ if (tmp == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "variable \"$%s$\" in %s is undefined",
+ localbuf, rule->self.name);
+ free(s);
+ return NULL;
+ }
+ }
+ if (tmp != NULL) {
+ if ((r = dollar_expand(rule, tmp, pp)) == NULL) {
+ free(s);
+ return NULL;
+ }
+ if ((s = append_string(s, r, strlen(r))) == NULL) {
+ free(r);
+ return NULL;
+ }
+ free(r);
+ }
+ }
+ }
+ else if (mark == NULL) { /* need memory to hold this character */
+ if ((s = append_string(s, sptr, 1)) == NULL)
+ return NULL;
+ }
+ }
+ if (mark != NULL) /* no terminating '$' */
+ s = append_string(s, mark, strlen(mark));
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - expanded '%s' to '%s'\n", string, s);
+#endif
+ return s;
+}
+
+
+/*
+ * #### main parsing routines ###
+ */
+
+/* need a SIGWINCH-aware read routine for interactive pmieconf */
+static int
+mygetc(FILE *f)
+{
+ int c;
+
+ for (;;) {
+ setoserror(0);
+ c = getc(f);
+ /* did we get told to resize the window during read? */
+ if (c == -1 && oserror() == EINTR && resized() == 1)
+ continue; /* signal handled, try reading again */
+ break;
+ }
+ return c;
+}
+
+/*
+ * skip leading white space and comments, return first character in next token
+ * or zero on end of file
+ */
+static int
+prime_next_pread(FILE *f, int end)
+{
+ int c;
+
+ do {
+ c = mygetc(f);
+ if (c == '#')
+ do {
+ c = mygetc(f);
+ } while (c != '\n' && c != end && c != EOF);
+ if (c == end)
+ return 0;
+ else if (c == EOF)
+ return -2;
+ if (c == '\n' && end != '\n')
+ linenum++;
+ } while (!isgraph(c));
+ return c;
+}
+
+/*
+ * read next input token; returns 1 ok, 0 end, -1 error, -2 EOF (if end!=EOF)
+ * nb: `end' can be either EOL or EOF, depending on use of this routine
+ */
+int
+read_token(FILE *f, char *token, int token_length, int end)
+{
+ int c;
+ int n = 0;
+
+ switch (c = prime_next_pread(f, end)) {
+ case 0: /* end */
+ case -2: /* EOF */
+ return c;
+ case '"': /* scan string */
+ c = mygetc(f);
+ while (c != '"') {
+ if (c == '\\')
+ c = mygetc(f);
+ if (c == end || c == EOF || n == token_length) {
+ token[n] = '\0';
+ parse_error("end-of-string", token);
+ return -1;
+ }
+ if (c == '\n' && end != '\n')
+ linenum++;
+ token[n++] = c;
+ c = mygetc(f);
+ }
+ break;
+ case ';':
+ case '=':
+ token[n++] = c; /* single char token */
+ break;
+ default: /* some other token */
+ while (isgraph(c)) {
+ if (c == '=' || c == ';') {
+ ungetc(c, f);
+ break;
+ }
+ if (n == token_length) {
+ token[n] = '\0';
+ parse_error("end-of-token", token);
+ return -1;
+ }
+ token[n++] = c;
+ c = mygetc(f);
+ if (c == end || c == EOF)
+ ungetc(c, f);
+ }
+ if (c == '\n' && end != '\n')
+ linenum++;
+ break;
+ }
+
+ token[n] = '\0';
+ return 1;
+}
+
+/*
+ * get attribute list part of an atom; returns -1 on error, 0 on reaching
+ * the end of the attribute list, and 1 at end of each attribute.
+ */
+static int
+read_next_attribute(FILE *f, char **attr, char **value)
+{
+ int sts;
+
+ if ((sts = read_token(f, token, TOKEN_LENGTH, EOF)) <= 0) {
+ if (sts == 0)
+ parse_error("attribute or ';'", "end-of-file");
+ return -1;
+ }
+ if (token[0] == ';')
+ return 0;
+ if (map_symbol(attribs, numattribs, token) < 0) {
+ parse_error("attribute keyword", token);
+ return -1;
+ }
+ if ((*attr = alloc_string(strlen(token)+1)) == NULL)
+ return -1;
+ strcpy(*attr, token);
+ if ((sts = read_token(f, token, TOKEN_LENGTH, EOF)) <= 0
+ || token[0] != '=') {
+ if (sts == 0)
+ parse_error("=", "end-of-file");
+ else
+ parse_error("=", token);
+ free(*attr);
+ return -1;
+ }
+ if ((sts = read_token(f, token, TOKEN_LENGTH, EOF)) <= 0) {
+ if (sts == 0)
+ parse_error("attribute value", "end-of-file");
+ else
+ parse_error("attribute value", token);
+ free(*attr);
+ return -1;
+ }
+ if ((*value = alloc_string(strlen(token)+1)) == NULL) {
+ free(*attr);
+ return -1;
+ }
+ strcpy(*value, token);
+ return 1;
+}
+
+
+/* parse an atom, return NULL/failure message */
+static char *
+read_atom(FILE *f, rule_t *r, char *name, int type, int global)
+{
+ int sts;
+ int attrib;
+ char *attr;
+ char *value;
+ atom_t atom;
+
+ memset(&atom, 0, sizeof(atom_t));
+ atom.name = name;
+ atom.type = type;
+ atom.enabled = atom.display = atom.modify = 1; /* defaults */
+ for (;;) {
+ if ((sts = read_next_attribute(f, &attr, &value)) < 0)
+ return errmsg;
+ else if (sts == 0) { /* end of parameter list */
+ if (alloc_atom(r, atom, global) == NULL)
+ return errmsg;
+ break;
+ }
+ else {
+ if ((attrib = map_symbol(attribs, numattribs, attr)) < 0) {
+ parse_error("attribute keyword", attr);
+ goto fail;
+ }
+ if (set_attribute(r, &atom, attrib, value, 0) != NULL)
+ goto fail;
+ free(attr);
+ free(value);
+ }
+ }
+ return NULL;
+
+fail:
+ free(attr);
+ free(value);
+ return errmsg;
+}
+
+
+/* parse type-identifier pair, return NULL/failure message */
+static char *
+read_type(FILE *f, rule_t *r, int *type, int *global, char **name)
+{
+ int sts;
+
+ /* read type - rule, percent, double, unsigned, string... */
+ if ((sts = read_token(f, token, TOKEN_LENGTH, EOF)) < 0)
+ return errmsg;
+ else if (sts == 0)
+ return NULL;
+ if ((*type = map_symbol(types, numtypes, token)) < 0) {
+ parse_error("type keyword", token);
+ return errmsg;
+ }
+
+ /* read name identifying this rule/atom of type '*type' */
+ if ((sts = read_token(f, token, TOKEN_LENGTH, EOF)) <= 0)
+ return errmsg;
+ if ((*name = alloc_string(strlen(token)+1)) == NULL)
+ return errmsg;
+ strcpy(*name, token);
+ *global = (strncmp(*name, "global.", GLOBAL_LEN) == 0)? 1 : 0;
+
+ /* do some simple validity checks */
+ if (IS_RULE(*type) && strncmp(*name, global_name, GLOBAL_LEN-1) == 0) {
+ snprintf(errmsg, sizeof(errmsg), "rule name may not be \"%s\" - this is reserved",
+ global_name);
+ free(*name);
+ return errmsg;
+ }
+ if (r == NULL) { /* any rule defined yet? - simple validity checks */
+ if (*global && IS_RULE(*type)) {
+ snprintf(errmsg, sizeof(errmsg), "rules not allowed in global group: \"%s\"", *name);
+ free(*name);
+ return errmsg;
+ }
+ else if (!*global && !IS_RULE(*type)) { /* not global, and no rule */
+ snprintf(errmsg, sizeof(errmsg), "no rule defined, cannot make sense of %s \"%s\""
+ " without one\n line number: %u (\"%s\")\n",
+ types[*type].symbol, *name, linenum, filename);
+ free(*name);
+ return errmsg;
+ }
+ }
+ return NULL;
+}
+
+/* set an attribute field in an atom; returns NULL/failure message */
+static char *
+set_rule_attribute(rule_t *rule, int attrib, char *value)
+{
+ char *s;
+
+ if (attrib == ATTRIB_PREDICATE) {
+ if ((s = alloc_string(strlen(value)+1)) == NULL)
+ return errmsg;
+ rule->predicate = strcpy(s, value);
+ return NULL;
+ }
+ if (attrib == ATTRIB_ENUMERATE) {
+ if ((s = alloc_string(strlen(value)+1)) == NULL)
+ return errmsg;
+ rule->enumerate = strcpy(s, value);
+ return NULL;
+ }
+ else if (attrib == ATTRIB_VERSION) {
+ rule->version = strtoul(value, &s, 10);
+ if (*s != '\0') {
+ parse_error("version number", "be a positive integer number");
+ return errmsg;
+ }
+ return NULL;
+ }
+ /* else */
+ return set_attribute(rule, &rule->self, attrib, value, 0);
+}
+
+
+/* parse a single "rule" expression, return NULL/failure message */
+static char *
+read_rule(FILE *f, rule_t **r, char *name)
+{
+ int sts;
+ int attrib;
+ char *attr;
+ char *value;
+ rule_t rule;
+
+ memset(&rule, 0, sizeof(rule_t));
+ rule.self.name = name;
+ rule.self.type = TYPE_RULE;
+ rule.version = rule.self.enabled = rule.self.display = 1; /* defaults */
+ for (;;) {
+ if ((sts = read_next_attribute(f, &attr, &value)) < 0)
+ return errmsg;
+ else if (sts == 0) { /* end of attribute list */
+ if ((*r = alloc_rule(rule)) == NULL)
+ return errmsg;
+ break;
+ }
+ else {
+ if ((attrib = map_symbol(attribs, numattribs, attr)) < 0) {
+ parse_error("rule attribute keyword", attr);
+ goto fail;
+ }
+ if (set_rule_attribute(&rule, attrib, value) != NULL)
+ goto fail;
+ free(attr);
+ free(value);
+ }
+ }
+ return NULL;
+
+fail:
+ free(attr);
+ free(value);
+ return errmsg;
+}
+
+
+/* parse rule description file; returns NULL/failure message */
+static char *
+read_all_rules(FILE *f)
+{
+ rule_t *rule = NULL; /* current rule */
+ char *name = NULL;
+ int type = 0;
+ int global = 0;
+
+ /* rule files have quite a simple grammar, along these lines:
+ TYPE identifier [ ATTRIB '=' value ]* ';'
+ */
+ for (;;) {
+ if (read_type(f, rule, &type, &global, &name) != NULL)
+ return errmsg;
+ if (feof(f)) /* end of file reached without error */
+ break;
+ if (type == TYPE_RULE) {
+ if (read_rule(f, &rule, name) != NULL)
+ return errmsg;
+ }
+ else {
+ if (read_atom(f, rule, name, type, global) != NULL)
+ return errmsg;
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ * validate header of rule description file, return NULL/failure message
+ */
+static char *
+read_pheader(FILE *f)
+{
+ int c;
+
+ c = getc(f);
+ if (c != '#' || read_token(f, token, TOKEN_LENGTH, EOF) != 1 ||
+ strcmp(token, RULES_FILE) != 0 ||
+ read_token(f, token, TOKEN_LENGTH, EOF) != 1) {
+ snprintf(errmsg, sizeof(errmsg), "%s is not a rule description file (bad header)\n"
+ "found \"%s\", expected \"%s\"", filename,
+ token, RULES_FILE);
+ return errmsg;
+ }
+ else if (strcmp(token, RULES_VERSION) != 0) { /* one version only */
+ snprintf(errmsg, sizeof(errmsg), "unknown version number in %s: \"%s\" (expected %s)",
+ filename, token, RULES_VERSION);
+ return errmsg;
+ }
+ return NULL;
+}
+
+
+/*
+ * builds up rule data structures for all rule files in given directory
+ * and all its subdirectories, returns NULL/failure message
+ */
+char *
+read_rule_subdir(char *subdir)
+{
+ struct stat sbuf;
+ struct dirent *dp;
+ FILE *fp;
+ DIR *dirp;
+ char fullpath[MAXPATHLEN+1];
+
+ if (stat(subdir, &sbuf) < 0) {
+ snprintf(errmsg, sizeof(errmsg), "cannot stat %s: %s",
+ subdir, osstrerror());
+ return errmsg;
+ }
+ if (!S_ISDIR(sbuf.st_mode)) {
+ if ((fp = fopen(subdir, "r")) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "cannot open %s: %s",
+ subdir, osstrerror());
+ return errmsg;
+ }
+ linenum = 1;
+ filename = subdir;
+ if (read_pheader(fp) == NULL) {
+ if (read_all_rules(fp) != NULL) {
+ fclose(fp);
+ return errmsg;
+ }
+ }
+#ifdef PMIECONF_DEBUG
+ else {
+ fprintf(stderr, "debug - %s isn't a pmie rule file: %s\n",
+ filename, errmsg);
+ }
+#endif
+ fclose(fp);
+ }
+ else {
+ /* iterate through the rules directory and for each subdirectory */
+ /* fetch all the rules along with associated parameters & values */
+
+ if ((dirp = opendir(subdir)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "cannot opendir %s: %s", subdir, osstrerror());
+ return errmsg;
+ }
+ while ((dp = readdir(dirp)) != NULL) { /* groups */
+ if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+ continue;
+ snprintf(fullpath, sizeof(fullpath), "%s%c%s", subdir, SEP, dp->d_name);
+ if (read_rule_subdir(fullpath) != NULL) { /* recurse */
+ closedir(dirp);
+ return errmsg;
+ }
+ }
+ closedir(dirp);
+ }
+ return NULL;
+}
+
+
+/* ##### pmiefile parsing routines #### */
+
+
+/* returns NULL on successfully adding rule to list, else failure message */
+char *
+deprecate_rule(char *name, unsigned int version, int type)
+{
+ int index;
+
+ /* first check to see if this rule is deprecated already */
+ for (index = 0; index < dcount; index++) {
+ if (strcmp(dlist[index].name, name) == 0
+ && dlist[index].version == version)
+ return NULL;
+ }
+
+ /* get the memory we need & then keep a copy of deprecated rule info */
+ if ((dlist = (dep_t *)realloc(dlist, (dcount+1)*sizeof(dep_t))) == NULL
+ || (dlist[dcount].name = strdup(name)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to deprecate rule %s", name);
+ return errmsg;
+ }
+ dlist[dcount].type = type;
+ dlist[dcount].version = version;
+ if (type == DEPRECATE_NORULE)
+ dlist[dcount].reason = drulestring;
+ else
+ dlist[dcount].reason = dverstring;
+ dcount++;
+ return NULL;
+}
+
+/*
+ * makes list of deprecated rules available to caller (for warning message)
+ * nb: called following a pmiefile write to see what was deprecated during
+ * that write - subsequent writing of this pmiefile should not deprecate
+ * anything (deprecations done once & not again). caller must free list.
+ */
+int
+fetch_deprecated(dep_t **list)
+{
+ int sts;
+
+ *list = dlist;
+ sts = dcount;
+ dcount = 0;
+ return sts;
+}
+
+/* merges local customisations back into the rules atom list */
+static char *
+merge_local(unsigned int version, char *name, char *attrib, char *value)
+{
+ atom_t *aptr;
+ rule_t *rule;
+ int a;
+
+ /*
+ first find the rule to which this local belongs, then figure
+ out what sort of attribute this really is, and finally merge
+ the customisation back into the values in the rules attribs.
+ */
+
+ if (find_rule(name, &rule) != NULL) /* in pmiefile but not rules */
+ return NULL; /* this will be deprecated later */
+ else if (rule->version != version) /* no rule for this version */
+ return NULL; /* this will be deprecated later */
+
+ if ((a = is_attribute(attrib)) != -1)
+ return set_attribute(rule, &rule->self, a, value, 1);
+ else { /* search through this rules list of atoms */
+ for (aptr = rule->self.next; aptr != NULL; aptr = aptr->next) {
+ if (strcmp(get_aname(rule, aptr), attrib) == 0)
+ return set_attribute(rule, aptr, ATTRIB_DEFAULT, value, 1);
+ }
+ for (aptr = globals->self.next; aptr != NULL; aptr = aptr->next) {
+ if (strcmp(get_aname(globals, aptr), attrib) == 0) {
+ /* promote global to become a local */
+ if ((aptr = alloc_atom(rule, *aptr, 0)) == NULL)
+ return errmsg;
+ if ((aptr->name = strdup(get_aname(globals, aptr))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to change value");
+ return errmsg;
+ }
+ return set_attribute(globals, aptr, ATTRIB_DEFAULT, value, 1);
+ }
+ }
+ }
+ snprintf(errmsg, sizeof(errmsg), "variable \"%s\" is undefined for rule %s",
+ attrib, name);
+ return errmsg;
+}
+
+
+/*
+ * #### produce pmie configuration file from rules ###
+ */
+
+char *
+action_string(int type)
+{
+ switch (type) {
+ case TYPE_PRINT: return "print";
+ case TYPE_SHELL: return "shell";
+ case TYPE_ALARM: return "alarm";
+ case TYPE_SYSLOG: return "syslog";
+ }
+ return NULL;
+}
+
+static int
+expand_action(FILE *f, int count, rule_t *rule, atom_t *atom)
+{
+ char *p;
+ char *str;
+
+ if (IS_ACTION(atom->type) && atom->enabled) {
+ if ((str = dollar_expand(rule, " $holdoff$ ", 0)) == NULL)
+ return count;
+ if (count == 0)
+ fprintf(f, " -> ");
+ else
+ fprintf(f, "\n & ");
+ fprintf(f, "%s", action_string(atom->type));
+ fprintf(f, "%s", str);
+ free(str);
+ if ((str = dollar_expand(rule, atom->data, 0)) == NULL) {
+ fprintf(stderr, "Warning - failed to expand action for rule %s\n"
+ " string: \"%s\"\n", rule->self.name, atom->data);
+ /* keep going - too late to bail out without file corruption */
+ }
+#ifdef PMIECONF_DEBUG
+ else {
+ fprintf(stderr, "expanded action= \"%s\"\n", str);
+ }
+#endif
+ fputc('"', f);
+ for (p = str; p != NULL && *p; p++) { /* expand the '^' character */
+ if (*p == '/' && *(p+1) == '^') {
+ fputc(*p, f); p++;
+ fputc(*p, f); p++;
+ }
+ else if (*p == '^')
+ fputs("\" \"", f);
+ else fputc(*p, f);
+ }
+ fputc('"', f);
+ if (str != NULL)
+ free(str);
+ return count + 1;
+ }
+ return count;
+}
+
+
+/*
+ * this struct and the enumerate function are used only in generate_rules()
+ * and the enumerate() routines, for enumeration of either a hostlist or an
+ * instlist
+ */
+
+typedef struct {
+ atom_t *atom;
+ int nvalues;
+ char **valuelist;
+ char *restore;
+} enumlist_t;
+
+static enumlist_t *list;
+static int nlistitems;
+static int writecount;
+
+/*
+ * expands and writes out a single rule, and optionally the delta
+ * note: in the single rule case (not enumerated), we absolutely
+ * must write out the delta every time
+ */
+static char *
+write_rule(FILE *f, rule_t *rule)
+{
+ atom_t *aptr;
+ char *dgen = NULL; /* holds generated "delta" */
+ char *pgen; /* holds generated "predicate" */
+ int actions = 0;
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - writing rule %s\n", rule->self.name);
+#endif
+
+ if (writecount == 0 && (dgen = dollar_expand(rule, "$delta$", 0)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "\"$delta$\" variable expansion failed for rule %s",
+ rule->self.name);
+ return errmsg;
+ }
+ if ((pgen = dollar_expand(rule, rule->predicate, 0)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "\"$predicate$\" variable expansion failed "
+ "for rule %s", rule->self.name);
+ return errmsg;
+ }
+ if (writecount == 0) {
+ fprintf(f, "// %u %s\ndelta = %s;\n%s = \n", rule->version,
+ rule->self.name, dgen, rule->self.name);
+ free(dgen);
+ }
+ else /* we're enumerating, need to differentiate rule names */
+ fprintf(f, "%s%u = \n", rule->self.name, writecount);
+ fputs(pgen, f);
+ free(pgen);
+ for (aptr = rule->self.next; aptr != NULL; aptr = aptr->next)
+ actions = expand_action(f, actions, rule, aptr);
+ for (aptr = globals->self.next; aptr != NULL; aptr = aptr->next)
+ actions = expand_action(f, actions, rule, aptr);
+ fprintf(f, ";\n\n");
+
+ writecount++;
+ return NULL;
+}
+
+/* parses the "enumerate" value string passed in thru the rules file */
+char *
+parse_enumerate(rule_t *rule)
+{
+ atom_t *ap;
+ char *p = rule->enumerate;
+ int needsave = 0; /* should we save this variable name yet? */
+ int i = 0;
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - parse_enumerate called for %s\n", rule->self.name);
+#endif
+
+ nlistitems = 0;
+ list = NULL;
+ while (*p != '\0') {
+ if (!isspace((int)*p)) {
+ needsave = 1;
+ token[i++] = *p;
+ }
+ p++;
+ if ((isspace((int)*p) && needsave) || *p == '\0') {
+ token[i] = '\0';
+ i = 0;
+ if (map_symbol(attribs, numattribs, token) != -1) {
+ snprintf(errmsg, sizeof(errmsg), "cannot enumerate rule %s using attribute"
+ " \"%s\"", rule->self.name, token);
+ return errmsg;
+ }
+ else {
+ for (ap = rule->self.next; ap != NULL; ap = ap->next)
+ if (strcmp(get_aname(rule, ap), token) == 0)
+ goto foundname;
+ for (ap = globals->self.next; ap != NULL; ap = ap->next)
+ if (strcmp(get_aname(globals, ap), token) == 0)
+ goto foundname;
+ snprintf(errmsg, sizeof(errmsg), "variable \"%s\" undefined for enumerated"
+ " rule %s", token, rule->self.name);
+ return errmsg;
+ }
+foundname:
+ if (ap->type != TYPE_HOSTLIST && ap->type != TYPE_INSTLIST) {
+ snprintf(errmsg, sizeof(errmsg), "rules file error - \"$%s$\" in \"enumerate\" "
+ "clause of rule %s is not of type hostlist or instlist",
+ token, rule->self.name);
+ return errmsg;
+ }
+ /* increase size of list & keep a copy of the variable name */
+ if ((list = realloc(list, (nlistitems+1)*sizeof(enumlist_t))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to write rules");
+ return errmsg;
+ }
+ list[nlistitems].atom = ap;
+ list[nlistitems].nvalues = 0;
+ list[nlistitems].valuelist = NULL;
+ list[nlistitems].restore = NULL;
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - variable %s added to enum list (#%d)\n",
+ list[nlistitems].atom->name, nlistitems);
+#endif
+ nlistitems++;
+ needsave = 0;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * converts a host/inst list into individual elements, overwrites liststr
+ * (turns all spaces to NULLs to mark string ends - reduces mallocing)
+ */
+char **
+get_listitems(char *liststr, int *count)
+{
+ char **result = NULL;
+ char *p = liststr;
+ int keepwhite = 0;
+ int startagain = 0; /* set to signify new list item has started */
+ int ptrcount = 0;
+
+ if ((result = realloc(result, (ptrcount+1) * sizeof(char *))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to get list elements");
+ return NULL;
+ }
+ result[ptrcount++] = p;
+ while (*p != '\0') {
+ if (!isspace((int)*p) || keepwhite) {
+ if (startagain) {
+ result = realloc(result, (ptrcount+1) * sizeof(char *));
+ if (result == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to get list elements");
+ return NULL;
+ }
+ result[ptrcount++] = p;
+ startagain = 0;
+ }
+ if (*p == '\\')
+ p++;
+ else if (*p == '\'')
+ keepwhite = !keepwhite;
+ }
+ else {
+ *p = '\0';
+ startagain = 1;
+ }
+ p++;
+ }
+#ifdef PMIECONF_DEBUG
+ fputs("debug - instances are:", stderr);
+ for (keepwhite = 0; keepwhite < ptrcount; keepwhite++)
+ fprintf(stderr, " %s", result[keepwhite]);
+ fputs("\n", stderr);
+#endif
+ *count = ptrcount;
+ return result;
+}
+
+/* expands variables from the "enumerate" string in the rules file */
+char *
+expand_enumerate(rule_t *rule)
+{
+ int i, j;
+ char *p;
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - expanding enum variables for rule %s\n",
+ rule->self.name);
+#endif
+
+ for (i = 0; i < nlistitems; i++) {
+ if ((p = dollar_expand(rule, list[i].atom->data, 0)) == NULL)
+ return errmsg;
+ if ((list[i].valuelist = realloc(list[i].valuelist,
+ sizeof(char *) * (list[i].nvalues + 1))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for rule enumeration");
+ free(p);
+ return errmsg;
+ }
+ if ((list[i].valuelist = get_listitems(p, &j)) == NULL) {
+ free(p);
+ return errmsg;
+ }
+ list[i].nvalues = j;
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - %s value list:", list[i].atom->name);
+ for (j = 0; j < list[i].nvalues; j++)
+ fprintf(stderr, " %s", list[i].valuelist[j]);
+ fprintf(stderr, "\n");
+#endif
+ }
+ return NULL;
+}
+
+void
+enumerate(FILE *f, rule_t *r, enumlist_t *listoffset, int wssize, char **wset)
+{
+ int i;
+
+ if (wssize < nlistitems) {
+ for (i = 0; i < listoffset->nvalues; i++) {
+ /* add current word to word set, and move down a level */
+ wset[wssize] = listoffset->valuelist[i];
+ enumerate(f, r, &list[wssize+1], wssize+1, wset);
+ }
+ }
+ else { /* have a full set, generate rule */
+#ifdef PMIECONF_DEBUG
+ for (i = 0; i < wssize; i++)
+ printf("%s=%s ", list[i].atom->name, wset[i]);
+ printf("\n");
+#endif
+ for (i = 0; i < wssize; i++) {
+ list[i].restore = list[i].atom->data;
+ list[i].atom->data = wset[i];
+ }
+
+ write_rule(f, r);
+
+ for (i = 0; i < wssize; i++)
+ list[i].atom->data = list[i].restore;
+ }
+}
+
+/* generate pmie rules for rule, returns rule string/NULL */
+static char *
+generate_rules(FILE *f, rule_t *rule)
+{
+ int i;
+
+ if (rule->self.enabled == 0)
+ return NULL;
+ if (rule->enumerate == NULL) {
+ writecount = 0;
+ write_rule(f, rule);
+ }
+ else {
+ char **workingset; /* holds current variable values set */
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - generating enumerated rule %s\n",
+ rule->self.name);
+#endif
+
+ /* "enumerate" attrib is a space-separated list of variables */
+ /* 1.create a list of variable info structs (name->valuelist) */
+ /* 2.recurse thru lists, when each set built, write out rule */
+
+ if ((parse_enumerate(rule)) != NULL)
+ return errmsg;
+ if ((expand_enumerate(rule)) != NULL)
+ return errmsg;
+ if ((workingset = malloc(nlistitems * sizeof(char*))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to generate rules");
+ return errmsg;
+ }
+ writecount = 0;
+ enumerate(f, rule, list, 0, workingset);
+ free(workingset);
+ for (i = 0; i < nlistitems; i++)
+ free(list[i].valuelist); /* alloc'd by dollar_expand */
+ free(list);
+ }
+
+ return NULL;
+}
+
+/* generate local configuration changes, returns rule string/NULL */
+static char *
+generate_pmiefile(FILE *f, rule_t *rule)
+{
+ atom_t *aptr;
+
+ for (aptr = &rule->self; aptr != NULL; aptr = aptr->next) {
+ if (aptr->changed == 0)
+ continue;
+ if (IS_RULE(aptr->type)) {
+ fprintf(f, "// %u %s %s = %s\n", rule->version,
+ get_aname(rule, aptr), "enabled",
+ rule->self.enabled? "yes" : "no");
+ }
+ else {
+ fprintf(f, "// %u %s %s = %s\n", rule->version, rule->self.name,
+ get_aname(rule, aptr), value_string(aptr, 1));
+ }
+ }
+ return NULL;
+}
+
+/* generate pmie rules and write to file, returns NULL/failure message */
+char *
+write_pmiefile(char *program, int autocreate)
+{
+ time_t now = time(NULL);
+ char *p, *msg = NULL;
+ char buf[MAXPATHLEN+10];
+ char *fname = get_pmiefile();
+ FILE *fp;
+ int i;
+
+ /* create full path to file if it doesn't exist */
+ if ((p = strrchr(fname, '/')) != NULL) {
+ struct stat sbuf;
+
+ *p = '\0'; /* p is the dirname of fname */
+ if (stat(fname, &sbuf) < 0) {
+ snprintf(buf, sizeof(buf), "/bin/mkdir -p %s", fname);
+ if (system(buf) < 0) {
+ snprintf(errmsg, sizeof(errmsg), "failed to create directory \"%s\"", p);
+ return errmsg;
+ }
+ }
+ else if (!S_ISDIR(sbuf.st_mode)) {
+ snprintf(errmsg, sizeof(errmsg), "\"%s\" exists and is not a directory", p);
+ return errmsg;
+ }
+ fname[strlen(fname)] = '/'; /* stitch together */
+ }
+
+ if ((fp = fopen(fname, "w")) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "cannot write file %s: %s", fname, osstrerror());
+ return errmsg;
+ }
+ else if (!gotpath) {
+ strcpy(token, fname);
+ if (realpath(token, pmiefile) == NULL) {
+ fclose(fp);
+ snprintf(errmsg, sizeof(errmsg), "failed to resolve %s realpath: %s", token, osstrerror());
+ return errmsg;
+ }
+ gotpath = 1;
+ }
+
+ fprintf(fp, "// %s %s %s\n", PMIE_FILE, PMIE_VERSION, get_rules());
+ for (i = 0; i < rulecount; ++i)
+ if ((msg = generate_pmiefile(fp, &rulelist[i])) != NULL)
+ goto imouttahere;
+ fputs("// end\n//\n", fp);
+
+ fprintf(fp, "%s// %sgenerated by %s on: %s//\n\n",
+ START_STRING, autocreate ? "Auto-" : " ", program, ctime(&now));
+ for (i = 1; i < rulecount; ++i) /* 1: start _after_ globals */
+ if ((msg = generate_rules(fp, &rulelist[i])) != NULL)
+ goto imouttahere;
+
+ /* write user-modifications area */
+ fprintf(fp, END_STRING);
+ /* finally any other local changes */
+ if (save_area != NULL)
+ fputs(save_area, fp);
+
+imouttahere:
+ fclose(fp);
+ return msg;
+}
+
+
+/*
+ * #### pmiefile manipulation routines ###
+ */
+
+/*
+ * skip leading white space and comments, return first character in next token
+ * or zero on end of file
+ */
+static int
+prime_next_lread(FILE *f)
+{
+ int c;
+
+ do {
+ c = getc(f);
+ if (c == EOF)
+ return 0;
+ if (c == '\n') {
+ linenum++;
+ if (getc(f) != '/') return 0;
+ if (getc(f) != '/') return 0;
+ }
+ } while (! isgraph(c));
+ return c;
+}
+
+/* read next input token; returns 1 ok, 0 eof, -1 error */
+static int
+read_ltoken(FILE *f)
+{
+ int c;
+ int n = 0;
+
+ switch (c = prime_next_lread(f)) {
+ case 0: /* EOF */
+ return 0;
+ case '"': /* scan string */
+ c = getc(f);
+ while (c != '"') {
+ if (c == '\\')
+ c = getc(f);
+ if (c == EOF || n == TOKEN_LENGTH) {
+ token[n] = '\0';
+ parse_error("end-of-string", token);
+ return -1;
+ }
+ if (c == '\n') {
+ token[n] = '\0';
+ parse_error("end-of-string", "end-of-line");
+ return -1;
+ }
+ token[n++] = c;
+ c = getc(f);
+ }
+ break;
+ case '=':
+ token[n++] = c; /* single char token */
+ break;
+ default: /* some other token */
+ while (isgraph(c)) {
+ if (c == '=') {
+ ungetc(c, f);
+ break;
+ }
+ if (n == TOKEN_LENGTH) {
+ token[n] = '\0';
+ parse_error("end-of-token", token);
+ return -1;
+ }
+ token[n++] = c;
+ c = getc(f);
+ }
+ if (c == '\n') {
+ linenum++;
+ if (strncmp(token, "end", 3) == 0) break;
+ if (getc(f) != '/') return 0;
+ if (getc(f) != '/') return 0;
+ }
+ break;
+ }
+
+ token[n] = '\0';
+ return 1;
+}
+
+
+/* allocates memory & appends a string to the save area */
+char *
+save_area_append(char *str)
+{
+ int size = strlen(str);
+
+ while ( (size+1) >= (sa_size-sa_mark) ) {
+ sa_size += 256; /* increase area by 256 bytes at a time */
+ if ((save_area = (char *)realloc(save_area, sa_size)) == NULL)
+ return NULL;
+ }
+ if (sa_mark == 1)
+ save_area = strcpy(save_area, str);
+ else
+ save_area = strcat(save_area, str);
+ sa_mark += size;
+ return save_area;
+}
+
+/* read and save text which is to be restored on pmiefile write */
+static char *
+read_restore(FILE *f)
+{
+ unsigned int version;
+ rule_t *rule;
+ char buf[LINE_LENGTH];
+ int saverule = 0;
+ int saveall = 0;
+
+ do {
+ if (fgets(buf, LINE_LENGTH, f) == NULL)
+ break;
+ if (!saveall) { /* not yet at start of explicit "save" position */
+ if (strcmp(buf, END_STRING) == 0)
+ saveall = 1;
+ else if (sscanf(buf, "// %u %s\n", &version, token) == 2) {
+ /*
+ * where the rule has disappeared or its version does not match
+ * the one in the pmiefile, add the rule name & version to list
+ * of rules to be deprecated (i.e. moved to the "save area")
+ */
+ /* check that we still have this rule definition */
+ if (find_rule(token, &rule) != NULL) { /* not found! */
+ snprintf(buf, sizeof(buf), "// %u %s (deprecated, %s)\n",
+ version, token, drulestring);
+ deprecate_rule(token, version, DEPRECATE_NORULE);
+ saverule = 1;
+ }
+ else if (rule->version != version) { /* not supported! */
+ snprintf(buf, sizeof(buf), "// %u %s (deprecated, %s)\n",
+ version, token, dverstring);
+ deprecate_rule(token, version, DEPRECATE_VERSION);
+ saverule = 1;
+ }
+ else
+ saverule = 0;
+ }
+ if (!saveall && saverule) {
+ if (save_area_append("// ") == NULL ||
+ save_area_append(buf) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to deprecate a rule");
+ return errmsg;
+ }
+ }
+ }
+ else if (save_area_append(buf) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to preserve save area");
+ return errmsg;
+ }
+ } while (!feof(f));
+ return NULL;
+}
+
+
+/* read custom values, return NULL/failure message */
+static char *
+read_locals(FILE *f)
+{
+ int sts;
+ char *rule;
+ char *attrib;
+ char *value;
+ unsigned int version;
+
+ /* general pmiefile format: version rulename attribute = value */
+
+ for (;;) {
+ if ((sts = read_ltoken(f)) < 0)
+ return errmsg;
+ else if (sts == 0) {
+ parse_error("rule identifier or \"end\" symbol", "end-of-file");
+ return errmsg;
+ }
+ if (strcmp("end", token) == 0)
+ break;
+
+ /* read the version number for this rule */
+ version = strtoul(token, &value, 10);
+ if (*value != '\0') {
+ parse_error("version number", token);
+ return errmsg;
+ }
+
+ /* read the name of the rule */
+ if ((sts = read_ltoken(f)) != 1 ||
+ (rule = alloc_string(strlen(token)+1)) == NULL) {
+ if (sts == 0)
+ parse_error("rule name", token);
+ return errmsg;
+ }
+ strcpy(rule, token);
+
+ /* read the rule attribute component */
+ if ((sts = read_ltoken(f)) != 1 ||
+ (attrib = alloc_string(strlen(token)+1)) == NULL) {
+ free(rule);
+ if (sts == 0)
+ parse_error("rule attribute", token);
+ return errmsg;
+ }
+ strcpy(attrib, token);
+
+ if ((sts = read_ltoken(f)) != 1 || strcmp("=", token) != 0) {
+ free(rule); free(attrib);
+ if (sts == 0)
+ parse_error("'=' symbol", "end-of-file");
+ return errmsg;
+ }
+
+ /* read the modified value of this attribute */
+ if ((sts = read_ltoken(f)) != 1 ||
+ (value = alloc_string(strlen(token)+1)) == NULL) {
+ free(rule); free(attrib);
+ if (sts == 0)
+ parse_error("rule attribute value", "end-of-file");
+ return errmsg;
+ }
+ strcpy(value, token);
+
+ if (merge_local(version, rule, attrib, value) != NULL) {
+ free(rule); free(attrib); free(value);
+ return errmsg;
+ }
+ free(rule); free(attrib); free(value); /* no longer need these */
+ }
+ return NULL;
+}
+
+/* validate header of rule customizations file, return NULL/failure message */
+static char *
+read_lheader(FILE *f, char **proot)
+{
+ if (read_ltoken(f) != 1 || strcmp(token, "//") || read_ltoken(f) != 1
+ || strcmp(token, PMIE_FILE) || read_ltoken(f) != 1) {
+ snprintf(errmsg, sizeof(errmsg), "%s is not a rule customization file (bad header)",
+ filename);
+ return errmsg;
+ }
+ else if (strcmp(token, PMIE_VERSION) != 0) { /* one version only */
+ snprintf(errmsg, sizeof(errmsg), "unknown version number in %s: \"%s\" (expected %s)",
+ filename, token, PMIE_VERSION);
+ return errmsg;
+ }
+ else if (read_ltoken(f) != 1) {
+ snprintf(errmsg, sizeof(errmsg), "no rules path specified in %s after version number",
+ filename);
+ return errmsg;
+ }
+ *proot = token;
+ return NULL;
+}
+
+
+/*
+ * read the pmiefile format into global data structures
+ */
+char *
+read_pmiefile(char *warning, size_t warnlen)
+{
+ char *tmp = NULL;
+ char *p, *home;
+ FILE *f;
+ char *rule_path_sep;
+
+ if ((f = fopen(get_pmiefile(), "r")) == NULL) {
+ if (oserror() == ENOENT)
+ return NULL;
+ snprintf(errmsg, sizeof(errmsg), "cannot open %s: %s",
+ get_pmiefile(), osstrerror());
+ return errmsg;
+ }
+
+ linenum = 1;
+ filename = get_pmiefile();
+ if (read_lheader(f, &tmp) != NULL) {
+ fclose(f);
+ return errmsg;
+ }
+
+ /* check that we have access to all components of the path */
+ if ((home = strdup(tmp)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for pmie file parsing");
+ return errmsg;
+ }
+#ifdef IS_MINGW
+ rule_path_sep = ";";
+#else
+ rule_path_sep = ":";
+#endif
+ p = strtok(home, rule_path_sep);
+ while (p != NULL) {
+ if (access(p, F_OK) < 0) {
+ free(home);
+ snprintf(errmsg, sizeof(errmsg), "cannot access rules path component: \"%s\"", p);
+ return errmsg;
+ }
+ p = strtok(NULL, rule_path_sep);
+ }
+ free(home);
+
+ if (strcmp(get_rules(), tmp) != 0)
+ snprintf(warning, warnlen, "warning - pmie configuration file \"%s\"\n"
+ " may not have been built using rules path:\n\t\"%s\"\n"
+ " (originally built using \"%s\")", filename, get_rules(), tmp);
+
+ tmp = NULL;
+ if (read_locals(f) != NULL || read_restore(f) != NULL)
+ tmp = errmsg;
+ fclose(f);
+ return tmp;
+}
+
+
+/* #### setup global data structures; return NULL/failure message #### */
+char *
+initialise(char *in_rules, char *in_pmie, char *warning, size_t warnlen)
+{
+ char *p;
+ char *home;
+ rule_t global;
+ char *rule_path_sep;
+
+ /* setup pointers to the configuration files */
+#ifdef IS_MINGW
+ if ((home = getenv("USERPROFILE")) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "USERPROFILE undefined in environment");
+ return errmsg;
+ }
+ if (in_pmie == NULL)
+ snprintf(pmiefile, sizeof(pmiefile), "%s\\%s", home, DEFAULT_USER_PMIE);
+ else
+ strcpy(pmiefile, in_pmie);
+ rule_path_sep = ";";
+#else
+ if (getuid() == 0) {
+ if (in_pmie == NULL)
+ snprintf(pmiefile, sizeof(pmiefile), "%s%c%s", pmGetConfig("PCP_SYSCONF_DIR"), SEP, DEFAULT_ROOT_PMIE);
+ else if (realpath(in_pmie, pmiefile) == NULL && oserror() != ENOENT) {
+ snprintf(errmsg, sizeof(errmsg), "failed to resolve realpath for %s: %s",
+ in_pmie, osstrerror());
+ return errmsg;
+ }
+ else if (oserror() != ENOENT)
+ gotpath = 1;
+ }
+ else {
+ if ((home = getenv("HOME")) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "$HOME undefined in environment");
+ return errmsg;
+ }
+ if (in_pmie == NULL)
+ snprintf(pmiefile, sizeof(pmiefile), "%s%c%s", home, SEP, DEFAULT_USER_PMIE);
+ else
+ strcpy(pmiefile, in_pmie);
+ }
+ rule_path_sep = ":";
+#endif
+
+ if (in_rules == NULL) {
+ if ((p = getenv("PMIECONF_PATH")) == NULL)
+ snprintf(rulepath, sizeof(rulepath), "%s%c%s", pmGetConfig("PCP_VAR_DIR"), SEP, DEFAULT_RULES);
+ else
+ strcpy(rulepath, p);
+ }
+ else
+ snprintf(rulepath, sizeof(rulepath), "%s", in_rules);
+
+ memset(&global, 0, sizeof(rule_t));
+ global.self.name = global_name;
+ global.self.data = global_data;
+ global.self.help = global_help;
+ global.self.global = 1;
+ if (alloc_rule(global) == NULL) { /* 1st rule holds global (fake rule) */
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for global parameters");
+ return errmsg;
+ }
+
+ if ((home = strdup(rulepath)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for rules path parsing");
+ return errmsg;
+ }
+ p = strtok(home, rule_path_sep);
+ while (p != NULL) {
+ if (read_rule_subdir(p) != NULL) {
+ free(home);
+ return errmsg;
+ }
+ p = strtok(NULL, rule_path_sep);
+ }
+ free(home);
+
+ if (read_pmiefile(warning, warnlen) != NULL)
+ return errmsg;
+ linenum = 0; /* finished all parsing */
+ return NULL;
+}
+
+
+/* iterate through the pmie status directory and find running pmies */
+char *
+lookup_processes(int *count, char ***processes)
+{
+ int fd;
+ int running = 0;
+ DIR *dirp;
+ void *ptr;
+ char proc[MAXPATHLEN+1];
+ char **proc_list = NULL;
+ size_t size;
+ pmiestats_t *stats;
+ struct dirent *dp;
+ struct stat statbuf;
+ int sep = __pmPathSeparator();
+
+ snprintf(proc, sizeof(proc), "%s%c%s",
+ pmGetConfig("PCP_TMP_DIR"), sep, PMIE_SUBDIR);
+ if ((dirp = opendir(proc)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "cannot opendir %s: %s",
+ proc, osstrerror());
+ return NULL;
+ }
+ while ((dp = readdir(dirp)) != NULL) {
+ /* bunch of checks to find valid pmie data files... */
+ if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+ continue;
+ snprintf(proc, sizeof(proc), "%s%c%s",
+ PROC_DIR, sep, dp->d_name); /* check /proc */
+ if (access(proc, F_OK) < 0)
+ continue; /* process has exited */
+ snprintf(proc, sizeof(proc), "%s%c%s%c%s",
+ pmGetConfig("PCP_TMP_DIR"), sep, PMIE_SUBDIR, sep, dp->d_name);
+ if (stat(proc, &statbuf) < 0)
+ continue;
+ if (statbuf.st_size != sizeof(pmiestats_t))
+ continue;
+ if ((fd = open(proc, O_RDONLY)) < 0)
+ continue;
+ ptr = __pmMemoryMap(fd, statbuf.st_size, 0);
+ close(fd);
+ if (ptr == NULL)
+ continue;
+ stats = (pmiestats_t *)ptr;
+ if (strcmp(stats->config, get_pmiefile()) != 0)
+ continue;
+
+ size = (1 + running) * sizeof(char *);
+ if ((proc_list = (char **)realloc(proc_list, size)) == NULL
+ || (proc_list[running] = strdup(dp->d_name)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for process search");
+ if (proc_list) free(proc_list);
+ closedir(dirp);
+ close(fd);
+ return errmsg;
+ }
+ running++;
+ }
+ closedir(dirp);
+ *count = running;
+ *processes = proc_list;
+ return NULL;
+}