/*-
* Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
#include <sys/time.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "debug.h"
#include "log.h"
#include "parser.h"
static void enable_cache(struct configuration *,const char *, int);
static struct configuration_entry *find_create_entry(struct configuration *,
const char *);
static int get_number(const char *, int, int);
static enum cache_policy_t get_policy(const char *);
static int get_yesno(const char *);
static int check_cachename(const char *);
static void check_files(struct configuration *, const char *, int);
static void set_keep_hot_count(struct configuration *, const char *, int);
static void set_negative_policy(struct configuration *, const char *,
enum cache_policy_t);
static void set_negative_time_to_live(struct configuration *,
const char *, int);
static void set_positive_policy(struct configuration *, const char *,
enum cache_policy_t);
static void set_perform_actual_lookups(struct configuration *, const char *,
int);
static void set_positive_time_to_live(struct configuration *,
const char *, int);
static void set_suggested_size(struct configuration *, const char *,
int size);
static void set_threads_num(struct configuration *, int);
static int strbreak(char *, char **, int);
static int
strbreak(char *str, char **fields, int fields_size)
{
char *c = str;
int i, num;
TRACE_IN(strbreak);
num = 0;
for (i = 0;
((*fields =
strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL);
++i)
if ((*(*fields)) != '\0') {
++fields;
++num;
}
TRACE_OUT(strbreak);
return (num);
}
/*
* Tries to find the configuration entry with the specified name. If search
* fails, the new entry with the default parameters will be created.
*/
static struct configuration_entry *
find_create_entry(struct configuration *config,
const char *entry_name)
{
struct configuration_entry *entry = NULL;
int res;
TRACE_IN(find_create_entry);
entry = configuration_find_entry(config, entry_name);
if (entry == NULL) {
entry = create_def_configuration_entry(entry_name);
assert( entry != NULL);
res = add_configuration_entry(config, entry);
assert(res == 0);
}
TRACE_OUT(find_create_entry);
return (entry);
}
/*
* The vast majority of the functions below corresponds to the particular
* keywords in the configuration file.
*/
static void
enable_cache(struct configuration *config, const char *entry_name, int flag)
{
struct configuration_entry *entry;
TRACE_IN(enable_cache);
entry = find_create_entry(config, entry_name);
entry->enabled = flag;
TRACE_OUT(enable_cache);
}
static void
set_positive_time_to_live(struct configuration *config,
const char *entry_name, int ttl)
{
struct configuration_entry *entry;
struct timeval lifetime;
TRACE_IN(set_positive_time_to_live);
assert(ttl >= 0);
assert(entry_name != NULL);
memset(&lifetime, 0, sizeof(struct timeval));
lifetime.tv_sec = ttl;
entry = find_create_entry(config, entry_name);
memcpy(&entry->positive_cache_params.max_lifetime,
&lifetime, sizeof(struct timeval));
memcpy(&entry->mp_cache_params.max_lifetime,
&lifetime, sizeof(struct timeval));
TRACE_OUT(set_positive_time_to_live);
}
static void
set_negative_time_to_live(struct configuration *config,
const char *entry_name, int nttl)
{
struct configuration_entry *entry;
struct timeval lifetime;
TRACE_IN(set_negative_time_to_live);
assert(nttl > 0);
assert(entry_name != NULL);
memset(&lifetime, 0, sizeof(struct timeval));
lifetime.tv_sec = nttl;
entry = find_create_entry(config, entry_name);
assert(entry != NULL);
memcpy(&entry->negative_cache_params.max_lifetime,
&lifetime, sizeof(struct timeval));
TRACE_OUT(set_negative_time_to_live);
}
static void
set_positive_confidence_threshold(struct configuration *config,
const char *entry_name, int conf_thresh)
{
struct configuration_entry *entry;
TRACE_IN(set_positive_conf_thresh);
assert(conf_thresh > 0);
assert(entry_name != NULL);
entry = find_create_entry(config, entry_name);
assert(entry != NULL);
entry->positive_cache_params.confidence_threshold = conf_thresh;
TRACE_OUT(set_positive_conf_thresh);
}
static void
set_negative_confidence_threshold(struct configuration *config,
const char *entry_name, int conf_thresh)
{
struct configuration_entry *entry;
TRACE_IN(set_negative_conf_thresh);
assert(conf_thresh > 0);
assert(entry_name != NULL);
entry = find_create_entry(config, entry_name);
assert(entry != NULL);
entry->negative_cache_params.confidence_threshold = conf_thresh;
TRACE_OUT(set_negative_conf_thresh);
}
/*
* Hot count is actually the elements size limit.
*/
static void
set_keep_hot_count(struct configuration *config,
const char *entry_name, int count)
{
struct configuration_entry *entry;
TRACE_IN(set_keep_hot_count);
assert(count >= 0);
assert(entry_name != NULL);
entry = find_create_entry(config, entry_name);
assert(entry != NULL);
entry->positive_cache_params.max_elemsize = count;
entry = find_create_entry(config, entry_name);
assert(entry != NULL);
entry->negative_cache_params.max_elemsize = count;
TRACE_OUT(set_keep_hot_count);
}
static void
set_positive_policy(struct configuration *config,
const char *entry_name, enum cache_policy_t policy)
{
struct configuration_entry *entry;
TRACE_IN(set_positive_policy);
assert(entry_name != NULL);
entry = find_create_entry(config, entry_name);
assert(entry != NULL);
entry->positive_cache_params.policy = policy;
TRACE_OUT(set_positive_policy);
}
static void
set_negative_policy(struct configuration *config,
const char *entry_name, enum cache_policy_t policy)
{
struct configuration_entry *entry;
TRACE_IN(set_negative_policy);
assert(entry_name != NULL);
entry = find_create_entry(config, entry_name);
assert(entry != NULL);
entry->negative_cache_params.policy = policy;
TRACE_OUT(set_negative_policy);
}
static void
set_perform_actual_lookups(struct configuration *config,
const char *entry_name, int flag)
{
struct configuration_entry *entry;
TRACE_IN(set_perform_actual_lookups);
assert(entry_name != NULL);
entry = find_create_entry(config, entry_name);
assert(entry != NULL);
entry->perform_actual_lookups = flag;
TRACE_OUT(set_perform_actual_lookups);
}
static void
set_suggested_size(struct configuration *config,
const char *entry_name, int size)
{
struct configuration_entry *entry;
TRACE_IN(set_suggested_size);
assert(config != NULL);
assert(entry_name != NULL);
assert(size > 0);
entry = find_create_entry(config, entry_name);
assert(entry != NULL);
entry->positive_cache_params.cache_entries_size = size;
entry->negative_cache_params.cache_entries_size = size;
TRACE_OUT(set_suggested_size);
}
static void
check_files(struct configuration *config, const char *entry_name, int flag)
{
TRACE_IN(check_files);
assert(entry_name != NULL);
TRACE_OUT(check_files);
}
static int
get_yesno(const char *str)
{
if (strcmp(str, "yes") == 0)
return (1);
else if (strcmp(str, "no") == 0)
return (0);
else
return (-1);
}
static int
get_number(const char *str, int low, int max)
{
char *end = NULL;
int res = 0;
if (str[0] == '\0')
return (-1);
res = strtol(str, &end, 10);
if (*end != '\0')
return (-1);
else
if (((res >= low) || (low == -1)) &&
((res <= max) || (max == -1)))
return (res);
else
return (-2);
}
static enum cache_policy_t
get_policy(const char *str)
{
if (strcmp(str, "fifo") == 0)
return (CPT_FIFO);
else if (strcmp(str, "lru") == 0)
return (CPT_LRU);
else if (strcmp(str, "lfu") == 0)
return (CPT_LFU);
return (-1);
}
static int
check_cachename(const char *str)
{
assert(str != NULL);
return ((strlen(str) > 0) ? 0 : -1);
}
static void
set_threads_num(struct configuration *config, int value)
{
assert(config != NULL);
config->threads_num = value;
}
/*
* The main configuration routine. Its implementation is hugely inspired by the
* the same routine implementation in Solaris NSCD.
*/
int
parse_config_file(struct configuration *config,
const char *fname, char const **error_str, int *error_line)
{
FILE *fin;
char buffer[255];
char *fields[128];
int field_count, line_num, value;
int res;
int invalid_value;
TRACE_IN(parse_config_file);
assert(config != NULL);
assert(fname != NULL);
fin = fopen(fname, "r");
if (fin == NULL) {
TRACE_OUT(parse_config_file);
return (-1);
}
res = 0;
line_num = 0;
invalid_value = 0;
memset(buffer, 0, sizeof(buffer));
while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) {
field_count = strbreak(buffer, fields, sizeof(fields));
++line_num;
if (field_count == 0)
continue;
switch (fields[0][0]) {
case '#':
case '\0':
continue;
case 'e':
if ((field_count == 3) &&
(strcmp(fields[0], "enable-cache") == 0) &&
(check_cachename(fields[1]) == 0) &&
((value = get_yesno(fields[2])) != -1)) {
enable_cache(config, fields[1], value);
continue;
}
break;
case 'd':
if ((field_count == 2) &&
(strcmp(fields[0], "debug-level") == 0) &&
((value = get_number(fields[1], 0, 10)) != -1)) {
continue;
}
break;
case 'p':
if ((field_count == 3) &&
(strcmp(fields[0], "positive-time-to-live") == 0) &&
(check_cachename(fields[1]) == 0) &&
((value = get_number(fields[2], 0, -1)) != -1)) {
if (value <= 0) {
invalid_value = 1;
break;
}
set_positive_time_to_live(config,
fields[1], value);
continue;
} else if ((field_count == 3) &&
(strcmp(fields[0], "positive-confidence-threshold") == 0) &&
((value = get_number(fields[2], 1, -1)) != -1)) {
if (value <= 0) {
invalid_value = 1;
break;
}
set_positive_confidence_threshold(config,
fields[1], value);
continue;
} else if ((field_count == 3) &&
(strcmp(fields[0], "positive-policy") == 0) &&
(check_cachename(fields[1]) == 0) &&
((value = get_policy(fields[2])) != -1)) {
set_positive_policy(config, fields[1], value);
continue;
} else if ((field_count == 3) &&
(strcmp(fields[0], "perform-actual-lookups") == 0) &&
(check_cachename(fields[1]) == 0) &&
((value = get_yesno(fields[2])) != -1)) {
set_perform_actual_lookups(config, fields[1],
value);
continue;
}
break;
case 'n':
if ((field_count == 3) &&
(strcmp(fields[0], "negative-time-to-live") == 0) &&
(check_cachename(fields[1]) == 0) &&
((value = get_number(fields[2], 0, -1)) != -1)) {
if (value <= 0) {
invalid_value = 1;
break;
}
set_negative_time_to_live(config,
fields[1], value);
continue;
} else if ((field_count == 3) &&
(strcmp(fields[0], "negative-confidence-threshold") == 0) &&
((value = get_number(fields[2], 1, -1)) != -1)) {
if (value <= 0) {
invalid_value = 1;
break;
}
set_negative_confidence_threshold(config,
fields[1], value);
continue;
} else if ((field_count == 3) &&
(strcmp(fields[0], "negative-policy") == 0) &&
(check_cachename(fields[1]) == 0) &&
((value = get_policy(fields[2])) != -1)) {
set_negative_policy(config,
fields[1], value);
continue;
}
break;
case 's':
if ((field_count == 3) &&
(strcmp(fields[0], "suggested-size") == 0) &&
(check_cachename(fields[1]) == 0) &&
((value = get_number(fields[2], 1, -1)) != -1)) {
if (value <= 0) {
invalid_value = 1;
break;
}
set_suggested_size(config, fields[1], value);
continue;
}
break;
case 't':
if ((field_count == 2) &&
(strcmp(fields[0], "threads") == 0) &&
((value = get_number(fields[1], 1, -1)) != -1)) {
set_threads_num(config, value);
continue;
}
break;
case 'k':
if ((field_count == 3) &&
(strcmp(fields[0], "keep-hot-count") == 0) &&
(check_cachename(fields[1]) == 0) &&
((value = get_number(fields[2], 0, -1)) != -1)) {
if (value < 0) {
invalid_value = 1;
break;
}
set_keep_hot_count(config,
fields[1], value);
continue;
}
break;
case 'c':
if ((field_count == 3) &&
(strcmp(fields[0], "check-files") == 0) &&
(check_cachename(fields[1]) == 0) &&
((value = get_yesno(fields[2])) != -1)) {
check_files(config,
fields[1], value);
continue;
}
break;
default:
break;
}
if (invalid_value != 0) {
LOG_ERR_2("Invalid value for parameter",
"error in file %s on line %d",
fname, line_num);
*error_str = "invalid value";
} else {
LOG_ERR_2("config file parser", "error in file "
"%s on line %d", fname, line_num);
*error_str = "syntax error";
}
*error_line = line_num;
res = -1;
}
fclose(fin);
TRACE_OUT(parse_config_file);
return (res);
}