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