/* state.c - Network UPS Tools common state management functions
Copyright (C) 2003 Russell Kroll <rkroll@exploits.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "common.h"
#include "state.h"
#include "parseconf.h"
static void val_escape(struct st_tree_t *node)
{
char etmp[ST_MAX_VALUE_LEN];
/* escape any tricky stuff like \ and " */
pconf_encode(node->val, etmp, sizeof(etmp));
/* if nothing was escaped, we don't need to do anything else */
if (!strcmp(node->raw, etmp)) {
node->val = node->raw;
return;
}
/* first time: set a good starting place */
if (node->safesize == 0) {
node->safesize = strlen(etmp) + 1;
node->safe = xmalloc(node->safesize);
}
/* if the escaped value grew, deal with it */
if (strlen(etmp) > (node->safesize - 1)) {
node->safesize = strlen(etmp) + 1;
node->safe = xrealloc(node->safe, node->safesize);
}
snprintf(node->safe, node->safesize, "%s", etmp);
node->val = node->safe;
}
static void st_tree_enum_free(struct enum_t *list)
{
if (!list)
return;
st_tree_enum_free(list->next);
free(list->val);
free(list);
}
/* free all memory associated with a node */
static void st_tree_node_free(struct st_tree_t *node)
{
free(node->var);
free(node->raw);
free(node->safe);
/* never free node->val, since it's just a pointer to raw or safe */
/* blow away the list of enums */
st_tree_enum_free(node->enum_list);
/* now finally kill the node itself */
free(node);
}
/* add a subtree to another subtree */
static void st_tree_node_add(struct st_tree_t **nptr, struct st_tree_t *sptr)
{
struct st_tree_t *node = *nptr;
if (!sptr)
return;
if (!node) {
*nptr = sptr;
return;
}
if (strcmp(sptr->var, node->var) < 0)
st_tree_node_add(&node->left, sptr);
else
st_tree_node_add(&node->right, sptr);
}
/* remove a variable from a tree */
int state_delinfo(struct st_tree_t **nptr, const char *var)
{
struct st_tree_t *node = *nptr;
if (!node)
return 0; /* variable not found! */
if (strcasecmp(var, node->var) < 0)
return state_delinfo(&node->left, var);
if (strcasecmp(var, node->var) > 0)
return state_delinfo(&node->right, var);
/* apparently, we've found it! */
/* whatever is on the left, hang it off current right */
st_tree_node_add(&node->right, node->left);
/* now point the parent at the old right child */
*nptr = node->right;
st_tree_node_free(node);
return 1;
}
/* interface */
int state_setinfo(struct st_tree_t **nptr, const char *var, const char *val)
{
struct st_tree_t *node = *nptr;
if (!node) {
node = xmalloc(sizeof(struct st_tree_t));
node->var = xstrdup(var);
node->rawsize = strlen(val) + 1;
node->raw = xmalloc(node->rawsize);
snprintf(node->raw, node->rawsize, "%s", val);
/* this is usually sufficient if nothing gets escaped */
node->val = node->raw;
node->safesize = 0;
node->safe = NULL;
/* but see if it needs escaping anyway */
val_escape(node);
/* these are updated by other functions */
node->flags = 0;
node->aux = 0;
node->enum_list = NULL;
node->left = NULL;
node->right = NULL;
/* now we're done with creating a new node, add it to the tree */
*nptr = node;
return 1; /* added */
}
if (strcasecmp(var, node->var) < 0)
return state_setinfo(&node->left, var, val);
if (strcasecmp(var, node->var) > 0)
return state_setinfo(&node->right, var, val);
/* var must equal node->var - updating an existing entry */
if (!strcasecmp(node->raw, val))
return 0; /* no change */
/* expand the buffer if the value grows */
if (strlen(val) > (node->rawsize - 1)) {
node->rawsize = strlen(val) + 1;
node->raw = xrealloc(node->raw, node->rawsize);
node->val = node->raw;
}
/* store the literal value for later comparisons */
snprintf(node->raw, node->rawsize, "%s", val);
val_escape(node);
return 1; /* changed */
}
static void st_tree_enum_add(struct enum_t **list, const char *enc)
{
struct enum_t *item = *list;
if (!item) { /* end of list reached */
item = xmalloc(sizeof(struct enum_t));
item->val = xstrdup(enc);
item->next = NULL;
/* now we're done creating it, add it to the list */
*list = item;
return;
}
/* don't add duplicates - silently ignore them */
if (!strcmp(item->val, enc))
return;
st_tree_enum_add(&item->next, enc);
}
int state_addenum(struct st_tree_t *root, const char *var, const char *value)
{
struct st_tree_t *sttmp;
char enc[ST_MAX_VALUE_LEN];
/* find the tree node for var */
sttmp = state_tree_find(root, var);
if (!sttmp) {
upslogx(LOG_ERR, "state_addenum: base variable (%s) "
"does not exist", var);
return 0; /* failed */
}
/* smooth over any oddities in the enum value */
pconf_encode(value, enc, sizeof(enc));
st_tree_enum_add(&sttmp->enum_list, enc);
return 1;
}
int state_setaux(struct st_tree_t *root, const char *var, const char *auxs)
{
struct st_tree_t *sttmp;
int aux;
/* find the tree node for var */
sttmp = state_tree_find(root, var);
if (!sttmp) {
upslogx(LOG_ERR, "state_addenum: base variable (%s) "
"does not exist", var);
return -1; /* failed */
}
aux = strtol(auxs, (char **) NULL, 10);
/* silently ignore matches */
if (sttmp->aux == aux)
return 0;
sttmp->aux = aux;
return 1;
}
const char *state_getinfo(struct st_tree_t *root, const char *var)
{
struct st_tree_t *sttmp;
/* find the tree node for var */
sttmp = state_tree_find(root, var);
if (!sttmp)
return NULL;
return sttmp->val;
}
int state_getflags(struct st_tree_t *root, const char *var)
{
struct st_tree_t *sttmp;
/* find the tree node for var */
sttmp = state_tree_find(root, var);
if (!sttmp)
return -1;
return sttmp->flags;
}
int state_getaux(struct st_tree_t *root, const char *var)
{
struct st_tree_t *sttmp;
/* find the tree node for var */
sttmp = state_tree_find(root, var);
if (!sttmp)
return -1;
return sttmp->aux;
}
const struct enum_t *state_getenumlist(struct st_tree_t *root, const char *var)
{
struct st_tree_t *sttmp;
/* find the tree node for var */
sttmp = state_tree_find(root, var);
if (!sttmp)
return NULL;
return sttmp->enum_list;
}
void state_setflags(struct st_tree_t *root, const char *var, int numflags,
char **flag)
{
int i;
struct st_tree_t *sttmp;
/* find the tree node for var */
sttmp = state_tree_find(root, var);
if (!sttmp) {
upslogx(LOG_ERR, "state_setflags: base variable (%s) "
"does not exist", var);
return;
}
sttmp->flags = 0;
for (i = 0; i < numflags; i++) {
if (!strcasecmp(flag[i], "RW")) {
sttmp->flags |= ST_FLAG_RW;
continue;
}
if (!strcasecmp(flag[i], "STRING")) {
sttmp->flags |= ST_FLAG_STRING;
continue;
}
upsdebugx(2, "Unrecognized flag [%s]", flag[i]);
}
}
int state_addcmd(struct cmdlist_t **list, const char *cmdname)
{
struct cmdlist_t *item = *list;
if (!item) { /* end of list reached */
item = xmalloc(sizeof(struct cmdlist_t));
item->name = xstrdup(cmdname);
item->next = NULL;
/* now we're done creating it, add it to the list */
*list = item;
return 1;
}
/* don't add duplicates - silently ignore them */
if (!strcasecmp(item->name, cmdname))
return 0;
return state_addcmd(&item->next, cmdname);
}
void state_infofree(struct st_tree_t *node)
{
if (!node)
return;
state_infofree(node->left);
state_infofree(node->right);
st_tree_node_free(node);
}
void state_cmdfree(struct cmdlist_t *list)
{
if (!list)
return;
state_cmdfree(list->next);
free(list->name);
free(list);
}
int state_delcmd(struct cmdlist_t **list, const char *cmd)
{
struct cmdlist_t *item = *list;
if (!item) /* not found */
return 0;
/* if this is not the right command, go on to the next */
if (strcmp(item->name, cmd))
return state_delcmd(&item->next, cmd);
/* we found it! */
*list = item->next;
free(item->name);
free(item);
return 1; /* deleted */
}
static int st_tree_del_enum(struct enum_t **list, const char *val)
{
struct enum_t *item = *list;
if (!item) /* not found */
return 0;
/* if this is not the right value, go on to the next */
if (strcmp(item->val, val))
return st_tree_del_enum(&item->next, val);
/* we found it! */
*list = item->next;
free(item->val);
free(item);
return 1; /* deleted */
}
int state_delenum(struct st_tree_t *root, const char *var, const char *val)
{
struct st_tree_t *sttmp;
/* find the tree node for var */
sttmp = state_tree_find(root, var);
if (!sttmp)
return 0;
return st_tree_del_enum(&sttmp->enum_list, val);
}
struct st_tree_t *state_tree_find(struct st_tree_t *node, const char *var)
{
if (!node)
return NULL;
if (strcasecmp(var, node->var) < 0)
return state_tree_find(node->left, var);
if (strcasecmp(var, node->var) > 0)
return state_tree_find(node->right, var);
return node;
}
syntax highlighted by Code2HTML, v. 0.9.1