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