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