settings: Add reference feature
[strongswan.git] / src / libstrongswan / settings / settings_types.c
1 /*
2 * Copyright (C) 2010-2018 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "settings_types.h"
17
18 /*
19 * Described in header
20 */
21 kv_t *settings_kv_create(char *key, char *value)
22 {
23 kv_t *this;
24
25 INIT(this,
26 .key = key,
27 .value = value,
28 );
29 return this;
30 }
31
32 /*
33 * Described in header
34 */
35 void settings_kv_destroy(kv_t *this, array_t *contents)
36 {
37 free(this->key);
38 if (contents && this->value)
39 {
40 array_insert(contents, ARRAY_TAIL, this->value);
41 }
42 else
43 {
44 free(this->value);
45 }
46 free(this);
47 }
48
49 /*
50 * Described in header
51 */
52 section_t *settings_section_create(char *name)
53 {
54 section_t *this;
55
56 INIT(this,
57 .name = name,
58 );
59 return this;
60 }
61
62 static void section_destroy(section_t *section, int idx, array_t *contents)
63 {
64 settings_section_destroy(section, contents);
65 }
66
67 static void kv_destroy(kv_t *kv, int idx, array_t *contents)
68 {
69 settings_kv_destroy(kv, contents);
70 }
71
72 static void ref_destroy(section_ref_t *ref, int idx, void *ctx)
73 {
74 free(ref->name);
75 free(ref);
76 }
77
78 /*
79 * Described in header
80 */
81 void settings_section_destroy(section_t *this, array_t *contents)
82 {
83 array_destroy_function(this->sections, (void*)section_destroy, contents);
84 array_destroy(this->sections_order);
85 array_destroy_function(this->kv, (void*)kv_destroy, contents);
86 array_destroy(this->kv_order);
87 array_destroy_function(this->references, (void*)ref_destroy, NULL);
88 free(this->name);
89 free(this);
90 }
91
92 /*
93 * Described in header
94 */
95 void settings_kv_set(kv_t *kv, char *value, array_t *contents)
96 {
97 if (value && kv->value && streq(value, kv->value))
98 { /* no update required */
99 free(value);
100 return;
101 }
102
103 /* if the new value was shorter we could overwrite the existing one but that
104 * could lead to reads of partially updated values from other threads that
105 * have a pointer to the existing value, so we replace it anyway */
106 if (kv->value && contents)
107 {
108 array_insert(contents, ARRAY_TAIL, kv->value);
109 }
110 else
111 {
112 free(kv->value);
113 }
114 kv->value = value;
115 }
116
117 /*
118 * Described in header
119 */
120 void settings_kv_add(section_t *section, kv_t *kv, array_t *contents)
121 {
122 kv_t *found;
123
124 if (array_bsearch(section->kv, kv->key, settings_kv_find, &found) == -1)
125 {
126 array_insert_create(&section->kv, ARRAY_TAIL, kv);
127 array_sort(section->kv, settings_kv_sort, NULL);
128 array_insert_create(&section->kv_order, ARRAY_TAIL, kv);
129 }
130 else
131 {
132 settings_kv_set(found, kv->value, contents);
133 kv->value = NULL;
134 settings_kv_destroy(kv, NULL);
135 }
136 }
137
138 /*
139 * Described in header
140 */
141 void settings_reference_add(section_t *section, char *name, bool permanent)
142 {
143 section_ref_t *ref;
144 int i;
145
146 for (i = 0; i < array_count(section->references); i++)
147 {
148 array_get(section->references, i, &ref);
149 if (ref->permanent && !permanent)
150 { /* add it before any permanent references */
151 break;
152 }
153 if (ref->permanent == permanent && streq(name, ref->name))
154 {
155 free(name);
156 return;
157 }
158 }
159
160 INIT(ref,
161 .name = name,
162 .permanent = permanent,
163 );
164 array_insert_create(&section->references, i, ref);
165 }
166
167 /*
168 * Add a section to the given parent, optionally remove settings/subsections
169 * not found when extending an existing section
170 */
171 static void add_section(section_t *parent, section_t *section,
172 array_t *contents, bool purge)
173 {
174 section_t *found;
175
176 if (array_bsearch(parent->sections, section->name, settings_section_find,
177 &found) == -1)
178 {
179 array_insert_create(&parent->sections, ARRAY_TAIL, section);
180 array_sort(parent->sections, settings_section_sort, NULL);
181 array_insert_create(&parent->sections_order, ARRAY_TAIL, section);
182 }
183 else
184 {
185 settings_section_extend(found, section, contents, purge);
186 settings_section_destroy(section, contents);
187 }
188 }
189
190 /*
191 * Described in header
192 */
193 void settings_section_add(section_t *parent, section_t *section,
194 array_t *contents)
195 {
196 add_section(parent, section, contents, FALSE);
197 }
198
199 /**
200 * Purge contents of a section, returns TRUE if section can be safely removed.
201 */
202 static bool section_purge(section_t *this, array_t *contents)
203 {
204 section_t *current;
205 section_ref_t *ref;
206 int i, idx;
207
208 array_destroy_function(this->kv, (void*)kv_destroy, contents);
209 this->kv = NULL;
210 array_destroy(this->kv_order);
211 this->kv_order = NULL;
212 /* remove non-permanent references */
213 for (i = array_count(this->references) - 1; i >= 0; i--)
214 {
215 array_get(this->references, i, &ref);
216 if (!ref->permanent)
217 {
218 array_remove(this->references, i, NULL);
219 ref_destroy(ref, 0, NULL);
220 }
221 }
222 if (!array_count(this->references))
223 {
224 array_destroy(this->references);
225 this->references = NULL;
226 }
227 for (i = array_count(this->sections_order) - 1; i >= 0; i--)
228 {
229 array_get(this->sections_order, i, &current);
230 if (section_purge(current, contents))
231 {
232 array_remove(this->sections_order, i, NULL);
233 idx = array_bsearch(this->sections, current->name,
234 settings_section_find, NULL);
235 array_remove(this->sections, idx, NULL);
236 settings_section_destroy(current, contents);
237 }
238 }
239 /* we ensure sections configured with permanent references (or having any
240 * such subsections) are not removed */
241 return !this->references && !array_count(this->sections);
242 }
243
244 /*
245 * Described in header
246 */
247 void settings_section_extend(section_t *base, section_t *extension,
248 array_t *contents, bool purge)
249 {
250 enumerator_t *enumerator;
251 section_t *section;
252 section_ref_t *ref;
253 kv_t *kv;
254 array_t *sections = NULL, *kvs = NULL;
255 int idx;
256
257 if (purge)
258 { /* remove sections, settings in base not found in extension, the others
259 * are removed too (from the _order list) so they can be inserted in the
260 * order found in extension, non-permanent references are removed */
261 enumerator = array_create_enumerator(base->sections_order);
262 while (enumerator->enumerate(enumerator, (void**)&section))
263 {
264 if (array_bsearch(extension->sections, section->name,
265 settings_section_find, NULL) == -1)
266 {
267 idx = array_bsearch(base->sections, section->name,
268 settings_section_find, NULL);
269 if (section_purge(section, contents))
270 { /* only remove them if we can purge them */
271 array_remove(base->sections, idx, NULL);
272 array_remove_at(base->sections_order, enumerator);
273 settings_section_destroy(section, contents);
274 }
275 }
276 else
277 {
278 array_remove_at(base->sections_order, enumerator);
279 array_insert_create(&sections, ARRAY_TAIL, section);
280 array_sort(sections, settings_section_sort, NULL);
281 }
282 }
283 enumerator->destroy(enumerator);
284
285 while (array_remove(base->kv_order, 0, &kv))
286 {
287 if (array_bsearch(extension->kv, kv->key, settings_kv_find,
288 NULL) == -1)
289 {
290 idx = array_bsearch(base->kv, kv->key, settings_kv_find, NULL);
291 array_remove(base->kv, idx, NULL);
292 settings_kv_destroy(kv, contents);
293 }
294 else
295 {
296 array_insert_create(&kvs, ARRAY_TAIL, kv);
297 array_sort(kvs, settings_kv_sort, NULL);
298 }
299 }
300
301 enumerator = array_create_enumerator(base->references);
302 while (enumerator->enumerate(enumerator, (void**)&ref))
303 {
304 if (ref->permanent)
305 { /* permanent references are ignored */
306 continue;
307 }
308 array_remove_at(base->references, enumerator);
309 ref_destroy(ref, 0, NULL);
310 }
311 enumerator->destroy(enumerator);
312 }
313
314 while (array_remove(extension->sections_order, 0, &section))
315 {
316 idx = array_bsearch(sections, section->name,
317 settings_section_find, NULL);
318 if (idx != -1)
319 {
320 section_t *existing;
321
322 array_remove(sections, idx, &existing);
323 array_insert(base->sections_order, ARRAY_TAIL, existing);
324 }
325 idx = array_bsearch(extension->sections, section->name,
326 settings_section_find, NULL);
327 array_remove(extension->sections, idx, NULL);
328 add_section(base, section, contents, purge);
329 }
330
331 while (array_remove(extension->kv_order, 0, &kv))
332 {
333 idx = array_bsearch(kvs, kv->key, settings_kv_find, NULL);
334 if (idx != -1)
335 {
336 kv_t *existing;
337
338 array_remove(kvs, idx, &existing);
339 array_insert(base->kv_order, ARRAY_TAIL, existing);
340 }
341 idx = array_bsearch(extension->kv, kv->key, settings_kv_find, NULL);
342 array_remove(extension->kv, idx, NULL);
343 settings_kv_add(base, kv, contents);
344 }
345
346 while (array_remove(extension->references, 0, &ref))
347 {
348 if (ref->permanent)
349 { /* ignore permanent references in the extension */
350 continue;
351 }
352 settings_reference_add(base, strdup(ref->name), FALSE);
353 ref_destroy(ref, 0, NULL);
354 }
355 array_destroy(sections);
356 array_destroy(kvs);
357 }
358
359 /*
360 * Described in header
361 */
362 int settings_section_find(const void *a, const void *b)
363 {
364 const char *key = a;
365 const section_t *item = b;
366 return strcmp(key, item->name);
367 }
368
369 /*
370 * Described in header
371 */
372 int settings_section_sort(const void *a, const void *b, void *user)
373 {
374 const section_t *sa = a, *sb = b;
375 return strcmp(sa->name, sb->name);
376 }
377
378 /*
379 * Described in header
380 */
381 int settings_kv_find(const void *a, const void *b)
382 {
383 const char *key = a;
384 const kv_t *item = b;
385 return strcmp(key, item->key);
386 }
387
388 /*
389 * Described in header
390 */
391 int settings_kv_sort(const void *a, const void *b, void *user)
392 {
393 const kv_t *kva = a, *kvb = b;
394 return strcmp(kva->key, kvb->key);
395 }