854f30a91f
The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
260 lines
5.1 KiB
C
260 lines
5.1 KiB
C
/*
|
|
* CDDL HEADER START
|
|
*
|
|
* The contents of this file are subject to the terms of the
|
|
* Common Development and Distribution License (the "License").
|
|
* You may not use this file except in compliance with the License.
|
|
*
|
|
* You can obtain a copy of the license from the top-level
|
|
* OPENSOLARIS.LICENSE or <http://opensource.org/licenses/CDDL-1.0>.
|
|
* See the License for the specific language governing permissions
|
|
* and limitations under the License.
|
|
*
|
|
* When distributing Covered Code, include this CDDL HEADER in each file
|
|
* and include the License file from the top-level OPENSOLARIS.LICENSE.
|
|
* If applicable, add the following below this CDDL HEADER, with the
|
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
|
*
|
|
* CDDL HEADER END
|
|
*/
|
|
|
|
/*
|
|
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
|
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/avl.h>
|
|
#include <sys/sysmacros.h>
|
|
#include "zed_strings.h"
|
|
|
|
struct zed_strings {
|
|
avl_tree_t tree;
|
|
avl_node_t *iteratorp;
|
|
};
|
|
|
|
struct zed_strings_node {
|
|
avl_node_t node;
|
|
char *key;
|
|
char *val;
|
|
};
|
|
|
|
typedef struct zed_strings_node zed_strings_node_t;
|
|
|
|
/*
|
|
* Compare zed_strings_node_t nodes [x1] and [x2].
|
|
* As required for the AVL tree, return -1 for <, 0 for ==, and +1 for >.
|
|
*/
|
|
static int
|
|
_zed_strings_node_compare(const void *x1, const void *x2)
|
|
{
|
|
const char *s1;
|
|
const char *s2;
|
|
int rv;
|
|
|
|
assert(x1 != NULL);
|
|
assert(x2 != NULL);
|
|
|
|
s1 = ((const zed_strings_node_t *) x1)->key;
|
|
assert(s1 != NULL);
|
|
s2 = ((const zed_strings_node_t *) x2)->key;
|
|
assert(s2 != NULL);
|
|
rv = strcmp(s1, s2);
|
|
|
|
if (rv < 0)
|
|
return (-1);
|
|
|
|
if (rv > 0)
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Return a new string container, or NULL on error.
|
|
*/
|
|
zed_strings_t *
|
|
zed_strings_create(void)
|
|
{
|
|
zed_strings_t *zsp;
|
|
|
|
zsp = calloc(1, sizeof (*zsp));
|
|
if (!zsp)
|
|
return (NULL);
|
|
|
|
avl_create(&zsp->tree, _zed_strings_node_compare,
|
|
sizeof (zed_strings_node_t), offsetof(zed_strings_node_t, node));
|
|
|
|
zsp->iteratorp = NULL;
|
|
return (zsp);
|
|
}
|
|
|
|
/*
|
|
* Destroy the string node [np].
|
|
*/
|
|
static void
|
|
_zed_strings_node_destroy(zed_strings_node_t *np)
|
|
{
|
|
if (!np)
|
|
return;
|
|
|
|
if (np->key) {
|
|
if (np->key != np->val)
|
|
free(np->key);
|
|
np->key = NULL;
|
|
}
|
|
if (np->val) {
|
|
free(np->val);
|
|
np->val = NULL;
|
|
}
|
|
free(np);
|
|
}
|
|
|
|
/*
|
|
* Return a new string node for storing the string [val], or NULL on error.
|
|
* If [key] is specified, it will be used to index the node; otherwise,
|
|
* the string [val] will be used.
|
|
*/
|
|
zed_strings_node_t *
|
|
_zed_strings_node_create(const char *key, const char *val)
|
|
{
|
|
zed_strings_node_t *np;
|
|
|
|
assert(val != NULL);
|
|
|
|
np = calloc(1, sizeof (*np));
|
|
if (!np)
|
|
return (NULL);
|
|
|
|
np->val = strdup(val);
|
|
if (!np->val)
|
|
goto nomem;
|
|
|
|
if (key) {
|
|
np->key = strdup(key);
|
|
if (!np->key)
|
|
goto nomem;
|
|
} else {
|
|
np->key = np->val;
|
|
}
|
|
return (np);
|
|
|
|
nomem:
|
|
_zed_strings_node_destroy(np);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Destroy the string container [zsp] and all nodes within.
|
|
*/
|
|
void
|
|
zed_strings_destroy(zed_strings_t *zsp)
|
|
{
|
|
void *cookie;
|
|
zed_strings_node_t *np;
|
|
|
|
if (!zsp)
|
|
return;
|
|
|
|
cookie = NULL;
|
|
while ((np = avl_destroy_nodes(&zsp->tree, &cookie)))
|
|
_zed_strings_node_destroy(np);
|
|
|
|
avl_destroy(&zsp->tree);
|
|
free(zsp);
|
|
}
|
|
|
|
/*
|
|
* Add a copy of the string [s] indexed by [key] to the container [zsp].
|
|
* If [key] already exists within the container [zsp], it will be replaced
|
|
* with the new string [s].
|
|
* If [key] is NULL, the string [s] will be used as the key.
|
|
* Return 0 on success, or -1 on error.
|
|
*/
|
|
int
|
|
zed_strings_add(zed_strings_t *zsp, const char *key, const char *s)
|
|
{
|
|
zed_strings_node_t *newp, *oldp;
|
|
|
|
if (!zsp || !s) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
if (key == s)
|
|
key = NULL;
|
|
|
|
newp = _zed_strings_node_create(key, s);
|
|
if (!newp)
|
|
return (-1);
|
|
|
|
oldp = avl_find(&zsp->tree, newp, NULL);
|
|
if (oldp) {
|
|
avl_remove(&zsp->tree, oldp);
|
|
_zed_strings_node_destroy(oldp);
|
|
}
|
|
avl_add(&zsp->tree, newp);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Return the first string in container [zsp].
|
|
* Return NULL if there are no strings, or on error.
|
|
* This can be called multiple times to re-traverse [zsp].
|
|
* XXX: Not thread-safe.
|
|
*/
|
|
const char *
|
|
zed_strings_first(zed_strings_t *zsp)
|
|
{
|
|
if (!zsp) {
|
|
errno = EINVAL;
|
|
return (NULL);
|
|
}
|
|
zsp->iteratorp = avl_first(&zsp->tree);
|
|
if (!zsp->iteratorp)
|
|
return (NULL);
|
|
|
|
return (((zed_strings_node_t *) zsp->iteratorp)->val);
|
|
|
|
}
|
|
|
|
/*
|
|
* Return the next string in container [zsp].
|
|
* Return NULL after the last string, or on error.
|
|
* This must be called after zed_strings_first().
|
|
* XXX: Not thread-safe.
|
|
*/
|
|
const char *
|
|
zed_strings_next(zed_strings_t *zsp)
|
|
{
|
|
if (!zsp) {
|
|
errno = EINVAL;
|
|
return (NULL);
|
|
}
|
|
if (!zsp->iteratorp)
|
|
return (NULL);
|
|
|
|
zsp->iteratorp = AVL_NEXT(&zsp->tree, zsp->iteratorp);
|
|
if (!zsp->iteratorp)
|
|
return (NULL);
|
|
|
|
return (((zed_strings_node_t *)zsp->iteratorp)->val);
|
|
}
|
|
|
|
/*
|
|
* Return the number of strings in container [zsp], or -1 on error.
|
|
*/
|
|
int
|
|
zed_strings_count(zed_strings_t *zsp)
|
|
{
|
|
if (!zsp) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
return (avl_numnodes(&zsp->tree));
|
|
}
|