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 zed_strings_node_t *n1 = x1; 46 const zed_strings_node_t *n2 = x2; 47 48 return (TREE_ISIGN(strcmp(n1->key, n2->key))); 49 } 50 51 /* 52 * Return a new string container, or NULL on error. 53 */ 54 zed_strings_t * 55 zed_strings_create(void) 56 { 57 zed_strings_t *zsp; 58 59 zsp = calloc(1, sizeof (*zsp)); 60 if (!zsp) 61 return (NULL); 62 63 avl_create(&zsp->tree, _zed_strings_node_compare, 64 sizeof (zed_strings_node_t), offsetof(zed_strings_node_t, node)); 65 66 zsp->iteratorp = NULL; 67 return (zsp); 68 } 69 70 /* 71 * Destroy the string node [np]. 72 */ 73 static void 74 _zed_strings_node_destroy(zed_strings_node_t *np) 75 { 76 if (!np) 77 return; 78 79 if (np->key) { 80 if (np->key != np->val) 81 free(np->key); 82 np->key = NULL; 83 } 84 if (np->val) { 85 free(np->val); 86 np->val = NULL; 87 } 88 free(np); 89 } 90 91 /* 92 * Return a new string node for storing the string [val], or NULL on error. 93 * If [key] is specified, it will be used to index the node; otherwise, 94 * the string [val] will be used. 95 */ 96 static zed_strings_node_t * 97 _zed_strings_node_create(const char *key, const char *val) 98 { 99 zed_strings_node_t *np; 100 101 assert(val != NULL); 102 103 np = calloc(1, sizeof (*np)); 104 if (!np) 105 return (NULL); 106 107 np->val = strdup(val); 108 if (!np->val) 109 goto nomem; 110 111 if (key) { 112 np->key = strdup(key); 113 if (!np->key) 114 goto nomem; 115 } else { 116 np->key = np->val; 117 } 118 return (np); 119 120 nomem: 121 _zed_strings_node_destroy(np); 122 return (NULL); 123 } 124 125 /* 126 * Destroy the string container [zsp] and all nodes within. 127 */ 128 void 129 zed_strings_destroy(zed_strings_t *zsp) 130 { 131 void *cookie; 132 zed_strings_node_t *np; 133 134 if (!zsp) 135 return; 136 137 cookie = NULL; 138 while ((np = avl_destroy_nodes(&zsp->tree, &cookie))) 139 _zed_strings_node_destroy(np); 140 141 avl_destroy(&zsp->tree); 142 free(zsp); 143 } 144 145 /* 146 * Add a copy of the string [s] indexed by [key] to the container [zsp]. 147 * If [key] already exists within the container [zsp], it will be replaced 148 * with the new string [s]. 149 * If [key] is NULL, the string [s] will be used as the key. 150 * Return 0 on success, or -1 on error. 151 */ 152 int 153 zed_strings_add(zed_strings_t *zsp, const char *key, const char *s) 154 { 155 zed_strings_node_t *newp, *oldp; 156 157 if (!zsp || !s) { 158 errno = EINVAL; 159 return (-1); 160 } 161 if (key == s) 162 key = NULL; 163 164 newp = _zed_strings_node_create(key, s); 165 if (!newp) 166 return (-1); 167 168 oldp = avl_find(&zsp->tree, newp, NULL); 169 if (oldp) { 170 avl_remove(&zsp->tree, oldp); 171 _zed_strings_node_destroy(oldp); 172 } 173 avl_add(&zsp->tree, newp); 174 return (0); 175 } 176 177 /* 178 * Return the first string in container [zsp]. 179 * Return NULL if there are no strings, or on error. 180 * This can be called multiple times to re-traverse [zsp]. 181 * XXX: Not thread-safe. 182 */ 183 const char * 184 zed_strings_first(zed_strings_t *zsp) 185 { 186 if (!zsp) { 187 errno = EINVAL; 188 return (NULL); 189 } 190 zsp->iteratorp = avl_first(&zsp->tree); 191 if (!zsp->iteratorp) 192 return (NULL); 193 194 return (((zed_strings_node_t *)zsp->iteratorp)->val); 195 196 } 197 198 /* 199 * Return the next string in container [zsp]. 200 * Return NULL after the last string, or on error. 201 * This must be called after zed_strings_first(). 202 * XXX: Not thread-safe. 203 */ 204 const char * 205 zed_strings_next(zed_strings_t *zsp) 206 { 207 if (!zsp) { 208 errno = EINVAL; 209 return (NULL); 210 } 211 if (!zsp->iteratorp) 212 return (NULL); 213 214 zsp->iteratorp = AVL_NEXT(&zsp->tree, zsp->iteratorp); 215 if (!zsp->iteratorp) 216 return (NULL); 217 218 return (((zed_strings_node_t *)zsp->iteratorp)->val); 219 } 220 221 /* 222 * Return the number of strings in container [zsp], or -1 on error. 223 */ 224 int 225 zed_strings_count(zed_strings_t *zsp) 226 { 227 if (!zsp) { 228 errno = EINVAL; 229 return (-1); 230 } 231 return (avl_numnodes(&zsp->tree)); 232 } 233