/*- * 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); }