xref: /freebsd/sys/contrib/openzfs/cmd/zed/zed_strings.c (revision 22649d4dba730d46244fd2dff4fd174903c8379f)
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