1/* 2 * This file is part of the ZFS Event Daemon (ZED). 3 * 4 * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). 5 * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. 6 * Refer to the OpenZFS git commit log for authoritative copyright attribution. 7 * 8 * The contents of this file are subject to the terms of the 9 * Common Development and Distribution License Version 1.0 (CDDL-1.0). 10 * You can obtain a copy of the license from the top-level file 11 * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>. 12 * You may not use this file except in compliance with the license. 13 */ 14 15#include <assert.h> 16#include <errno.h> 17#include <stddef.h> 18#include <stdlib.h> 19#include <string.h> 20#include <sys/avl.h> 21#include <sys/sysmacros.h> 22#include "zed_strings.h" 23 24struct zed_strings { 25 avl_tree_t tree; 26 avl_node_t *iteratorp; 27}; 28 29struct zed_strings_node { 30 avl_node_t node; 31 char *key; 32 char *val; 33}; 34 35typedef struct zed_strings_node zed_strings_node_t; 36 37/* 38 * Compare zed_strings_node_t nodes [x1] and [x2]. 39 * As required for the AVL tree, return -1 for <, 0 for ==, and +1 for >. 40 */ 41static int 42_zed_strings_node_compare(const void *x1, const void *x2) 43{ 44 const char *s1; 45 const char *s2; 46 int rv; 47 48 assert(x1 != NULL); 49 assert(x2 != NULL); 50 51 s1 = ((const zed_strings_node_t *) x1)->key; 52 assert(s1 != NULL); 53 s2 = ((const zed_strings_node_t *) x2)->key; 54 assert(s2 != NULL); 55 rv = strcmp(s1, s2); 56 57 if (rv < 0) 58 return (-1); 59 60 if (rv > 0) 61 return (1); 62 63 return (0); 64} 65 66/* 67 * Return a new string container, or NULL on error. 68 */ 69zed_strings_t * 70zed_strings_create(void) 71{ 72 zed_strings_t *zsp; 73 74 zsp = calloc(1, sizeof (*zsp)); 75 if (!zsp) 76 return (NULL); 77 78 avl_create(&zsp->tree, _zed_strings_node_compare, 79 sizeof (zed_strings_node_t), offsetof(zed_strings_node_t, node)); 80 81 zsp->iteratorp = NULL; 82 return (zsp); 83} 84 85/* 86 * Destroy the string node [np]. 87 */ 88static void 89_zed_strings_node_destroy(zed_strings_node_t *np) 90{ 91 if (!np) 92 return; 93 94 if (np->key) { 95 if (np->key != np->val) 96 free(np->key); 97 np->key = NULL; 98 } 99 if (np->val) { 100 free(np->val); 101 np->val = NULL; 102 } 103 free(np); 104} 105 106/* 107 * Return a new string node for storing the string [val], or NULL on error. 108 * If [key] is specified, it will be used to index the node; otherwise, 109 * the string [val] will be used. 110 */ 111static zed_strings_node_t * 112_zed_strings_node_create(const char *key, const char *val) 113{ 114 zed_strings_node_t *np; 115 116 assert(val != NULL); 117 118 np = calloc(1, sizeof (*np)); 119 if (!np) 120 return (NULL); 121 122 np->val = strdup(val); 123 if (!np->val) 124 goto nomem; 125 126 if (key) { 127 np->key = strdup(key); 128 if (!np->key) 129 goto nomem; 130 } else { 131 np->key = np->val; 132 } 133 return (np); 134 135nomem: 136 _zed_strings_node_destroy(np); 137 return (NULL); 138} 139 140/* 141 * Destroy the string container [zsp] and all nodes within. 142 */ 143void 144zed_strings_destroy(zed_strings_t *zsp) 145{ 146 void *cookie; 147 zed_strings_node_t *np; 148 149 if (!zsp) 150 return; 151 152 cookie = NULL; 153 while ((np = avl_destroy_nodes(&zsp->tree, &cookie))) 154 _zed_strings_node_destroy(np); 155 156 avl_destroy(&zsp->tree); 157 free(zsp); 158} 159 160/* 161 * Add a copy of the string [s] indexed by [key] to the container [zsp]. 162 * If [key] already exists within the container [zsp], it will be replaced 163 * with the new string [s]. 164 * If [key] is NULL, the string [s] will be used as the key. 165 * Return 0 on success, or -1 on error. 166 */ 167int 168zed_strings_add(zed_strings_t *zsp, const char *key, const char *s) 169{ 170 zed_strings_node_t *newp, *oldp; 171 172 if (!zsp || !s) { 173 errno = EINVAL; 174 return (-1); 175 } 176 if (key == s) 177 key = NULL; 178 179 newp = _zed_strings_node_create(key, s); 180 if (!newp) 181 return (-1); 182 183 oldp = avl_find(&zsp->tree, newp, NULL); 184 if (oldp) { 185 avl_remove(&zsp->tree, oldp); 186 _zed_strings_node_destroy(oldp); 187 } 188 avl_add(&zsp->tree, newp); 189 return (0); 190} 191 192/* 193 * Return the first string in container [zsp]. 194 * Return NULL if there are no strings, or on error. 195 * This can be called multiple times to re-traverse [zsp]. 196 * XXX: Not thread-safe. 197 */ 198const char * 199zed_strings_first(zed_strings_t *zsp) 200{ 201 if (!zsp) { 202 errno = EINVAL; 203 return (NULL); 204 } 205 zsp->iteratorp = avl_first(&zsp->tree); 206 if (!zsp->iteratorp) 207 return (NULL); 208 209 return (((zed_strings_node_t *)zsp->iteratorp)->val); 210 211} 212 213/* 214 * Return the next string in container [zsp]. 215 * Return NULL after the last string, or on error. 216 * This must be called after zed_strings_first(). 217 * XXX: Not thread-safe. 218 */ 219const char * 220zed_strings_next(zed_strings_t *zsp) 221{ 222 if (!zsp) { 223 errno = EINVAL; 224 return (NULL); 225 } 226 if (!zsp->iteratorp) 227 return (NULL); 228 229 zsp->iteratorp = AVL_NEXT(&zsp->tree, zsp->iteratorp); 230 if (!zsp->iteratorp) 231 return (NULL); 232 233 return (((zed_strings_node_t *)zsp->iteratorp)->val); 234} 235 236/* 237 * Return the number of strings in container [zsp], or -1 on error. 238 */ 239int 240zed_strings_count(zed_strings_t *zsp) 241{ 242 if (!zsp) { 243 errno = EINVAL; 244 return (-1); 245 } 246 return (avl_numnodes(&zsp->tree)); 247} 248