1101209Srwatson/*-
2126218Srwatson * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3101209Srwatson * All rights reserved.
4101209Srwatson *
5101209Srwatson * Redistribution and use in source and binary forms, with or without
6101209Srwatson * modification, are permitted provided that the following conditions
7101209Srwatson * are met:
8101209Srwatson * 1. Redistributions of source code must retain the above copyright
9101209Srwatson *    notice, this list of conditions and the following disclaimer.
10101209Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11101209Srwatson *    notice, this list of conditions and the following disclaimer in the
12101209Srwatson *    documentation and/or other materials provided with the distribution.
13101209Srwatson *
14101209Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15101209Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16101209Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17101209Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18101209Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19101209Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20101209Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21101209Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22101209Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23101209Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24101209Srwatson * SUCH DAMAGE.
25101209Srwatson *
26101209Srwatson */
27101209Srwatson
28101209Srwatson#include <sys/cdefs.h>
29101209Srwatson__FBSDID("$FreeBSD: releng/11.0/usr.sbin/nscd/config.c 238094 2012-07-04 09:02:12Z se $");
30101209Srwatson
31140343Scharnier#include <sys/stat.h>
32140343Scharnier#include <sys/time.h>
33140343Scharnier
34140343Scharnier#include <assert.h>
35101209Srwatson#include <math.h>
36101209Srwatson#include <nsswitch.h>
37157986Sdwmalone#include <pthread.h>
38101209Srwatson#include <stdio.h>
39101209Srwatson#include <stdlib.h>
40101209Srwatson#include <string.h>
41101209Srwatson
42101209Srwatson#include "config.h"
43140343Scharnier#include "debug.h"
44101209Srwatson#include "log.h"
45101209Srwatson
46101209Srwatson/*
47101209Srwatson * Default entries, which always exist in the configuration
48101209Srwatson */
49140343Scharnierconst char *c_default_entries[6] = {
50140343Scharnier	NSDB_PASSWD,
51140343Scharnier	NSDB_GROUP,
52140343Scharnier	NSDB_HOSTS,
53140343Scharnier	NSDB_SERVICES,
54140343Scharnier	NSDB_PROTOCOLS,
55101209Srwatson	NSDB_RPC
56101209Srwatson	};
57101209Srwatson
58101209Srwatsonstatic int configuration_entry_cmp(const void *, const void *);
59140343Scharnierstatic int configuration_entry_sort_cmp(const void *, const void *);
60126218Srwatsonstatic int configuration_entry_cache_mp_sort_cmp(const void *, const void *);
61126218Srwatsonstatic int configuration_entry_cache_mp_cmp(const void *, const void *);
62140343Scharnierstatic int configuration_entry_cache_mp_part_cmp(const void *, const void *);
63140343Scharnierstatic struct configuration_entry *create_configuration_entry(const char *,
64101209Srwatson	struct timeval const *, struct timeval const *,
65101209Srwatson	struct common_cache_entry_params const *,
66140343Scharnier	struct common_cache_entry_params const *,
67101209Srwatson	struct mp_cache_entry_params const *);
68140343Scharnier
69101209Srwatsonstatic int
70101209Srwatsonconfiguration_entry_sort_cmp(const void *e1, const void *e2)
71101209Srwatson{
72126218Srwatson	return (strcmp((*((struct configuration_entry **)e1))->name,
73126218Srwatson		(*((struct configuration_entry **)e2))->name
74186480Srwatson		));
75126218Srwatson}
76126218Srwatson
77126218Srwatsonstatic int
78126218Srwatsonconfiguration_entry_cmp(const void *e1, const void *e2)
79126218Srwatson{
80140343Scharnier	return (strcmp((const char *)e1,
81126218Srwatson		(*((struct configuration_entry **)e2))->name
82126218Srwatson		));
83126218Srwatson}
84126218Srwatson
85126218Srwatsonstatic int
86140343Scharnierconfiguration_entry_cache_mp_sort_cmp(const void *e1, const void *e2)
87126218Srwatson{
88126218Srwatson	return (strcmp((*((cache_entry *)e1))->params->entry_name,
89186480Srwatson		(*((cache_entry *)e2))->params->entry_name
90186480Srwatson		));
91186480Srwatson}
92186480Srwatson
93126218Srwatsonstatic int
94126218Srwatsonconfiguration_entry_cache_mp_cmp(const void *e1, const void *e2)
95126218Srwatson{
96101209Srwatson	return (strcmp((const char *)e1,
97101209Srwatson		(*((cache_entry *)e2))->params->entry_name
98101209Srwatson		));
99101209Srwatson}
100101209Srwatson
101101209Srwatsonstatic int
102101209Srwatsonconfiguration_entry_cache_mp_part_cmp(const void *e1, const void *e2)
103101209Srwatson{
104140343Scharnier	return (strncmp((const char *)e1,
105140343Scharnier		(*((cache_entry *)e2))->params->entry_name,
106140343Scharnier		strlen((const char *)e1)
107101209Srwatson		));
108101209Srwatson}
109101209Srwatson
110140343Scharnierstatic struct configuration_entry *
111140343Scharniercreate_configuration_entry(const char *name,
112101209Srwatson	struct timeval const *common_timeout,
113101209Srwatson	struct timeval const *mp_timeout,
114101209Srwatson	struct common_cache_entry_params const *positive_params,
115148240Savatar	struct common_cache_entry_params const *negative_params,
116101209Srwatson	struct mp_cache_entry_params const *mp_params)
117101209Srwatson{
118101209Srwatson	struct configuration_entry *retval;
119101209Srwatson	size_t	size;
120101209Srwatson	int res;
121140343Scharnier
122101209Srwatson	TRACE_IN(create_configuration_entry);
123101209Srwatson	assert(name != NULL);
124101209Srwatson	assert(positive_params != NULL);
125101209Srwatson	assert(negative_params != NULL);
126101209Srwatson	assert(mp_params != NULL);
127101209Srwatson
128140343Scharnier	retval = calloc(1,
129101209Srwatson		sizeof(*retval));
130101209Srwatson	assert(retval != NULL);
131101209Srwatson
132101209Srwatson	res = pthread_mutex_init(&retval->positive_cache_lock, NULL);
133101209Srwatson	if (res != 0) {
134101209Srwatson		free(retval);
135101209Srwatson		LOG_ERR_2("create_configuration_entry",
136101209Srwatson			"can't create positive cache lock");
137101209Srwatson		TRACE_OUT(create_configuration_entry);
138101209Srwatson		return (NULL);
139101209Srwatson	}
140101209Srwatson
141101209Srwatson	res = pthread_mutex_init(&retval->negative_cache_lock, NULL);
142101209Srwatson	if (res != 0) {
143101209Srwatson		pthread_mutex_destroy(&retval->positive_cache_lock);
144101209Srwatson		free(retval);
145101209Srwatson		LOG_ERR_2("create_configuration_entry",
146101209Srwatson			"can't create negative cache lock");
147101209Srwatson		TRACE_OUT(create_configuration_entry);
148101209Srwatson		return (NULL);
149101209Srwatson	}
150101209Srwatson
151101209Srwatson	res = pthread_mutex_init(&retval->mp_cache_lock, NULL);
152101209Srwatson	if (res != 0) {
153101209Srwatson		pthread_mutex_destroy(&retval->positive_cache_lock);
154101209Srwatson		pthread_mutex_destroy(&retval->negative_cache_lock);
155101209Srwatson		free(retval);
156101209Srwatson		LOG_ERR_2("create_configuration_entry",
157140343Scharnier			"can't create negative cache lock");
158101209Srwatson		TRACE_OUT(create_configuration_entry);
159101209Srwatson		return (NULL);
160101209Srwatson	}
161101209Srwatson
162101209Srwatson	memcpy(&retval->positive_cache_params, positive_params,
163140343Scharnier		sizeof(struct common_cache_entry_params));
164101209Srwatson	memcpy(&retval->negative_cache_params, negative_params,
165101209Srwatson		sizeof(struct common_cache_entry_params));
166101209Srwatson	memcpy(&retval->mp_cache_params, mp_params,
167101209Srwatson		sizeof(struct mp_cache_entry_params));
168101209Srwatson
169101209Srwatson	size = strlen(name);
170101209Srwatson	retval->name = calloc(1, size + 1);
171101209Srwatson	assert(retval->name != NULL);
172101209Srwatson	memcpy(retval->name, name, size);
173101209Srwatson
174101209Srwatson	memcpy(&retval->common_query_timeout, common_timeout,
175101209Srwatson		sizeof(struct timeval));
176101209Srwatson	memcpy(&retval->mp_query_timeout, mp_timeout,
177101209Srwatson		sizeof(struct timeval));
178101209Srwatson
179101209Srwatson	asprintf(&retval->positive_cache_params.cep.entry_name, "%s+", name);
180101209Srwatson	assert(retval->positive_cache_params.cep.entry_name != NULL);
181101209Srwatson
182101209Srwatson	asprintf(&retval->negative_cache_params.cep.entry_name, "%s-", name);
183101209Srwatson	assert(retval->negative_cache_params.cep.entry_name != NULL);
184101209Srwatson
185101209Srwatson	asprintf(&retval->mp_cache_params.cep.entry_name, "%s*", name);
186101209Srwatson	assert(retval->mp_cache_params.cep.entry_name != NULL);
187101209Srwatson
188101209Srwatson	TRACE_OUT(create_configuration_entry);
189101209Srwatson	return (retval);
190140343Scharnier}
191101209Srwatson
192101209Srwatson/*
193101209Srwatson * Creates configuration entry and fills it with default values
194101209Srwatson */
195101209Srwatsonstruct configuration_entry *
196101209Srwatsoncreate_def_configuration_entry(const char *name)
197101209Srwatson{
198101209Srwatson	struct common_cache_entry_params positive_params, negative_params;
199101209Srwatson	struct mp_cache_entry_params mp_params;
200126218Srwatson	struct timeval default_common_timeout, default_mp_timeout;
201126218Srwatson
202126218Srwatson	struct configuration_entry *res = NULL;
203101209Srwatson
204101209Srwatson	TRACE_IN(create_def_configuration_entry);
205101209Srwatson	memset(&positive_params, 0,
206101209Srwatson		sizeof(struct common_cache_entry_params));
207101209Srwatson	positive_params.cep.entry_type = CET_COMMON;
208101209Srwatson	positive_params.cache_entries_size = DEFAULT_CACHE_HT_SIZE;
209101209Srwatson	positive_params.max_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE;
210101209Srwatson	positive_params.satisf_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE / 2;
211101209Srwatson	positive_params.max_lifetime.tv_sec = DEFAULT_POSITIVE_LIFETIME;
212101209Srwatson	positive_params.confidence_threshold = DEFAULT_POSITIVE_CONF_THRESH;
213101209Srwatson	positive_params.policy = CPT_LRU;
214101209Srwatson
215	memcpy(&negative_params, &positive_params,
216		sizeof(struct common_cache_entry_params));
217	negative_params.max_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE;
218	negative_params.satisf_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE / 2;
219	negative_params.max_lifetime.tv_sec = DEFAULT_NEGATIVE_LIFETIME;
220	negative_params.confidence_threshold = DEFAULT_NEGATIVE_CONF_THRESH;
221	negative_params.policy = CPT_FIFO;
222
223	memset(&default_common_timeout, 0, sizeof(struct timeval));
224	default_common_timeout.tv_sec = DEFAULT_COMMON_ENTRY_TIMEOUT;
225
226	memset(&default_mp_timeout, 0, sizeof(struct timeval));
227	default_mp_timeout.tv_sec = DEFAULT_MP_ENTRY_TIMEOUT;
228
229	memset(&mp_params, 0,
230		sizeof(struct mp_cache_entry_params));
231	mp_params.cep.entry_type = CET_MULTIPART;
232	mp_params.max_elemsize = DEFAULT_MULTIPART_ELEMENTS_SIZE;
233	mp_params.max_sessions = DEFAULT_MULITPART_SESSIONS_SIZE;
234	mp_params.max_lifetime.tv_sec = DEFAULT_MULITPART_LIFETIME;
235
236	res = create_configuration_entry(name, &default_common_timeout,
237		&default_mp_timeout, &positive_params, &negative_params,
238		&mp_params);
239
240	TRACE_OUT(create_def_configuration_entry);
241	return (res);
242}
243
244void
245destroy_configuration_entry(struct configuration_entry *entry)
246{
247	TRACE_IN(destroy_configuration_entry);
248	assert(entry != NULL);
249	pthread_mutex_destroy(&entry->positive_cache_lock);
250	pthread_mutex_destroy(&entry->negative_cache_lock);
251	pthread_mutex_destroy(&entry->mp_cache_lock);
252	free(entry->name);
253	free(entry->positive_cache_params.cep.entry_name);
254	free(entry->negative_cache_params.cep.entry_name);
255	free(entry->mp_cache_params.cep.entry_name);
256	free(entry->mp_cache_entries);
257	free(entry);
258	TRACE_OUT(destroy_configuration_entry);
259}
260
261int
262add_configuration_entry(struct configuration *config,
263	struct configuration_entry *entry)
264{
265	TRACE_IN(add_configuration_entry);
266	assert(entry != NULL);
267	assert(entry->name != NULL);
268	if (configuration_find_entry(config, entry->name) != NULL) {
269		TRACE_OUT(add_configuration_entry);
270		return (-1);
271	}
272
273	if (config->entries_size == config->entries_capacity) {
274		struct configuration_entry **new_entries;
275
276		config->entries_capacity *= 2;
277		new_entries = calloc(1,
278			sizeof(*new_entries) *
279			config->entries_capacity);
280		assert(new_entries != NULL);
281		memcpy(new_entries, config->entries,
282			sizeof(struct configuration_entry *) *
283		        config->entries_size);
284
285		free(config->entries);
286		config->entries = new_entries;
287	}
288
289	config->entries[config->entries_size++] = entry;
290	qsort(config->entries, config->entries_size,
291		sizeof(struct configuration_entry *),
292		configuration_entry_sort_cmp);
293
294	TRACE_OUT(add_configuration_entry);
295	return (0);
296}
297
298size_t
299configuration_get_entries_size(struct configuration *config)
300{
301	TRACE_IN(configuration_get_entries_size);
302	assert(config != NULL);
303	TRACE_OUT(configuration_get_entries_size);
304	return (config->entries_size);
305}
306
307struct configuration_entry *
308configuration_get_entry(struct configuration *config, size_t index)
309{
310	TRACE_IN(configuration_get_entry);
311	assert(config != NULL);
312	assert(index < config->entries_size);
313	TRACE_OUT(configuration_get_entry);
314	return (config->entries[index]);
315}
316
317struct configuration_entry *
318configuration_find_entry(struct configuration *config,
319	const char *name)
320{
321	struct configuration_entry	**retval;
322
323	TRACE_IN(configuration_find_entry);
324
325	retval = bsearch(name, config->entries, config->entries_size,
326		sizeof(struct configuration_entry *), configuration_entry_cmp);
327	TRACE_OUT(configuration_find_entry);
328
329	return ((retval != NULL) ? *retval : NULL);
330}
331
332/*
333 * All multipart cache entries are stored in the configuration_entry in the
334 * sorted array (sorted by names). The 3 functions below manage this array.
335 */
336
337int
338configuration_entry_add_mp_cache_entry(struct configuration_entry *config_entry,
339	cache_entry c_entry)
340{
341	cache_entry *new_mp_entries, *old_mp_entries;
342
343	TRACE_IN(configuration_entry_add_mp_cache_entry);
344	++config_entry->mp_cache_entries_size;
345	new_mp_entries = malloc(sizeof(*new_mp_entries) *
346		config_entry->mp_cache_entries_size);
347	assert(new_mp_entries != NULL);
348	new_mp_entries[0] = c_entry;
349
350	if (config_entry->mp_cache_entries_size - 1 > 0) {
351		memcpy(new_mp_entries + 1,
352		    config_entry->mp_cache_entries,
353		    (config_entry->mp_cache_entries_size - 1) *
354		    sizeof(cache_entry));
355	}
356
357	old_mp_entries = config_entry->mp_cache_entries;
358	config_entry->mp_cache_entries = new_mp_entries;
359	free(old_mp_entries);
360
361	qsort(config_entry->mp_cache_entries,
362		config_entry->mp_cache_entries_size,
363		sizeof(cache_entry),
364		configuration_entry_cache_mp_sort_cmp);
365
366	TRACE_OUT(configuration_entry_add_mp_cache_entry);
367	return (0);
368}
369
370cache_entry
371configuration_entry_find_mp_cache_entry(
372	struct configuration_entry *config_entry, const char *mp_name)
373{
374	cache_entry *result;
375
376	TRACE_IN(configuration_entry_find_mp_cache_entry);
377	result = bsearch(mp_name, config_entry->mp_cache_entries,
378		config_entry->mp_cache_entries_size,
379		sizeof(cache_entry), configuration_entry_cache_mp_cmp);
380
381	if (result == NULL) {
382		TRACE_OUT(configuration_entry_find_mp_cache_entry);
383		return (NULL);
384	} else {
385		TRACE_OUT(configuration_entry_find_mp_cache_entry);
386		return (*result);
387	}
388}
389
390/*
391 * Searches for all multipart entries with names starting with mp_name.
392 * Needed for cache flushing.
393 */
394int
395configuration_entry_find_mp_cache_entries(
396	struct configuration_entry *config_entry, const char *mp_name,
397	cache_entry **start, cache_entry **finish)
398{
399	cache_entry *result;
400
401	TRACE_IN(configuration_entry_find_mp_cache_entries);
402	result = bsearch(mp_name, config_entry->mp_cache_entries,
403		config_entry->mp_cache_entries_size,
404		sizeof(cache_entry), configuration_entry_cache_mp_part_cmp);
405
406	if (result == NULL) {
407		TRACE_OUT(configuration_entry_find_mp_cache_entries);
408		return (-1);
409	}
410
411	*start = result;
412	*finish = result + 1;
413
414	while (*start != config_entry->mp_cache_entries) {
415	    if (configuration_entry_cache_mp_part_cmp(mp_name, *start - 1) == 0)
416		*start = *start - 1;
417	    else
418		break;
419	}
420
421	while (*finish != config_entry->mp_cache_entries +
422		config_entry->mp_cache_entries_size) {
423
424	    if (configuration_entry_cache_mp_part_cmp(
425		mp_name, *finish) == 0)
426	    	*finish = *finish + 1;
427	    else
428		break;
429	}
430
431	TRACE_OUT(configuration_entry_find_mp_cache_entries);
432	return (0);
433}
434
435/*
436 * Configuration entry uses rwlock to handle access to its fields.
437 */
438void
439configuration_lock_rdlock(struct configuration *config)
440{
441    TRACE_IN(configuration_lock_rdlock);
442    pthread_rwlock_rdlock(&config->rwlock);
443    TRACE_OUT(configuration_lock_rdlock);
444}
445
446void
447configuration_lock_wrlock(struct configuration *config)
448{
449    TRACE_IN(configuration_lock_wrlock);
450    pthread_rwlock_wrlock(&config->rwlock);
451    TRACE_OUT(configuration_lock_wrlock);
452}
453
454void
455configuration_unlock(struct configuration *config)
456{
457    TRACE_IN(configuration_unlock);
458    pthread_rwlock_unlock(&config->rwlock);
459    TRACE_OUT(configuration_unlock);
460}
461
462/*
463 * Configuration entry uses 3 mutexes to handle cache operations. They are
464 * acquired by configuration_lock_entry and configuration_unlock_entry
465 * functions.
466 */
467void
468configuration_lock_entry(struct configuration_entry *entry,
469	enum config_entry_lock_type lock_type)
470{
471	TRACE_IN(configuration_lock_entry);
472	assert(entry != NULL);
473
474	switch (lock_type) {
475	case CELT_POSITIVE:
476		pthread_mutex_lock(&entry->positive_cache_lock);
477		break;
478	case CELT_NEGATIVE:
479		pthread_mutex_lock(&entry->negative_cache_lock);
480		break;
481	case CELT_MULTIPART:
482		pthread_mutex_lock(&entry->mp_cache_lock);
483		break;
484	default:
485		/* should be unreachable */
486		break;
487	}
488	TRACE_OUT(configuration_lock_entry);
489}
490
491void
492configuration_unlock_entry(struct configuration_entry *entry,
493	enum config_entry_lock_type lock_type)
494{
495	TRACE_IN(configuration_unlock_entry);
496	assert(entry != NULL);
497
498	switch (lock_type) {
499	case CELT_POSITIVE:
500		pthread_mutex_unlock(&entry->positive_cache_lock);
501		break;
502	case CELT_NEGATIVE:
503		pthread_mutex_unlock(&entry->negative_cache_lock);
504		break;
505	case CELT_MULTIPART:
506		pthread_mutex_unlock(&entry->mp_cache_lock);
507		break;
508	default:
509		/* should be unreachable */
510		break;
511	}
512	TRACE_OUT(configuration_unlock_entry);
513}
514
515struct configuration *
516init_configuration(void)
517{
518	struct configuration	*retval;
519
520	TRACE_IN(init_configuration);
521	retval = calloc(1, sizeof(*retval));
522	assert(retval != NULL);
523
524	retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
525	retval->entries = calloc(1,
526		sizeof(*retval->entries) *
527		retval->entries_capacity);
528	assert(retval->entries != NULL);
529
530	pthread_rwlock_init(&retval->rwlock, NULL);
531
532	TRACE_OUT(init_configuration);
533	return (retval);
534}
535
536void
537fill_configuration_defaults(struct configuration *config)
538{
539	size_t	len, i;
540
541	TRACE_IN(fill_configuration_defaults);
542	assert(config != NULL);
543
544	if (config->socket_path != NULL)
545		free(config->socket_path);
546
547	len = strlen(DEFAULT_SOCKET_PATH);
548	config->socket_path = calloc(1, len + 1);
549	assert(config->socket_path != NULL);
550	memcpy(config->socket_path, DEFAULT_SOCKET_PATH, len);
551
552	len = strlen(DEFAULT_PIDFILE_PATH);
553	config->pidfile_path = calloc(1, len + 1);
554	assert(config->pidfile_path != NULL);
555	memcpy(config->pidfile_path, DEFAULT_PIDFILE_PATH, len);
556
557	config->socket_mode =  S_IFSOCK | S_IRUSR | S_IWUSR |
558		S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
559	config->force_unlink = 1;
560
561	config->query_timeout = DEFAULT_QUERY_TIMEOUT;
562	config->threads_num = DEFAULT_THREADS_NUM;
563
564	for (i = 0; i < config->entries_size; ++i)
565		destroy_configuration_entry(config->entries[i]);
566	config->entries_size = 0;
567
568	TRACE_OUT(fill_configuration_defaults);
569}
570
571void
572destroy_configuration(struct configuration *config)
573{
574	unsigned int i;
575
576	TRACE_IN(destroy_configuration);
577	assert(config != NULL);
578	free(config->pidfile_path);
579	free(config->socket_path);
580
581	for (i = 0; i < config->entries_size; ++i)
582		destroy_configuration_entry(config->entries[i]);
583	free(config->entries);
584
585	pthread_rwlock_destroy(&config->rwlock);
586	free(config);
587	TRACE_OUT(destroy_configuration);
588}
589