xref: /src/sys/contrib/openzfs/cmd/zed/zed_strings.c (revision 80aae8a3f8aa70712930664572be9e6885dc0be7)
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