2291 lines
57 KiB
C
2291 lines
57 KiB
C
/*
|
|
* rules.c - rule description parsing routines (rules & pmie config)
|
|
*
|
|
* Copyright 1998, Silicon Graphics, Inc.
|
|
* ALL RIGHTS RESERVED
|
|
*
|
|
* UNPUBLISHED -- Rights reserved under the copyright laws of the United
|
|
* States. Use of a copyright notice is precautionary only and does not
|
|
* imply publication or disclosure.
|
|
*
|
|
* U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND:
|
|
* Use, duplication or disclosure by the Government is subject to restrictions
|
|
* as set forth in FAR 52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights
|
|
* in Technical Data and Computer Software clause at DFARS 252.227-7013 and/or
|
|
* in similar or successor clauses in the FAR, or the DOD or NASA FAR
|
|
* Supplement. Contractor/manufacturer is Silicon Graphics, Inc.,
|
|
* 2011 N. Shoreline Blvd. Mountain View, CA 94039-7311.
|
|
*
|
|
* THE CONTENT OF THIS WORK CONTAINS CONFIDENTIAL AND PROPRIETARY
|
|
* INFORMATION OF SILICON GRAPHICS, INC. ANY DUPLICATION, MODIFICATION,
|
|
* DISTRIBUTION, OR DISCLOSURE IN ANY FORM, IN WHOLE, OR IN PART, IS STRICTLY
|
|
* PROHIBITED WITHOUT THE PRIOR EXPRESS WRITTEN PERMISSION OF SILICON
|
|
* GRAPHICS, INC.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include "pmapi.h"
|
|
#include "rules.h"
|
|
#include "pmie/src/pmiestats.h"
|
|
|
|
|
|
#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
|
|
|
|
extern int errno;
|
|
|
|
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 */
|
|
sprintf(errmsg, "insufficient memory for requested operation.\n"
|
|
" requested: %u bytes", request);
|
|
else
|
|
sprintf(errmsg, "insufficient memory for parsing file.\n"
|
|
" requested: %u bytes", request);
|
|
}
|
|
|
|
static void
|
|
parse_error(char *expected, char *found)
|
|
{
|
|
if (linenum == 0) /* parsing user input, not a file */
|
|
sprintf(errmsg, "input is invalid - expected %.60s, got \"%.60s\"",
|
|
expected, found);
|
|
else
|
|
sprintf(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)
|
|
{
|
|
sprintf(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;
|
|
}
|
|
}
|
|
sprintf(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) {
|
|
sprintf(errmsg, "insufficient memory for rule search"
|
|
" (needed %u bytes)\n", size);
|
|
return errmsg;
|
|
}
|
|
rptr[matches] = &rulelist[i];
|
|
matches++;
|
|
}
|
|
}
|
|
if (matches == 0) {
|
|
sprintf(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) {
|
|
sprintf(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(*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(*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(*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;
|
|
|
|
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:
|
|
strtod(value, &s);
|
|
if (*s != '\0') {
|
|
type_error(name, "be a real number");
|
|
return errmsg;
|
|
}
|
|
break;
|
|
case TYPE_INTEGER:
|
|
strtol(value, &s, 10);
|
|
if (*s != '\0') {
|
|
type_error(name, "be an integer number");
|
|
return errmsg;
|
|
}
|
|
break;
|
|
case TYPE_UNSIGNED:
|
|
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) {
|
|
sprintf(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:
|
|
sprintf(token, "%g", strtod(atom->data, &s));
|
|
return token;
|
|
case TYPE_INTEGER:
|
|
sprintf(token, "%d", strtol(atom->data, &s, 10));
|
|
return token;
|
|
case TYPE_UNSIGNED:
|
|
sprintf(token, "%u", strtoul(atom->data, &s, 10));
|
|
return token;
|
|
case TYPE_PERCENT:
|
|
sprintf(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 */
|
|
p->next = a->next;
|
|
free(a->name);
|
|
free(a);
|
|
a = NULL;
|
|
}
|
|
else
|
|
a->enabled = a->denabled; /* reset enabled flag */
|
|
a->changed = 0;
|
|
return NULL;
|
|
}
|
|
sprintf(errmsg, "variable \"%s\" is inappropriate for this "
|
|
"operation", param);
|
|
return errmsg;
|
|
}
|
|
else {
|
|
if (a->global) { /* this was a global atom promoted to local */
|
|
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) {
|
|
sprintf(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)
|
|
return atom_defaults(&rule->self, NULL, param);
|
|
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) {
|
|
sprintf(errmsg, "insufficient memory to change value");
|
|
return errmsg;
|
|
}
|
|
return set_attribute(globals, aptr, ATTRIB_DEFAULT, value, 1);
|
|
}
|
|
}
|
|
}
|
|
sprintf(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 = NULL;
|
|
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 *)realloc(s, 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) {
|
|
sprintf(errmsg, "variable \"$%s$\" in %s is undefined",
|
|
localbuf, rule->self.name);
|
|
return NULL;
|
|
}
|
|
}
|
|
if (tmp != NULL) {
|
|
if ((r = dollar_expand(rule, tmp, pp)) == NULL)
|
|
return NULL;
|
|
if ((s = append_string(s, r, strlen(r))) == NULL)
|
|
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 (;;) {
|
|
errno = 0;
|
|
c = getc(f);
|
|
/* did we get told to resize the window during read? */
|
|
if (c == -1 && errno == 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);
|
|
}
|
|
if (c == '\n' && end != '\n')
|
|
linenum++;
|
|
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);
|
|
return errmsg;
|
|
}
|
|
if (set_attribute(r, &atom, attrib, value, 0) != NULL)
|
|
return errmsg;
|
|
free(attr);
|
|
free(value);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* 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) {
|
|
sprintf(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)) {
|
|
sprintf(errmsg, "rules not allowed in global group: \"%s\"", *name);
|
|
free(*name);
|
|
return errmsg;
|
|
}
|
|
else if (!*global && !IS_RULE(*type)) { /* not global, and no rule */
|
|
sprintf(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);
|
|
return errmsg;
|
|
}
|
|
if (set_rule_attribute(&rule, attrib, value) != NULL)
|
|
return errmsg;
|
|
free(attr);
|
|
free(value);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* parse rule description file; returns NULL/failure message */
|
|
static char *
|
|
read_all_rules(FILE *f)
|
|
{
|
|
rule_t *rule = NULL; /* current rule */
|
|
char *name;
|
|
int type;
|
|
int global;
|
|
|
|
/* 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) {
|
|
sprintf(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 */
|
|
sprintf(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) {
|
|
sprintf(errmsg, "cannot stat %s: %s", subdir, strerror(errno));
|
|
return errmsg;
|
|
}
|
|
if (!S_ISDIR(sbuf.st_mode)) {
|
|
if ((fp = fopen(subdir, "r")) == NULL) {
|
|
sprintf(errmsg, "cannot open %s: %s", subdir, strerror(errno));
|
|
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) {
|
|
sprintf(errmsg, "cannot opendir %s: %s", subdir, strerror(errno));
|
|
return errmsg;
|
|
}
|
|
while ((dp = readdir(dirp)) != NULL) { /* groups */
|
|
if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
|
|
continue;
|
|
sprintf(fullpath, "%s/%s", subdir, 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) {
|
|
sprintf(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) {
|
|
sprintf(errmsg, "insufficient memory to change value");
|
|
return errmsg;
|
|
}
|
|
return set_attribute(globals, aptr, ATTRIB_DEFAULT, value, 1);
|
|
}
|
|
}
|
|
}
|
|
sprintf(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, action_string(atom->type));
|
|
fprintf(f, 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; /* 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) {
|
|
sprintf(errmsg, "\"$delta$\" variable expansion failed for rule %s",
|
|
rule->self.name);
|
|
return errmsg;
|
|
}
|
|
if ((pgen = dollar_expand(rule, rule->predicate, 0)) == NULL) {
|
|
sprintf(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) {
|
|
sprintf(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;
|
|
sprintf(errmsg, "variable \"%s\" undefined for enumerated"
|
|
" rule %s", token, rule->self.name);
|
|
return errmsg;
|
|
}
|
|
foundname:
|
|
if (ap->type != TYPE_HOSTLIST && ap->type != TYPE_INSTLIST) {
|
|
sprintf(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) {
|
|
sprintf(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) {
|
|
sprintf(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) {
|
|
sprintf(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) {
|
|
sprintf(errmsg, "insufficient memory for rule enumeration");
|
|
return errmsg;
|
|
}
|
|
if ((list[i].valuelist = get_listitems(p, &j)) == NULL)
|
|
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) {
|
|
sprintf(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)
|
|
{
|
|
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) {
|
|
sprintf(buf, "/bin/mkdir -p %s", fname);
|
|
if (system(buf) < 0) {
|
|
sprintf(errmsg, "failed to create directory \"%s\"", p);
|
|
return errmsg;
|
|
}
|
|
}
|
|
else if (!S_ISDIR(sbuf.st_mode)) {
|
|
sprintf(errmsg, "\"%s\" exists and is not a directory", p);
|
|
return errmsg;
|
|
}
|
|
fname[strlen(fname)] = '/'; /* stitch together */
|
|
}
|
|
|
|
if ((fp = fopen(fname, "w")) == NULL) {
|
|
sprintf(errmsg, "cannot write file %s: %s", fname, strerror(errno));
|
|
return errmsg;
|
|
}
|
|
else if (!gotpath) {
|
|
strcpy(token, fname);
|
|
if (realpath(token, pmiefile) == NULL) {
|
|
fclose(fp);
|
|
sprintf(errmsg, "failed to resolve %s realpath: %s", token, strerror(errno));
|
|
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// generated by %s on: %s//\n\n",
|
|
START_STRING, 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);
|
|
}
|
|
if (c == '\n') {
|
|
linenum++;
|
|
if (getc(f) != '/') return 0;
|
|
if (getc(f) != '/') return 0;
|
|
}
|
|
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! */
|
|
sprintf(buf, "// %u %s (deprecated, %s)\n",
|
|
version, token, drulestring);
|
|
deprecate_rule(token, version, DEPRECATE_NORULE);
|
|
saverule = 1;
|
|
}
|
|
else if (rule->version != version) { /* not supported! */
|
|
sprintf(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) {
|
|
sprintf(errmsg, "insufficient memory to deprecate a rule");
|
|
return errmsg;
|
|
}
|
|
}
|
|
}
|
|
else if (save_area_append(buf) == NULL) {
|
|
sprintf(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) {
|
|
sprintf(errmsg, "%s is not a rule customization file (bad header)",
|
|
filename);
|
|
return errmsg;
|
|
}
|
|
else if (strcmp(token, PMIE_VERSION) != 0) { /* one version only */
|
|
sprintf(errmsg, "unknown version number in %s: \"%s\" (expected %s)",
|
|
filename, token, PMIE_VERSION);
|
|
return errmsg;
|
|
}
|
|
else if (read_ltoken(f) != 1) {
|
|
sprintf(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)
|
|
{
|
|
char *tmp = NULL;
|
|
char *p, *home;
|
|
FILE *f;
|
|
|
|
if ((f = fopen(get_pmiefile(), "r")) == NULL) {
|
|
if (errno == ENOENT)
|
|
return NULL;
|
|
sprintf(errmsg, "cannot open %s: %s", get_pmiefile(), strerror(errno));
|
|
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) {
|
|
sprintf(errmsg, "insufficient memory for pmie file parsing");
|
|
return errmsg;
|
|
}
|
|
p = strtok(home, ":");
|
|
while (p != NULL) {
|
|
if (access(p, F_OK) < 0) {
|
|
free(home);
|
|
sprintf(errmsg, "cannot access rules path component: \"%s\"", p);
|
|
return errmsg;
|
|
}
|
|
p = strtok(NULL, ":");
|
|
}
|
|
free(home);
|
|
|
|
if (strcmp(get_rules(), tmp) != 0)
|
|
sprintf(warning, "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)
|
|
{
|
|
char *p;
|
|
char *home;
|
|
rule_t global;
|
|
|
|
/* setup pointers to the configuration files */
|
|
if (getuid() == 0) {
|
|
if (in_pmie == NULL)
|
|
strcpy(pmiefile, DEFAULT_ROOT_PMIE);
|
|
else if (realpath(in_pmie, pmiefile) == NULL && errno != ENOENT) {
|
|
sprintf(errmsg, "failed to resolve realpath for %s: %s",
|
|
in_pmie, strerror(errno));
|
|
return errmsg;
|
|
}
|
|
else if (errno != ENOENT)
|
|
gotpath = 1;
|
|
}
|
|
else {
|
|
if ((home = getenv("HOME")) == NULL) {
|
|
sprintf(errmsg, "$HOME undefined in environment");
|
|
return errmsg;
|
|
}
|
|
if (in_pmie == NULL)
|
|
sprintf(pmiefile, "%s/%s", home, DEFAULT_USER_PMIE);
|
|
else
|
|
strcpy(pmiefile, in_pmie);
|
|
}
|
|
|
|
if (in_rules == NULL) {
|
|
if ((p = getenv("PMIECONF_PATH")) == NULL)
|
|
strcpy(rulepath, DEFAULT_RULES);
|
|
else
|
|
strcpy(rulepath, p);
|
|
}
|
|
else
|
|
sprintf(rulepath, 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) */
|
|
sprintf(errmsg, "insufficient memory for global parameters");
|
|
return errmsg;
|
|
}
|
|
|
|
if ((home = strdup(rulepath)) == NULL) {
|
|
sprintf(errmsg, "insufficient memory for rules path parsing");
|
|
return errmsg;
|
|
}
|
|
p = strtok(home, ":");
|
|
while (p != NULL) {
|
|
if (read_rule_subdir(p) != NULL) {
|
|
free(home);
|
|
return errmsg;
|
|
}
|
|
p = strtok(NULL, ":");
|
|
}
|
|
free(home);
|
|
|
|
if (read_pmiefile(warning) != 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;
|
|
|
|
if ((dirp = opendir(PMIE_DIR)) == NULL) {
|
|
sprintf(errmsg, "cannot opendir %s: %s", PMIE_DIR, strerror(errno));
|
|
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;
|
|
sprintf(proc, "%s/%s", PROC_DIR, dp->d_name); /* check /proc */
|
|
if (access(proc, F_OK) < 0)
|
|
continue; /* process has exited */
|
|
sprintf(proc, "%s/%s", PMIE_DIR, 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 = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
|
close(fd);
|
|
if (ptr == MAP_FAILED)
|
|
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) {
|
|
sprintf(errmsg, "insufficient memory for process search");
|
|
closedir(dirp);
|
|
close(fd);
|
|
return errmsg;
|
|
}
|
|
running++;
|
|
}
|
|
closedir(dirp);
|
|
*count = running;
|
|
*processes = proc_list;
|
|
return NULL;
|
|
}
|