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
_zed_strings_node_compare(const void * x1,const void * x2)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 *
zed_strings_create(void)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
_zed_strings_node_destroy(zed_strings_node_t * np)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 *
_zed_strings_node_create(const char * key,const char * val)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
zed_strings_destroy(zed_strings_t * zsp)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
zed_strings_add(zed_strings_t * zsp,const char * key,const char * s)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 *
zed_strings_first(zed_strings_t * zsp)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 *
zed_strings_next(zed_strings_t * zsp)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
zed_strings_count(zed_strings_t * zsp)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