/*
Copyright (C) 2000 - 2006 Christian Kreibich <christian@whoop.org>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies of the Software and its documentation and acknowledgment shall be
given in the documentation and software packages that this Software was
used.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <errno.h>
#include <libnd_debug.h>
#include <libnd_prefs.h>
#include <libnd_macros.h>
#include <libnd_misc.h>
#define LND_CONFIG_FILE "preferences"
typedef struct nd_prefs_user_data
{
FILE *f;
GHashTable *table;
char *domain;
} LND_PrefsUserData;
static LND_PrefsEntry prefs_entries_netdude[] = {
{ "tcpdump_path", LND_PREFS_STR, LND_UNUSED, LND_UNUSED, TCPDUMP },
{ "tcpdump_resolve", LND_PREFS_INT, FALSE, LND_UNUSED, LND_UNUSED },
{ "tcpdump_print_domains", LND_PREFS_INT, FALSE, LND_UNUSED, LND_UNUSED },
{ "tcpdump_quick", LND_PREFS_INT, FALSE, LND_UNUSED, LND_UNUSED },
{ "tcpdump_print_link", LND_PREFS_INT, FALSE, LND_UNUSED, LND_UNUSED },
{ "tcpdump_print_timestamp", LND_PREFS_INT, FALSE, LND_UNUSED, LND_UNUSED },
{ "workdir", LND_PREFS_STR, LND_UNUSED, LND_UNUSED, "/tmp" },
{ "num_mem_packets", LND_PREFS_INT, 500, LND_UNUSED, LND_UNUSED },
{ "num_recycled_packets", LND_PREFS_INT, 1000, LND_UNUSED, LND_UNUSED },
};
static GList *global_domains = NULL;
static mode_t mode_755 = (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
S_IXGRP | S_IROTH | S_IXOTH);
static gboolean prefs_assert_dirs(void);
/* static void prefs_table_free(GHashTable *table); */
/* static void prefs_table_clear(GHashTable *table); */
static GList *prefs_read_config_file(GList *domains);
static gboolean prefs_write_config_file(GList *domains);
static LND_PrefsDomain *prefs_domain_new(GList *domains,
const char *dom_name,
LND_PrefsEntry *entries,
int num_entries);
static LND_PrefsDomain *prefs_domain_find(GList *domains, const char *dom_name);
static GList *prefs_domain_add(GList *domains, LND_PrefsDomain *domain);
static void prefs_set_item_direct(GHashTable *table, const char *key,
LND_PrefsType type, const void *data);
static void prefs_set_item(GList *domains,
const char *dom_name, const char *key,
LND_PrefsType type, const void *data);
static void *prefs_get_item(GList *domains, const char *dom_name, const char *key);
static void prefs_del_item_direct(GHashTable *prefs_table, const char *key);
static void prefs_del_item(GList *domains, const char *dom_name, const char *key);
static LND_PrefsType prefs_get_item_type(GHashTable *table, const char *key);
const char *
libnd_prefs_get_netdude_dir(void)
{
static char cfg_file[MAXPATHLEN] = "\0";
if (cfg_file[0] != 0)
return cfg_file;
g_snprintf(cfg_file, MAXPATHLEN, "%s/.nd", getenv("HOME"));
return cfg_file;
}
const char *
libnd_prefs_get_config_file(void)
{
static char cfg_file[MAXPATHLEN] = "\0";
if (cfg_file[0] != 0)
return cfg_file;
g_snprintf(cfg_file, MAXPATHLEN, "%s/%s", libnd_prefs_get_netdude_dir(), LND_CONFIG_FILE);
return cfg_file;
}
const char *
libnd_prefs_get_plugin_dir_global(void)
{
static char dir[MAXPATHLEN] = "\0";
if (dir[0] != 0)
return dir;
g_snprintf(dir, MAXPATHLEN, "%s/%s.%s/plugins",
PACKAGE_DATA_DIR, VERSION_MAJOR, VERSION_MINOR);
return dir;
}
const char *
libnd_prefs_get_plugin_dir_user(void)
{
static char dir[MAXPATHLEN] = "\0";
if (dir[0] != 0)
return dir;
g_snprintf(dir, MAXPATHLEN, "%s/%s.%s/plugins",
libnd_prefs_get_netdude_dir(), VERSION_MAJOR, VERSION_MINOR);
return dir;
}
const char *
libnd_prefs_get_proto_dir_global(void)
{
static char dir[MAXPATHLEN] = "\0";
if (dir[0] != 0)
return dir;
g_snprintf(dir, MAXPATHLEN, "%s/%s.%s/protocols",
PACKAGE_DATA_DIR, VERSION_MAJOR, VERSION_MINOR);
return dir;
}
const char *
libnd_prefs_get_proto_dir_user(void)
{
static char dir[MAXPATHLEN] = "\0";
if (dir[0] != 0)
return dir;
g_snprintf(dir, MAXPATHLEN, "%s/%s.%s/protocols",
libnd_prefs_get_netdude_dir(), VERSION_MAJOR, VERSION_MINOR);
return dir;
}
/* currently unused ...
static gboolean
prefs_table_clear_cb(gpointer key, gpointer value, gpointer user_data)
{
char *key_str = (char *) key;
if (!key || !value)
return FALSE;
if (key_str[0] != '#')
g_free(value);
g_free(key);
return TRUE;
TOUCH(user_data);
}
static void
prefs_table_free(GHashTable *table)
{
if (!table)
return;
prefs_table_clear(table);
g_hash_table_destroy(table);
}
static void
prefs_table_clear(GHashTable *table)
{
if (!table)
return;
g_hash_table_foreach_remove(table, prefs_table_clear_cb, NULL);
}
*/
static LND_PrefsDomain *
prefs_domain_new(GList *domains,
const char *dom_name,
LND_PrefsEntry *entries,
int num_entries)
{
LND_PrefsDomain *domain;
int i;
if (!dom_name)
return NULL;
if ( (domain = prefs_domain_find(domains, dom_name)) == NULL)
{
domain = g_new0(LND_PrefsDomain, 1);
domain->table = g_hash_table_new(g_str_hash, g_str_equal);
}
g_free(domain->name);
domain->name = g_strdup(dom_name);
if (entries)
{
domain->entries = entries;
domain->num_entries = num_entries;
}
D(("Domain %s status: %i entries\n", domain->name, domain->num_entries));
for (i = 0; i < num_entries; i++)
{
switch (entries[i].type)
{
case LND_PREFS_INT:
prefs_set_item_direct(domain->table, entries[i].key,
LND_PREFS_INT, &entries[i].int_val);
break;
case LND_PREFS_STR:
prefs_set_item_direct(domain->table, entries[i].key,
LND_PREFS_STR, entries[i].str_val);
break;
case LND_PREFS_FLT:
prefs_set_item_direct(domain->table, entries[i].key,
LND_PREFS_FLT, &entries[i].flt_val);
break;
default:
D(("Unknown preferences data type %i\n", entries[i].type));
}
}
return domain;
}
static LND_PrefsDomain *
prefs_domain_find(GList *domains, const char *dom_name)
{
GList *l;
for (l = domains; l; l = g_list_next(l))
{
LND_PrefsDomain *domain = (LND_PrefsDomain *) l->data;
if (strcmp(domain->name, dom_name) == 0)
return domain;
}
return NULL;
}
static GList *
prefs_domain_add(GList *domains, LND_PrefsDomain *domain)
{
if (!domain)
return NULL;
if (prefs_domain_find(domains, domain->name))
{
D(("Domain %s alread found, not adding\n", domain->name));
return domains;
}
D(("Adding new domain %s to list, now %i domains\n",
domain->name, g_list_length(domains) + 1));
return g_list_prepend(domains, domain);
}
/* ----------------------- currently dead code ...
static void
prefs_domain_free(LND_PrefsDomain *domain)
{
g_free(domain->name);
prefs_table_free(domain->table);
g_list_free(domain->apply_callbacks);
g_free(domain);
}
static void
prefs_domains_free(GList *domains)
{
GList *l;
if (!domains)
return;
for (l = domains; l; l = g_list_next(l))
{
LND_PrefsDomain *domain = (LND_PrefsDomain *) l->data;
prefs_domain_free(domain);
l->data = NULL;
}
g_list_free(domains);
}
static LND_PrefsType
prefs_get_data_type_from_entries(const char *key, LND_PrefsEntry *entries, int num_entries)
{
int i;
for (i = 0; i < num_entries; i++)
{
if (strcmp(entries[i].key, key) == 0)
return entries[i].type;
}
return LND_PREFS_UNK;
}
*/
static GList *
prefs_read_config_file(GList *domains)
{
LND_PrefsDomain *domain;
int int_data;
float flt_data;
char str_data[4096];
char dom_name[512];
char *key;
const char *cfg_file = NULL;
FILE *f;
LND_PrefsType type;
if ( (cfg_file = libnd_prefs_get_config_file()) == NULL)
return NULL;
if ((f = fopen(cfg_file, "r")) == NULL)
return NULL;
for ( ; ; )
{
if (fscanf(f, "%512s %u", dom_name, (int*) &type) == EOF)
break;
/* Terminate string to be extra sure. */
dom_name[511] = '\0';
if ( (key = strchr(dom_name, '/')) == NULL)
{
D(("Domain/key error in key %s\n", dom_name));
continue;
}
*key = '\0';
key++;
domain = prefs_domain_find(domains, dom_name);
if (!domain)
{
D(("Domain %s not found when reading %s, allocating.\n",
dom_name, cfg_file));
domain = prefs_domain_new(domains, dom_name, NULL, 0);
domains = prefs_domain_add(domains, domain);
}
switch (type)
{
case LND_PREFS_INT:
if (fscanf(f, "%i\n", &int_data) != 1)
{
D(("Domain/key error in key %s/%s\n", dom_name, key));
continue;
}
prefs_set_item_direct(domain->table, key, LND_PREFS_INT, &int_data);
break;
case LND_PREFS_FLT:
if (fscanf(f, "%f\n", &flt_data) != 1)
{
D(("Domain/key error in key %s/%s\n", dom_name, key));
continue;
}
prefs_set_item_direct(domain->table, key, LND_PREFS_FLT, &flt_data);
break;
case LND_PREFS_STR:
if (fscanf(f, "%s4096\n", str_data) != 1)
{
D(("Domain/key error in key %s/%s\n", dom_name, key));
continue;
}
str_data[4095] = '\0';
/* Store only if it's not our "undefined" marker */
if (strcmp(str_data, "---"))
prefs_set_item_direct(domain->table, key, LND_PREFS_STR, str_data);
break;
default:
D(("Unknown preference entry type for item '%s'.\n", key));
}
D(("Added key %s to domain %s, size now %i\n",
key, domain->name, g_hash_table_size(domain->table)));
}
fclose(f);
return domains;
}
static void
prefs_write_config_entry(gpointer key_ptr, gpointer value, gpointer user_data)
{
char key[MAXPATHLEN];
char *key_str = (char *) key_ptr;
LND_PrefsUserData *data = (LND_PrefsUserData *) user_data;
LND_PrefsType type;
if (key_str[0] == '#')
return;
g_snprintf(key, MAXPATHLEN, "%s/%s", data->domain, key_str);
type = prefs_get_item_type(data->table, key_str);
/* D(("Writing config item '%s'\n", (char*) key)); */
switch (type)
{
case LND_PREFS_INT:
fprintf(data->f, "%-40s \t %i %i\n",
key, type, *((int *) value));
break;
case LND_PREFS_FLT:
fprintf(data->f, "%-40s \t %i %f\n",
key, type, *((float *) value));
break;
case LND_PREFS_STR:
{
char *s = (char *) value;
/* Use "unset" marker if undefined */
if (!s || *s == '\0')
s = "---";
fprintf(data->f, "%-40s \t %i %s\n",
key, type, s);
}
break;
case LND_PREFS_UNK:
break;
default:
D(("Unknown preference entry type %i.\n", type));
}
}
static gboolean
prefs_assert_dirs(void)
{
char dir[MAXPATHLEN];
if (!libnd_misc_exists(libnd_prefs_get_netdude_dir()))
{
if (mkdir(libnd_prefs_get_netdude_dir(), mode_755) < 0)
{
if (errno != EEXIST)
return FALSE;
}
g_snprintf(dir, MAXPATHLEN, "%s/%s.%s/plugins",
libnd_prefs_get_netdude_dir(), VERSION_MAJOR, VERSION_MINOR);
if (mkdir(dir, mode_755) < 0)
{
if (errno != EEXIST)
return FALSE;
}
g_snprintf(dir, MAXPATHLEN, "%s/%s.%s/protocols",
libnd_prefs_get_netdude_dir(), VERSION_MAJOR, VERSION_MINOR);
if (mkdir(dir, mode_755) < 0)
{
if (errno != EEXIST)
return FALSE;
}
}
return TRUE;
}
static gboolean
prefs_write_config_file(GList *domains)
{
GList *l;
LND_PrefsUserData data;
if (!domains)
return FALSE;
if (!prefs_assert_dirs())
return FALSE;
if ((data.f = fopen(libnd_prefs_get_config_file(), "w")) == NULL)
return FALSE;
for (l = domains; l; l = g_list_next(l))
{
LND_PrefsDomain *domain = (LND_PrefsDomain *) l->data;
D(("Saving domain %s\n", domain->name));
data.table = domain->table;
data.domain = domain->name;
g_hash_table_foreach(domain->table, prefs_write_config_entry, &data);
}
fclose(data.f);
return TRUE;
}
void
libnd_prefs_init(void)
{
}
void
libnd_prefs_load(void)
{
LND_PrefsDomain *domain;
D_ENTER;
domain = prefs_domain_new(global_domains, LND_DOM_NETDUDE,
prefs_entries_netdude,
sizeof (prefs_entries_netdude) / sizeof(LND_PrefsEntry));
global_domains = prefs_domain_add(global_domains, domain);
if (libnd_misc_exists(libnd_prefs_get_config_file()))
global_domains = prefs_read_config_file(global_domains);
/* Write the config file back out -- that ensures we store
* new config values that got added in the default tables
* on disk.
*/
prefs_write_config_file(global_domains);
libnd_prefs_apply();
D_RETURN;
}
int
libnd_prefs_save(void)
{
int result;
D_ENTER;
result = prefs_write_config_file(global_domains);
D_RETURN_(result);
}
void
libnd_prefs_apply(void)
{
GList *l, *l2;
libnd_prefs_get_int_item(LND_DOM_NETDUDE, "tcpdump_print_timestamp",
&libnet_tcpdump_print_timestamp);
for (l = global_domains; l; l = g_list_next(l))
{
LND_PrefsDomain *domain = (LND_PrefsDomain *) l->data;
for (l2 = domain->apply_callbacks; l2; l2 = g_list_next(l2))
{
LND_PrefsCallback callback = (LND_PrefsCallback) l2->data;
if (callback)
callback(domain, NULL);
}
}
}
LND_PrefsDomain *
libnd_prefs_add_domain(const char *dom_name,
LND_PrefsEntry *entries,
int num_entries)
{
LND_PrefsDomain *domain;
if ( (domain = prefs_domain_find(global_domains, dom_name)))
{
D(("Domain %s alread found, not adding\n", dom_name));
domain->entries = entries;
domain->num_entries = num_entries;
return domain;
}
domain = prefs_domain_new(global_domains, dom_name,
entries, num_entries);
global_domains = prefs_domain_add(global_domains, domain);
return domain;
}
LND_PrefsDomain *
libnd_prefs_get_domain(const char *domain)
{
return prefs_domain_find(global_domains, domain);
}
void
libnd_prefs_domain_add_apply_cb(LND_PrefsDomain *domain,
LND_PrefsCallback apply_cb)
{
if (!domain)
return;
domain->apply_callbacks = g_list_append(domain->apply_callbacks, apply_cb);
}
void
libnd_prefs_foreach_domain(LND_PrefsCallback callback, void *user_data)
{
GList *l;
if (!callback)
return;
for (l = global_domains; l; l = g_list_next(l))
{
LND_PrefsDomain *domain = (LND_PrefsDomain *) l->data;
callback(domain, user_data);
}
}
static void *
prefs_get_item(GList *domains, const char *dom_name, const char *key)
{
LND_PrefsDomain *domain;
if (!domains || !dom_name || !key || key[0] == '\0')
return NULL;
if ( (domain = prefs_domain_find(domains, dom_name)) == NULL)
return NULL;
D_ASSERT_PTR(domain->table);
if (!domain->table)
{
D(("No hash table in preferences domain! ARGH!\n"));
return NULL;
}
return g_hash_table_lookup(domain->table, key);
}
gboolean
libnd_prefs_get_str_item(const char *domain, const char *key, char **result)
{
void *data = prefs_get_item(global_domains, domain, key);
if (!data || !result)
return FALSE;
*result = (char *) data;
return TRUE;
}
gboolean
libnd_prefs_get_int_item(const char *domain, const char *key, int *result)
{
void *data = prefs_get_item(global_domains, domain, key);
if (!data || !result)
return FALSE;
*result = *((int *) data);
return TRUE;
}
gboolean
libnd_prefs_get_flt_item(const char *domain, const char *key, float *result)
{
void *data = prefs_get_item(global_domains, domain, key);
if (!data || !result)
return FALSE;
*result = *((float *) data);
return TRUE;
}
static LND_PrefsType
prefs_get_item_type(GHashTable *table, const char *key)
{
void *data;
char data_type_key[MAXPATHLEN];
D_ASSERT_PTR(table);
if (!table)
return LND_PREFS_UNK;
g_snprintf(data_type_key, MAXPATHLEN, "#%s", key);
data = g_hash_table_lookup(table, data_type_key);
if (!data)
{
D(("Returning type UNK for '%s' in %p\n", data_type_key, table));
return LND_PREFS_UNK;
}
return GPOINTER_TO_INT(data);
}
static void
prefs_del_item_direct(GHashTable *prefs_table, const char *key)
{
char data_type_key[MAXPATHLEN];
if (!prefs_table || !key)
return;
/* FIXME -- how do I properly clean up the key and value? */
g_hash_table_remove(prefs_table, key);
g_snprintf(data_type_key, MAXPATHLEN, "#%s", key);
g_hash_table_remove(prefs_table, data_type_key);
}
static void
prefs_del_item(GList *domains,
const char *dom_name, const char *key)
{
LND_PrefsDomain *domain;
if (!domains || !dom_name || !key || key[0] == '\0' || key[0] == '#')
return;
if ( (domain = prefs_domain_find(domains, dom_name)) == NULL)
return;
prefs_del_item_direct(domain->table, key);
}
static void
prefs_set_item_direct(GHashTable *table, const char *key,
LND_PrefsType type, const void *data)
{
char data_type_key[MAXPATHLEN];
void *data_copy, *data_old;
if (!table || !key || !data)
return;
switch (type)
{
case LND_PREFS_INT:
data_copy = g_new0(int, 1);
memcpy(data_copy, data, sizeof(int));
break;
case LND_PREFS_STR:
data_copy = g_strdup((char *)data);
break;
case LND_PREFS_FLT:
data_copy = g_new0(float, 1);
memcpy(data_copy, data, sizeof(float));
break;
default:
data_copy = NULL;
D(("Unknown preferences data type %i\n", type));
return;
}
if ( (data_old = g_hash_table_lookup(table, key)))
{
/* D(("Updating prefs item '%s'.\n", key)); */
g_hash_table_remove(table, key);
g_hash_table_insert(table, g_strdup(key), data_copy);
g_snprintf(data_type_key, MAXPATHLEN, "#%s", key);
g_hash_table_insert(table, g_strdup(data_type_key), GINT_TO_POINTER(type));
}
else
{
/* D(("Inserting new prefs item '%s' into %p.\n", key, table)); */
g_hash_table_insert(table, strdup(key), data_copy);
g_snprintf(data_type_key, MAXPATHLEN, "#%s", key);
g_hash_table_insert(table, g_strdup(data_type_key), GINT_TO_POINTER(type));
}
}
static void
prefs_set_item(GList *domains,
const char *dom_name, const char *key,
LND_PrefsType type, const void *data)
{
LND_PrefsDomain *domain;
if (!domains || !dom_name || !key || key[0] == '\0' || key[0] == '#')
return;
if ( (domain = prefs_domain_find(domains, dom_name)) == NULL)
return;
prefs_set_item_direct(domain->table, key, type, data);
}
void
libnd_prefs_set_str_item(const char *domain, const char *key, const char *data)
{
prefs_set_item(global_domains, domain, key, LND_PREFS_STR, data);
}
void
libnd_prefs_set_flt_item(const char *domain, const char *key, float data)
{
prefs_set_item(global_domains, domain, key, LND_PREFS_FLT, &data);
}
void
libnd_prefs_set_int_item(const char *domain, const char *key, int data)
{
prefs_set_item(global_domains, domain, key, LND_PREFS_INT, &data);
}
void
libnd_prefs_del_item(const char *domain, const char *key)
{
prefs_del_item(global_domains, domain, key);
}
syntax highlighted by Code2HTML, v. 0.9.1