1/*-
2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29#include <sys/stat.h>
30#include <sys/time.h>
31
32#include <assert.h>
33#include <math.h>
34#include <nsswitch.h>
35#include <pthread.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39
40#include "config.h"
41#include "debug.h"
42#include "log.h"
43
44/*
45 * Default entries, which always exist in the configuration
46 */
47const char *c_default_entries[6] = {
48	NSDB_PASSWD,
49	NSDB_GROUP,
50	NSDB_HOSTS,
51	NSDB_SERVICES,
52	NSDB_PROTOCOLS,
53	NSDB_RPC
54	};
55
56static int configuration_entry_cmp(const void *, const void *);
57static int configuration_entry_sort_cmp(const void *, const void *);
58static int configuration_entry_cache_mp_sort_cmp(const void *, const void *);
59static int configuration_entry_cache_mp_cmp(const void *, const void *);
60static int configuration_entry_cache_mp_part_cmp(const void *, const void *);
61static struct configuration_entry *create_configuration_entry(const char *,
62	struct timeval const *, struct timeval const *,
63	struct common_cache_entry_params const *,
64	struct common_cache_entry_params const *,
65	struct mp_cache_entry_params const *);
66
67static int
68configuration_entry_sort_cmp(const void *e1, const void *e2)
69{
70	return (strcmp((*((struct configuration_entry **)e1))->name,
71		(*((struct configuration_entry **)e2))->name
72		));
73}
74
75static int
76configuration_entry_cmp(const void *e1, const void *e2)
77{
78	return (strcmp((const char *)e1,
79		(*((struct configuration_entry **)e2))->name
80		));
81}
82
83static int
84configuration_entry_cache_mp_sort_cmp(const void *e1, const void *e2)
85{
86	return (strcmp((*((cache_entry *)e1))->params->entry_name,
87		(*((cache_entry *)e2))->params->entry_name
88		));
89}
90
91static int
92configuration_entry_cache_mp_cmp(const void *e1, const void *e2)
93{
94	return (strcmp((const char *)e1,
95		(*((cache_entry *)e2))->params->entry_name
96		));
97}
98
99static int
100configuration_entry_cache_mp_part_cmp(const void *e1, const void *e2)
101{
102	return (strncmp((const char *)e1,
103		(*((cache_entry *)e2))->params->entry_name,
104		strlen((const char *)e1)
105		));
106}
107
108static struct configuration_entry *
109create_configuration_entry(const char *name,
110	struct timeval const *common_timeout,
111	struct timeval const *mp_timeout,
112	struct common_cache_entry_params const *positive_params,
113	struct common_cache_entry_params const *negative_params,
114	struct mp_cache_entry_params const *mp_params)
115{
116	struct configuration_entry *retval;
117	size_t	size;
118	int res;
119
120	TRACE_IN(create_configuration_entry);
121	assert(name != NULL);
122	assert(positive_params != NULL);
123	assert(negative_params != NULL);
124	assert(mp_params != NULL);
125
126	retval = calloc(1,
127		sizeof(*retval));
128	assert(retval != NULL);
129
130	res = pthread_mutex_init(&retval->positive_cache_lock, NULL);
131	if (res != 0) {
132		free(retval);
133		LOG_ERR_2("create_configuration_entry",
134			"can't create positive cache lock");
135		TRACE_OUT(create_configuration_entry);
136		return (NULL);
137	}
138
139	res = pthread_mutex_init(&retval->negative_cache_lock, NULL);
140	if (res != 0) {
141		pthread_mutex_destroy(&retval->positive_cache_lock);
142		free(retval);
143		LOG_ERR_2("create_configuration_entry",
144			"can't create negative cache lock");
145		TRACE_OUT(create_configuration_entry);
146		return (NULL);
147	}
148
149	res = pthread_mutex_init(&retval->mp_cache_lock, NULL);
150	if (res != 0) {
151		pthread_mutex_destroy(&retval->positive_cache_lock);
152		pthread_mutex_destroy(&retval->negative_cache_lock);
153		free(retval);
154		LOG_ERR_2("create_configuration_entry",
155			"can't create negative cache lock");
156		TRACE_OUT(create_configuration_entry);
157		return (NULL);
158	}
159
160	memcpy(&retval->positive_cache_params, positive_params,
161		sizeof(struct common_cache_entry_params));
162	memcpy(&retval->negative_cache_params, negative_params,
163		sizeof(struct common_cache_entry_params));
164	memcpy(&retval->mp_cache_params, mp_params,
165		sizeof(struct mp_cache_entry_params));
166
167	size = strlen(name);
168	retval->name = calloc(1, size + 1);
169	assert(retval->name != NULL);
170	memcpy(retval->name, name, size);
171
172	memcpy(&retval->common_query_timeout, common_timeout,
173		sizeof(struct timeval));
174	memcpy(&retval->mp_query_timeout, mp_timeout,
175		sizeof(struct timeval));
176
177	asprintf(&retval->positive_cache_params.cep.entry_name, "%s+", name);
178	assert(retval->positive_cache_params.cep.entry_name != NULL);
179
180	asprintf(&retval->negative_cache_params.cep.entry_name, "%s-", name);
181	assert(retval->negative_cache_params.cep.entry_name != NULL);
182
183	asprintf(&retval->mp_cache_params.cep.entry_name, "%s*", name);
184	assert(retval->mp_cache_params.cep.entry_name != NULL);
185
186	TRACE_OUT(create_configuration_entry);
187	return (retval);
188}
189
190/*
191 * Creates configuration entry and fills it with default values
192 */
193struct configuration_entry *
194create_def_configuration_entry(const char *name)
195{
196	struct common_cache_entry_params positive_params, negative_params;
197	struct mp_cache_entry_params mp_params;
198	struct timeval default_common_timeout, default_mp_timeout;
199
200	struct configuration_entry *res = NULL;
201
202	TRACE_IN(create_def_configuration_entry);
203	memset(&positive_params, 0,
204		sizeof(struct common_cache_entry_params));
205	positive_params.cep.entry_type = CET_COMMON;
206	positive_params.cache_entries_size = DEFAULT_CACHE_HT_SIZE;
207	positive_params.max_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE;
208	positive_params.satisf_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE / 2;
209	positive_params.max_lifetime.tv_sec = DEFAULT_POSITIVE_LIFETIME;
210	positive_params.confidence_threshold = DEFAULT_POSITIVE_CONF_THRESH;
211	positive_params.policy = CPT_LRU;
212
213	memcpy(&negative_params, &positive_params,
214		sizeof(struct common_cache_entry_params));
215	negative_params.max_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE;
216	negative_params.satisf_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE / 2;
217	negative_params.max_lifetime.tv_sec = DEFAULT_NEGATIVE_LIFETIME;
218	negative_params.confidence_threshold = DEFAULT_NEGATIVE_CONF_THRESH;
219	negative_params.policy = CPT_FIFO;
220
221	memset(&default_common_timeout, 0, sizeof(struct timeval));
222	default_common_timeout.tv_sec = DEFAULT_COMMON_ENTRY_TIMEOUT;
223
224	memset(&default_mp_timeout, 0, sizeof(struct timeval));
225	default_mp_timeout.tv_sec = DEFAULT_MP_ENTRY_TIMEOUT;
226
227	memset(&mp_params, 0,
228		sizeof(struct mp_cache_entry_params));
229	mp_params.cep.entry_type = CET_MULTIPART;
230	mp_params.max_elemsize = DEFAULT_MULTIPART_ELEMENTS_SIZE;
231	mp_params.max_sessions = DEFAULT_MULITPART_SESSIONS_SIZE;
232	mp_params.max_lifetime.tv_sec = DEFAULT_MULITPART_LIFETIME;
233
234	res = create_configuration_entry(name, &default_common_timeout,
235		&default_mp_timeout, &positive_params, &negative_params,
236		&mp_params);
237
238	TRACE_OUT(create_def_configuration_entry);
239	return (res);
240}
241
242void
243destroy_configuration_entry(struct configuration_entry *entry)
244{
245	TRACE_IN(destroy_configuration_entry);
246	assert(entry != NULL);
247	pthread_mutex_destroy(&entry->positive_cache_lock);
248	pthread_mutex_destroy(&entry->negative_cache_lock);
249	pthread_mutex_destroy(&entry->mp_cache_lock);
250	free(entry->name);
251	free(entry->positive_cache_params.cep.entry_name);
252	free(entry->negative_cache_params.cep.entry_name);
253	free(entry->mp_cache_params.cep.entry_name);
254	free(entry->mp_cache_entries);
255	free(entry);
256	TRACE_OUT(destroy_configuration_entry);
257}
258
259int
260add_configuration_entry(struct configuration *config,
261	struct configuration_entry *entry)
262{
263	TRACE_IN(add_configuration_entry);
264	assert(entry != NULL);
265	assert(entry->name != NULL);
266	if (configuration_find_entry(config, entry->name) != NULL) {
267		TRACE_OUT(add_configuration_entry);
268		return (-1);
269	}
270
271	if (config->entries_size == config->entries_capacity) {
272		struct configuration_entry **new_entries;
273
274		config->entries_capacity *= 2;
275		new_entries = calloc(config->entries_capacity,
276			sizeof(*new_entries));
277		assert(new_entries != NULL);
278		memcpy(new_entries, config->entries,
279			sizeof(struct configuration_entry *) *
280		        config->entries_size);
281
282		free(config->entries);
283		config->entries = new_entries;
284	}
285
286	config->entries[config->entries_size++] = entry;
287	qsort(config->entries, config->entries_size,
288		sizeof(struct configuration_entry *),
289		configuration_entry_sort_cmp);
290
291	TRACE_OUT(add_configuration_entry);
292	return (0);
293}
294
295size_t
296configuration_get_entries_size(struct configuration *config)
297{
298	TRACE_IN(configuration_get_entries_size);
299	assert(config != NULL);
300	TRACE_OUT(configuration_get_entries_size);
301	return (config->entries_size);
302}
303
304struct configuration_entry *
305configuration_get_entry(struct configuration *config, size_t index)
306{
307	TRACE_IN(configuration_get_entry);
308	assert(config != NULL);
309	assert(index < config->entries_size);
310	TRACE_OUT(configuration_get_entry);
311	return (config->entries[index]);
312}
313
314struct configuration_entry *
315configuration_find_entry(struct configuration *config,
316	const char *name)
317{
318	struct configuration_entry	**retval;
319
320	TRACE_IN(configuration_find_entry);
321
322	retval = bsearch(name, config->entries, config->entries_size,
323		sizeof(struct configuration_entry *), configuration_entry_cmp);
324	TRACE_OUT(configuration_find_entry);
325
326	return ((retval != NULL) ? *retval : NULL);
327}
328
329/*
330 * All multipart cache entries are stored in the configuration_entry in the
331 * sorted array (sorted by names). The 3 functions below manage this array.
332 */
333
334int
335configuration_entry_add_mp_cache_entry(struct configuration_entry *config_entry,
336	cache_entry c_entry)
337{
338	cache_entry *new_mp_entries, *old_mp_entries;
339
340	TRACE_IN(configuration_entry_add_mp_cache_entry);
341	++config_entry->mp_cache_entries_size;
342	new_mp_entries = malloc(sizeof(*new_mp_entries) *
343		config_entry->mp_cache_entries_size);
344	assert(new_mp_entries != NULL);
345	new_mp_entries[0] = c_entry;
346
347	if (config_entry->mp_cache_entries_size - 1 > 0) {
348		memcpy(new_mp_entries + 1,
349		    config_entry->mp_cache_entries,
350		    (config_entry->mp_cache_entries_size - 1) *
351		    sizeof(cache_entry));
352	}
353
354	old_mp_entries = config_entry->mp_cache_entries;
355	config_entry->mp_cache_entries = new_mp_entries;
356	free(old_mp_entries);
357
358	qsort(config_entry->mp_cache_entries,
359		config_entry->mp_cache_entries_size,
360		sizeof(cache_entry),
361		configuration_entry_cache_mp_sort_cmp);
362
363	TRACE_OUT(configuration_entry_add_mp_cache_entry);
364	return (0);
365}
366
367cache_entry
368configuration_entry_find_mp_cache_entry(
369	struct configuration_entry *config_entry, const char *mp_name)
370{
371	cache_entry *result;
372
373	TRACE_IN(configuration_entry_find_mp_cache_entry);
374	result = bsearch(mp_name, config_entry->mp_cache_entries,
375		config_entry->mp_cache_entries_size,
376		sizeof(cache_entry), configuration_entry_cache_mp_cmp);
377
378	if (result == NULL) {
379		TRACE_OUT(configuration_entry_find_mp_cache_entry);
380		return (NULL);
381	} else {
382		TRACE_OUT(configuration_entry_find_mp_cache_entry);
383		return (*result);
384	}
385}
386
387/*
388 * Searches for all multipart entries with names starting with mp_name.
389 * Needed for cache flushing.
390 */
391int
392configuration_entry_find_mp_cache_entries(
393	struct configuration_entry *config_entry, const char *mp_name,
394	cache_entry **start, cache_entry **finish)
395{
396	cache_entry *result;
397
398	TRACE_IN(configuration_entry_find_mp_cache_entries);
399	result = bsearch(mp_name, config_entry->mp_cache_entries,
400		config_entry->mp_cache_entries_size,
401		sizeof(cache_entry), configuration_entry_cache_mp_part_cmp);
402
403	if (result == NULL) {
404		TRACE_OUT(configuration_entry_find_mp_cache_entries);
405		return (-1);
406	}
407
408	*start = result;
409	*finish = result + 1;
410
411	while (*start != config_entry->mp_cache_entries) {
412	    if (configuration_entry_cache_mp_part_cmp(mp_name, *start - 1) == 0)
413		*start = *start - 1;
414	    else
415		break;
416	}
417
418	while (*finish != config_entry->mp_cache_entries +
419		config_entry->mp_cache_entries_size) {
420
421	    if (configuration_entry_cache_mp_part_cmp(
422		mp_name, *finish) == 0)
423	    	*finish = *finish + 1;
424	    else
425		break;
426	}
427
428	TRACE_OUT(configuration_entry_find_mp_cache_entries);
429	return (0);
430}
431
432/*
433 * Configuration entry uses rwlock to handle access to its fields.
434 */
435void
436configuration_lock_rdlock(struct configuration *config)
437{
438    TRACE_IN(configuration_lock_rdlock);
439    pthread_rwlock_rdlock(&config->rwlock);
440    TRACE_OUT(configuration_lock_rdlock);
441}
442
443void
444configuration_lock_wrlock(struct configuration *config)
445{
446    TRACE_IN(configuration_lock_wrlock);
447    pthread_rwlock_wrlock(&config->rwlock);
448    TRACE_OUT(configuration_lock_wrlock);
449}
450
451void
452configuration_unlock(struct configuration *config)
453{
454    TRACE_IN(configuration_unlock);
455    pthread_rwlock_unlock(&config->rwlock);
456    TRACE_OUT(configuration_unlock);
457}
458
459/*
460 * Configuration entry uses 3 mutexes to handle cache operations. They are
461 * acquired by configuration_lock_entry and configuration_unlock_entry
462 * functions.
463 */
464void
465configuration_lock_entry(struct configuration_entry *entry,
466	enum config_entry_lock_type lock_type)
467{
468	TRACE_IN(configuration_lock_entry);
469	assert(entry != NULL);
470
471	switch (lock_type) {
472	case CELT_POSITIVE:
473		pthread_mutex_lock(&entry->positive_cache_lock);
474		break;
475	case CELT_NEGATIVE:
476		pthread_mutex_lock(&entry->negative_cache_lock);
477		break;
478	case CELT_MULTIPART:
479		pthread_mutex_lock(&entry->mp_cache_lock);
480		break;
481	default:
482		/* should be unreachable */
483		break;
484	}
485	TRACE_OUT(configuration_lock_entry);
486}
487
488void
489configuration_unlock_entry(struct configuration_entry *entry,
490	enum config_entry_lock_type lock_type)
491{
492	TRACE_IN(configuration_unlock_entry);
493	assert(entry != NULL);
494
495	switch (lock_type) {
496	case CELT_POSITIVE:
497		pthread_mutex_unlock(&entry->positive_cache_lock);
498		break;
499	case CELT_NEGATIVE:
500		pthread_mutex_unlock(&entry->negative_cache_lock);
501		break;
502	case CELT_MULTIPART:
503		pthread_mutex_unlock(&entry->mp_cache_lock);
504		break;
505	default:
506		/* should be unreachable */
507		break;
508	}
509	TRACE_OUT(configuration_unlock_entry);
510}
511
512struct configuration *
513init_configuration(void)
514{
515	struct configuration	*retval;
516
517	TRACE_IN(init_configuration);
518	retval = calloc(1, sizeof(*retval));
519	assert(retval != NULL);
520
521	retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
522	retval->entries = calloc(retval->entries_capacity,
523		sizeof(*retval->entries));
524	assert(retval->entries != NULL);
525
526	pthread_rwlock_init(&retval->rwlock, NULL);
527
528	TRACE_OUT(init_configuration);
529	return (retval);
530}
531
532void
533fill_configuration_defaults(struct configuration *config)
534{
535	size_t	len, i;
536
537	TRACE_IN(fill_configuration_defaults);
538	assert(config != NULL);
539
540	if (config->socket_path != NULL)
541		free(config->socket_path);
542
543	len = strlen(DEFAULT_SOCKET_PATH);
544	config->socket_path = calloc(1, len + 1);
545	assert(config->socket_path != NULL);
546	memcpy(config->socket_path, DEFAULT_SOCKET_PATH, len);
547
548	len = strlen(DEFAULT_PIDFILE_PATH);
549	config->pidfile_path = calloc(1, len + 1);
550	assert(config->pidfile_path != NULL);
551	memcpy(config->pidfile_path, DEFAULT_PIDFILE_PATH, len);
552
553	config->socket_mode =  S_IFSOCK | S_IRUSR | S_IWUSR |
554		S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
555	config->force_unlink = 1;
556
557	config->query_timeout = DEFAULT_QUERY_TIMEOUT;
558	config->threads_num = DEFAULT_THREADS_NUM;
559
560	for (i = 0; i < config->entries_size; ++i)
561		destroy_configuration_entry(config->entries[i]);
562	config->entries_size = 0;
563
564	TRACE_OUT(fill_configuration_defaults);
565}
566
567void
568destroy_configuration(struct configuration *config)
569{
570	unsigned int i;
571
572	TRACE_IN(destroy_configuration);
573	assert(config != NULL);
574	free(config->pidfile_path);
575	free(config->socket_path);
576
577	for (i = 0; i < config->entries_size; ++i)
578		destroy_configuration_entry(config->entries[i]);
579	free(config->entries);
580
581	pthread_rwlock_destroy(&config->rwlock);
582	free(config);
583	TRACE_OUT(destroy_configuration);
584}
585