summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stefw@collabora.co.uk>2011-06-08 21:21:54 +0200
committerStef Walter <stefw@collabora.co.uk>2011-06-08 21:21:54 +0200
commit21333019a5afceb5f07637fb50b784a4ecd9f9ff (patch)
tree41a8c5ea0a8e06ea7fb1411a3fd856d72b9596bb
parent7c1edab7e6c1c6939ecdeaefc5f006772298f9eb (diff)
Refactor configuration
* Move configuration loading into conf.c * Have user modules with same name merge/override modules in system.
-rw-r--r--p11-kit/Makefile.am2
-rw-r--r--p11-kit/conf.c429
-rw-r--r--p11-kit/conf.h24
-rw-r--r--p11-kit/hash.c117
-rw-r--r--p11-kit/hash.h10
-rw-r--r--p11-kit/modules.c366
-rw-r--r--p11-kit/private.h11
-rw-r--r--p11-kit/util.c17
-rw-r--r--tests/conf-test.c40
9 files changed, 610 insertions, 406 deletions
diff --git a/p11-kit/Makefile.am b/p11-kit/Makefile.am
index 99d7d1a..6f2fc19 100644
--- a/p11-kit/Makefile.am
+++ b/p11-kit/Makefile.am
@@ -10,10 +10,10 @@ inc_HEADERS = \
pkcs11.h
MODULE_SRCS = \
+ util.c util.h \
conf.c conf.h \
debug.c debug.h \
hash.c hash.h \
- util.c util.h \
modules.c \
proxy.c \
private.h \
diff --git a/p11-kit/conf.c b/p11-kit/conf.c
index 51fe579..8d4703e 100644
--- a/p11-kit/conf.c
+++ b/p11-kit/conf.c
@@ -40,6 +40,7 @@
#include "conf.h"
#define DEBUG_FLAG DEBUG_CONF
#include "debug.h"
+#include "private.h"
#include <sys/param.h>
#include <sys/stat.h>
@@ -49,6 +50,7 @@
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
+#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -56,23 +58,6 @@
#include <unistd.h>
static void
-errmsg (conf_error_func error_func, const char* msg, ...)
-{
- #define MAX_MSGLEN 1024
- char buf[MAX_MSGLEN];
- va_list ap;
-
- if (!error_func)
- return;
-
- va_start (ap, msg);
- vsnprintf (buf, MAX_MSGLEN, msg, ap);
- buf[MAX_MSGLEN - 1] = 0;
- error_func (buf);
- va_end (ap);
-}
-
-static void
strcln (char* data, char ch)
{
char* p;
@@ -112,22 +97,64 @@ strtrim (char* data)
return data;
}
+static int
+strequal (const char *one, const char *two)
+{
+ return strcmp (one, two) == 0;
+}
+
+static char*
+strconcat (const char *first, ...)
+{
+ size_t length = 0;
+ const char *arg;
+ char *result, *at;
+ va_list va;
+
+ va_start (va, first);
+
+ for (arg = first; arg; arg = va_arg (va, const char*))
+ length += strlen (arg);
+
+ va_end (va);
+
+ at = result = malloc (length + 1);
+ if (!result) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ va_start (va, first);
+
+ for (arg = first; arg; arg = va_arg (va, const char*)) {
+ length = strlen (arg);
+ memcpy (at, arg, length);
+ at += length;
+ }
+
+ va_end (va);
+
+ *at = 0;
+ return result;
+}
+
/* -----------------------------------------------------------------------------
* CONFIG PARSER
*/
static char*
-read_config_file (const char* filename, int flags,
- conf_error_func error_func)
+read_config_file (const char* filename, int flags)
{
char* config = NULL;
FILE* f = NULL;
+ int error = 0;
long len;
assert (filename);
f = fopen (filename, "r");
if (f == NULL) {
+ error = errno;
if ((flags & CONF_IGNORE_MISSING) &&
(errno == ENOENT || errno == ENOTDIR)) {
debug ("config file does not exist");
@@ -136,7 +163,8 @@ read_config_file (const char* filename, int flags,
errno = ENOMEM;
return config;
}
- errmsg (error_func, "couldn't open config file: %s", filename);
+ _p11_warning ("couldn't open config file: %s", filename);
+ errno = error;
return NULL;
}
@@ -144,19 +172,23 @@ read_config_file (const char* filename, int flags,
if (fseek (f, 0, SEEK_END) == -1 ||
(len = ftell (f)) == -1 ||
fseek (f, 0, SEEK_SET) == -1) {
- errmsg (error_func, "couldn't seek config file: %s", filename);
+ error = errno;
+ _p11_warning ("couldn't seek config file: %s", filename);
+ errno = error;
return NULL;
}
if ((config = (char*)malloc (len + 2)) == NULL) {
- errmsg (error_func, "out of memory");
+ _p11_warning ("out of memory");
errno = ENOMEM;
return NULL;
}
/* And read in one block */
if (fread (config, 1, len, f) != len) {
- errmsg (error_func, "couldn't read config file: %s", filename);
+ error = errno;
+ _p11_warning ("couldn't read config file: %s", filename);
+ errno = error;
return NULL;
}
@@ -172,28 +204,69 @@ read_config_file (const char* filename, int flags,
return config;
}
+int
+_p11_conf_merge_defaults (hash_t *ht, hash_t *defaults)
+{
+ hash_iter_t hi;
+ void *key;
+ void *value;
+
+ hash_iterate (defaults, &hi);
+ while (hash_next (&hi, &key, &value)) {
+ /* Only override if not set */
+ if (hash_get (ht, key))
+ continue;
+ key = strdup (key);
+ if (key == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ value = strdup (value);
+ if (value == NULL) {
+ free (key);
+ errno = ENOMEM;
+ return -1;
+ }
+ if (!hash_set (ht, key, value)) {
+ free (key);
+ free (value);
+ errno = ENOMEM;
+ return -1;
+ }
+ key = NULL;
+ value = NULL;
+ }
+
+ return 0;
+}
+
hash_t*
-conf_parse_file (const char* filename, int flags,
- conf_error_func error_func)
+_p11_conf_parse_file (const char* filename, int flags)
{
char *name;
char *value;
hash_t *ht = NULL;
- char *config;
+ char *data;
char *next;
char *end;
+ int error = 0;
assert (filename);
debug ("reading config file: %s", filename);
/* Adds an extra newline to end of file */
- config = read_config_file (filename, flags, error_func);
- if (!config)
+ data = read_config_file (filename, flags);
+ if (!data)
return NULL;
ht = hash_create (hash_string_hash, hash_string_equal, free, free);
- next = config;
+ if (ht == NULL) {
+ free (data);
+ errno = ENOMEM;
+ return NULL;
+ }
+ next = data;
/* Go through lines and process them */
while ((end = strchr (next, '\n')) != NULL) {
@@ -208,8 +281,8 @@ conf_parse_file (const char* filename, int flags,
/* Look for the break between name: value on the same line */
value = name + strcspn (name, ":");
if (!*value) {
- errmsg (error_func, "%s: invalid config line: %s", filename, name);
- errno = EINVAL;
+ _p11_warning ("%s: invalid config line: %s", filename, name);
+ error = EINVAL;
break;
}
@@ -222,13 +295,13 @@ conf_parse_file (const char* filename, int flags,
name = strdup (name);
if (!name) {
- errno = ENOMEM;
+ error = ENOMEM;
break;
}
value = strdup (value);
if (!value) {
free (name);
- errno = ENOMEM;
+ error = ENOMEM;
break;
}
@@ -237,17 +310,299 @@ conf_parse_file (const char* filename, int flags,
if (!hash_set (ht, name, value)) {
free (name);
free (value);
- errno = ENOMEM;
+ error = ENOMEM;
break;
}
}
- /* Unsuccessful? */
- if (end != NULL) {
+ free (data);
+
+ if (error != 0) {
hash_free (ht);
ht = NULL;
+ errno = error;
}
- free (config);
return ht;
}
+
+static char*
+expand_user_path (const char *path)
+{
+ const char *env;
+ struct passwd *pwd;
+ int error = 0;
+
+ if (path[0] == '~' && path[1] == '/') {
+ env = getenv ("HOME");
+ if (env && env[0]) {
+ return strconcat (env, path + 1, NULL);
+ } else {
+ pwd = getpwuid (getuid ());
+ if (!pwd) {
+ error = errno;
+ _p11_warning ("couldn't lookup home directory for user %d: %s",
+ getuid (), strerror (errno));
+ errno = error;
+ return NULL;
+ }
+ return strconcat (pwd->pw_dir, path + 1, NULL);
+ }
+ }
+
+ return strdup (path);
+}
+
+static int
+user_config_mode (hash_t *config, int defmode)
+{
+ const char *mode;
+
+ /* Whether we should use or override from user directory */
+ mode = hash_get (config, "user-config");
+ if (mode == NULL) {
+ return defmode;
+ } else if (strequal (mode, "none")) {
+ return CONF_USER_NONE;
+ } else if (strequal (mode, "merge")) {
+ return CONF_USER_MERGE;
+ } else if (strequal (mode, "only")) {
+ return CONF_USER_ONLY;
+ } else if (strequal (mode, "override")) {
+ return CONF_USER_ONLY;
+ } else {
+ _p11_warning ("invalid mode for 'user-config': %s", mode);
+ return CONF_USER_INVALID;
+ }
+}
+
+hash_t*
+_p11_conf_load_globals (const char *system_conf, const char *user_conf,
+ int *user_mode)
+{
+ hash_t *config = NULL;
+ hash_t *uconfig = NULL;
+ hash_t *result = NULL;
+ char *path = NULL;
+ int error = 0;
+ int mode;
+
+ /*
+ * This loads the system and user configs. This depends on the user-config
+ * value in both the system and user configs. A bit more complex than
+ * you might imagine, since user-config can be set to 'none' in the
+ * user configuration, essentially turning itself off.
+ */
+
+ /* Load the main configuration */
+ config = _p11_conf_parse_file (system_conf, CONF_IGNORE_MISSING);
+ if (!config)
+ goto finished;
+
+ /* Whether we should use or override from user directory */
+ mode = user_config_mode (config, CONF_USER_NONE);
+ if (mode == CONF_USER_INVALID) {
+ error = EINVAL;
+ goto finished;
+ }
+
+ if (mode != CONF_USER_NONE) {
+ path = expand_user_path (user_conf);
+ if (!path) {
+ error = errno;
+ goto finished;
+ }
+
+ /* Load up the user configuration */
+ uconfig = _p11_conf_parse_file (path, CONF_IGNORE_MISSING);
+ if (!uconfig) {
+ error = errno;
+ goto finished;
+ }
+
+ /* Figure out what the user mode is, defaulting to system mode if not set */
+ mode = user_config_mode (uconfig, mode);
+ if (mode == CONF_USER_INVALID) {
+ error = EINVAL;
+ goto finished;
+ }
+
+ /* If merging, then supplement user config with system values */
+ if (mode == CONF_USER_MERGE) {
+ if (_p11_conf_merge_defaults (uconfig, config) < 0) {
+ error = errno;
+ goto finished;
+ }
+ }
+
+ /* If user config valid at all, then replace system with what we have */
+ if (mode != CONF_USER_NONE) {
+ hash_free (config);
+ config = uconfig;
+ uconfig = NULL;
+ }
+ }
+
+ if (user_mode)
+ *user_mode = mode;
+
+ result = config;
+ config = NULL;
+
+finished:
+ free (path);
+ hash_free (config);
+ hash_free (uconfig);
+ errno = error;
+ return result;
+}
+
+static int
+load_config_from_file (const char *configfile, const char *name, hash_t *configs)
+{
+ hash_t *config;
+ hash_t *prev;
+ char *key;
+ int error = 0;
+
+ assert (configfile);
+
+ config = _p11_conf_parse_file (configfile, 0);
+ if (!config)
+ return -1;
+
+ prev = hash_get (configs, name);
+ if (prev == NULL) {
+ key = strdup (name);
+ if (key == NULL)
+ error = ENOMEM;
+ else if (!hash_set (configs, key, config))
+ error = errno;
+ else
+ config = NULL;
+ } else {
+ if (_p11_conf_merge_defaults (prev, config) < 0)
+ error = errno;
+ }
+
+ /* If still set */
+ hash_free (config);
+
+ if (error) {
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+load_configs_from_directory (const char *directory, hash_t *configs)
+{
+ struct dirent *dp;
+ struct stat st;
+ DIR *dir;
+ int error = 0;
+ int is_dir;
+ char *path;
+ int count = 0;
+
+ debug ("loading module configs in: %s", directory);
+
+ /* First we load all the modules */
+ dir = opendir (directory);
+ if (!dir) {
+ error = errno;
+ if (errno == ENOENT || errno == ENOTDIR)
+ return 0;
+ _p11_warning ("couldn't list directory: %s", directory);
+ errno = error;
+ return -1;
+ }
+
+ /* We're within a global mutex, so readdir is safe */
+ while ((dp = readdir(dir)) != NULL) {
+ path = strconcat (directory, "/", dp->d_name, NULL);
+ if (!path) {
+ error = ENOMEM;
+ break;
+ }
+
+ is_dir = 0;
+#ifdef HAVE_STRUCT_DIRENT_D_TYPE
+ if(dp->d_type != DT_UNKNOWN) {
+ is_dir = (dp->d_type == DT_DIR);
+ } else
+#endif
+ {
+ if (stat (path, &st) < 0) {
+ error = errno;
+ _p11_warning ("couldn't stat path: %s", path);
+ free (path);
+ break;
+ }
+ is_dir = S_ISDIR (st.st_mode);
+ }
+
+ if (!is_dir && load_config_from_file (path, dp->d_name, configs) < 0) {
+ error = errno;
+ free (path);
+ break;
+ }
+
+ free (path);
+ count ++;
+ }
+
+ closedir (dir);
+
+ if (error) {
+ errno = error;
+ return -1;
+ }
+
+ return count;
+}
+
+hash_t*
+_p11_conf_load_modules (int mode, const char *system_dir, const char *user_dir)
+{
+ hash_t *configs;
+ char *path;
+ int error = 0;
+
+ /* A hash table of name -> config */
+ configs = hash_create (hash_string_hash, hash_string_equal,
+ free, (hash_destroy_func)hash_free);
+
+ /* Load each user config first, if user config is allowed */
+ if (mode != CONF_USER_NONE) {
+ path = expand_user_path (user_dir);
+ if (!path)
+ error = errno;
+ else if (load_configs_from_directory (path, configs) < 0)
+ error = errno;
+ free (path);
+ if (error != 0) {
+ hash_free (configs);
+ errno = error;
+ return NULL;
+ }
+ }
+
+ /*
+ * Now unless user config is overriding, load system modules.
+ * Basically if a value for the same config name is not already
+ * loaded above (in the user configs) then they're loaded here.
+ */
+ if (mode != CONF_USER_ONLY) {
+ if (load_configs_from_directory (system_dir, configs) < 0) {
+ error = errno;
+ hash_free (configs);
+ errno = error;
+ return NULL;
+ }
+ }
+
+ return configs;
+}
diff --git a/p11-kit/conf.h b/p11-kit/conf.h
index 81e0fb8..dc48210 100644
--- a/p11-kit/conf.h
+++ b/p11-kit/conf.h
@@ -42,10 +42,28 @@ enum {
CONF_IGNORE_MISSING = 0x01,
};
+enum {
+ CONF_USER_INVALID = 0,
+ CONF_USER_NONE = 1,
+ CONF_USER_MERGE,
+ CONF_USER_ONLY
+};
+
typedef void (*conf_error_func) (const char *message);
-hash_t* conf_parse_file (const char *filename,
- int flags,
- conf_error_func error_func);
+int _p11_conf_merge_defaults (hash_t *config,
+ hash_t *defaults);
+
+/* Returns a hash of char *key -> char *value */
+hash_t* _p11_conf_parse_file (const char *filename,
+ int flags);
+
+/* Returns a hash of char *key -> char *value */
+hash_t* _p11_conf_load_globals (const char *system_conf, const char *user_conf,
+ int *user_mode);
+
+/* Returns a hash of char* name -> hash_t *config */
+hash_t* _p11_conf_load_modules (int user_mode, const char *system_dir,
+ const char *user_dir);
#endif /* __CONF_H__ */
diff --git a/p11-kit/hash.c b/p11-kit/hash.c
index cfa6bf7..d116916 100644
--- a/p11-kit/hash.c
+++ b/p11-kit/hash.c
@@ -100,10 +100,6 @@ struct hash {
#define int_calloc calloc
#define int_free free
-/*
- * Hash creation functions.
- */
-
static hash_entry_t**
alloc_array(hash_t* ht, unsigned int max)
{
@@ -138,46 +134,30 @@ hash_create (hash_hash_func hash_func,
return ht;
}
-void
-hash_free (hash_t* ht)
+static hash_entry_t*
+next_entry (hash_iter_t* hi)
{
- hash_iter_t hi;
-
- if (!ht)
- return;
-
- hash_iterate (ht, &hi);
- while (hash_next (&hi, NULL, NULL)) {
- if (ht->key_destroy_func)
- ht->key_destroy_func (hi.ths->key);
- if (ht->value_destroy_func)
- ht->value_destroy_func (hi.ths->val);
- free (hi.ths);
+ hash_entry_t *he = hi->next;
+ while (!he) {
+ if (hi->index > hi->ht->max)
+ return NULL;
+ he = hi->ht->array[hi->index++];
}
-
- if (ht->array)
- int_free (ht->array);
-
- int_free (ht);
+ hi->next = he->next;
+ return he;
}
-/*
- * Hash iteration functions.
- */
+
int
hash_next (hash_iter_t* hi, void **key, void **value)
{
- hi->ths = hi->next;
- while (!hi->ths) {
- if (hi->index > hi->ht->max)
- return 0;
- hi->ths = hi->ht->array[hi->index++];
- }
- hi->next = hi->ths->next;
+ hash_entry_t *he = next_entry (hi);
+ if (he == NULL)
+ return 0;
if (key)
- *key = hi->ths->key;
+ *key = he->key;
if (value)
- *value = hi->ths->val;
+ *value = he->val;
return 1;
}
@@ -186,19 +166,15 @@ hash_iterate (hash_t* ht, hash_iter_t *hi)
{
hi->ht = ht;
hi->index = 0;
- hi->ths = NULL;
hi->next = NULL;
}
-/*
- * Expanding a hash table
- */
-
static int
expand_array (hash_t* ht)
{
hash_iter_t hi;
- hash_entry_t** new_array;
+ hash_entry_t *he;
+ hash_entry_t **new_array;
unsigned int new_max;
new_max = ht->max * 2 + 1;
@@ -208,10 +184,10 @@ expand_array (hash_t* ht)
return 0;
hash_iterate (ht, &hi);
- while (hash_next (&hi, NULL, NULL)) {
- unsigned int i = hi.ths->hash & new_max;
- hi.ths->next = new_array[i];
- new_array[i] = hi.ths;
+ while ((he = next_entry (&hi)) != NULL) {
+ unsigned int i = he->hash & new_max;
+ he->next = new_array[i];
+ new_array[i] = he;
}
if(ht->array)
@@ -301,7 +277,7 @@ hash_set (hash_t* ht, void* key, void* val)
}
int
-hash_remove (hash_t* ht, const void* key)
+hash_steal (hash_t *ht, const void *key, void **stolen_key, void **stolen_value)
{
hash_entry_t** hep = find_entry (ht, key, 0);
@@ -309,15 +285,32 @@ hash_remove (hash_t* ht, const void* key)
hash_entry_t* old = *hep;
*hep = (*hep)->next;
--ht->count;
- if (ht->key_destroy_func)
- ht->key_destroy_func (old->key);
- if (ht->value_destroy_func)
- ht->value_destroy_func (old->val);
+ if (stolen_key)
+ *stolen_key = old->key;
+ if (stolen_value)
+ *stolen_value = old->val;
free (old);
return 1;
}
return 0;
+
+}
+
+int
+hash_remove (hash_t *ht, const void *key)
+{
+ void *old_key;
+ void *old_value;
+
+ if (!hash_steal (ht, key, &old_key, &old_value))
+ return 0;
+
+ if (ht->key_destroy_func)
+ ht->key_destroy_func (old_key);
+ if (ht->value_destroy_func)
+ ht->value_destroy_func (old_value);
+ return 1;
}
void
@@ -344,6 +337,30 @@ hash_clear (hash_t* ht)
ht->count = 0;
}
+void
+hash_free (hash_t* ht)
+{
+ hash_entry_t *he;
+ hash_iter_t hi;
+
+ if (!ht)
+ return;
+
+ hash_iterate (ht, &hi);
+ while ((he = next_entry (&hi)) != NULL) {
+ if (ht->key_destroy_func)
+ ht->key_destroy_func (he->key);
+ if (ht->value_destroy_func)
+ ht->value_destroy_func (he->val);
+ free (he);
+ }
+
+ if (ht->array)
+ int_free (ht->array);
+
+ int_free (ht);
+}
+
unsigned int
hash_count (hash_t* ht)
{
diff --git a/p11-kit/hash.h b/p11-kit/hash.h
index 649b98b..8c3060a 100644
--- a/p11-kit/hash.h
+++ b/p11-kit/hash.h
@@ -81,7 +81,6 @@ typedef struct hash hash_t;
typedef struct hash_iter
{
hash_t* ht;
- struct hash_entry* ths;
struct hash_entry* next;
unsigned int index;
} hash_iter_t;
@@ -140,6 +139,15 @@ int hash_remove (hash_t* ht,
const void* key);
/*
+ * hash_steal: Remove a value from the hash table without calling destroy funcs
+ * - returns 1 if the entry was found
+ */
+int hash_steal (hash_t *ht,
+ const void *key,
+ void **stolen_key,
+ void **stolen_value);
+
+/*
* hash_first: Start enumerating through the hash table
* - returns a hash iterator
*/
diff --git a/p11-kit/modules.c b/p11-kit/modules.c
index 268a3d5..999770d 100644
--- a/p11-kit/modules.c
+++ b/p11-kit/modules.c
@@ -51,7 +51,6 @@
#include <dlfcn.h>
#include <errno.h>
#include <pthread.h>
-#include <pwd.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
@@ -130,71 +129,6 @@ static struct _Shared {
} gl = { NULL, NULL };
/* -----------------------------------------------------------------------------
- * UTILITIES
- */
-
-static void
-warning (const char* msg, ...)
-{
- char buffer[512];
- va_list va;
-
- va_start (va, msg);
-
- vsnprintf(buffer, sizeof (buffer) - 1, msg, va);
- buffer[sizeof (buffer) - 1] = 0;
- fprintf (stderr, "p11-kit: %s\n", buffer);
-
- va_end (va);
-}
-
-static void
-conf_error (const char *buffer)
-{
- /* called from conf.c */
- fprintf (stderr, "p11-kit: %s\n", buffer);
-}
-
-static char*
-strconcat (const char *first, ...)
-{
- size_t length = 0;
- const char *arg;
- char *result, *at;
- va_list va;
-
- va_start (va, first);
-
- for (arg = first; arg; arg = va_arg (va, const char*))
- length += strlen (arg);
-
- va_end (va);
-
- at = result = malloc (length + 1);
- if (!result)
- return NULL;
-
- va_start (va, first);
-
- for (arg = first; arg; arg = va_arg (va, const char*)) {
- length = strlen (arg);
- memcpy (at, arg, length);
- at += length;
- }
-
- va_end (va);
-
- *at = 0;
- return result;
-}
-
-static int
-strequal (const char *one, const char *two)
-{
- return strcmp (one, two) == 0;
-}
-
-/* -----------------------------------------------------------------------------
* P11-KIT FUNCTIONALITY
*/
@@ -322,7 +256,7 @@ dlopen_and_get_function_list (Module *mod, const char *path)
mod->dl_module = dlopen (path, RTLD_LOCAL | RTLD_NOW);
if (mod->dl_module == NULL) {
- warning ("couldn't load module: %s: %s", path, dlerror ());
+ _p11_warning ("couldn't load module: %s: %s", path, dlerror ());
return CKR_GENERAL_ERROR;
}
@@ -330,15 +264,15 @@ dlopen_and_get_function_list (Module *mod, const char *path)
gfl = dlsym (mod->dl_module, "C_GetFunctionList");
if (!gfl) {
- warning ("couldn't find C_GetFunctionList entry point in module: %s: %s",
- path, dlerror ());
+ _p11_warning ("couldn't find C_GetFunctionList entry point in module: %s: %s",
+ path, dlerror ());
return CKR_GENERAL_ERROR;
}
rv = gfl (&mod->funcs);
if (rv != CKR_OK) {
- warning ("call to C_GetFunctiontList failed in module: %s: %s",
- path, p11_kit_strerror (rv));
+ _p11_warning ("call to C_GetFunctiontList failed in module: %s: %s",
+ path, p11_kit_strerror (rv));
return rv;
}
@@ -380,38 +314,32 @@ load_module_from_file_unlocked (const char *path, Module **result)
}
static CK_RV
-load_module_from_config_unlocked (const char *configfile, const char *name)
+take_config_and_load_module_unlocked (char **name, hash_t **config)
{
Module *mod, *prev;
const char *path;
CK_RV rv;
- assert (configfile);
-
- mod = alloc_module_unlocked ();
- if (!mod)
- return CKR_HOST_MEMORY;
+ assert (name);
+ assert (*name);
+ assert (config);
+ assert (*config);
- mod->config = conf_parse_file (configfile, 0, conf_error);
- if (!mod->config) {
- free_module_unlocked (mod);
- if (errno == ENOMEM)
- return CKR_HOST_MEMORY;
- return CKR_GENERAL_ERROR;
+ path = hash_get (*config, "module");
+ if (path == NULL) {
+ debug ("no module path for module, skipping: %s", *name);
+ return CKR_OK;
}
- mod->name = strdup (name);
- if (!mod->name) {
- free_module_unlocked (mod);
+ mod = alloc_module_unlocked ();
+ if (!mod)
return CKR_HOST_MEMORY;
- }
- path = hash_get (mod->config, "module");
- if (path == NULL) {
- free_module_unlocked (mod);
- debug ("no module path specified in config, skipping: %s", configfile);
- return CKR_OK;
- }
+ /* Take ownership of thes evariables */
+ mod->config = *config;
+ *config = NULL;
+ mod->name = *name;
+ *name = NULL;
rv = dlopen_and_get_function_list (mod, path);
if (rv != CKR_OK) {
@@ -432,7 +360,7 @@ load_module_from_config_unlocked (const char *configfile, const char *name)
/* Refuse to load duplicate module */
if (prev) {
- warning ("duplicate configured module: %s: %s", mod->name, path);
+ _p11_warning ("duplicate configured module: %s: %s", mod->name, path);
free_module_unlocked (mod);
return CKR_GENERAL_ERROR;
}
@@ -453,233 +381,61 @@ load_module_from_config_unlocked (const char *configfile, const char *name)
}
static CK_RV
-load_modules_from_config_unlocked (const char *directory)
-{
- struct dirent *dp;
- struct stat st;
- CK_RV rv = CKR_OK;
- DIR *dir;
- int is_dir;
- char *path;
-
- debug ("loading module configs in: %s", directory);
-
- /* First we load all the modules */
- dir = opendir (directory);
- if (!dir) {
- if (errno == ENOENT || errno == ENOTDIR)
- return CKR_OK;
- warning ("couldn't list directory: %s", directory);
- return CKR_GENERAL_ERROR;
- }
-
- /* We're within a global mutex, so readdir is safe */
- while ((dp = readdir(dir)) != NULL) {
- path = strconcat (directory, "/", dp->d_name, NULL);
- if (!path) {
- rv = CKR_HOST_MEMORY;
- break;
- }
-
- is_dir = 0;
-#ifdef HAVE_STRUCT_DIRENT_D_TYPE
- if(dp->d_type != DT_UNKNOWN) {
- is_dir = (dp->d_type == DT_DIR);
- } else
-#endif
- {
- if (stat (path, &st) < 0) {
- warning ("couldn't stat path: %s", path);
- free (path);
- rv = CKR_GENERAL_ERROR;
- break;
- }
- is_dir = S_ISDIR (st.st_mode);
- }
-
- if (is_dir)
- rv = CKR_OK;
- else
- rv = load_module_from_config_unlocked (path, dp->d_name);
-
- free (path);
-
- if (rv != CKR_OK)
- break;
- }
-
- closedir (dir);
-
- return rv;
-}
-
-static char*
-expand_user_path (const char *path)
-{
- const char *env;
- struct passwd *pwd;
-
- if (path[0] == '~' && path[1] == '/') {
- env = getenv ("HOME");
- if (env && env[0]) {
- return strconcat (env, path + 1, NULL);
- } else {
- pwd = getpwuid (getuid ());
- if (!pwd)
- return NULL;
- return strconcat (pwd->pw_dir, path + 1, NULL);
- }
- }
-
- return strdup (path);
-}
-
-enum {
- USER_CONFIG_INVALID = 0,
- USER_CONFIG_NONE = 1,
- USER_CONFIG_MERGE,
- USER_CONFIG_OVERRIDE
-};
-
-static int
-user_config_mode (hash_t *config, int defmode)
-{
- const char *mode;
-
- /* Whether we should use or override from user directory */
- mode = hash_get (config, "user-config");
- if (mode == NULL) {
- return defmode;
- } else if (strequal (mode, "none")) {
- return USER_CONFIG_NONE;
- } else if (strequal (mode, "merge")) {
- return USER_CONFIG_MERGE;
- } else if (strequal (mode, "override")) {
- return USER_CONFIG_OVERRIDE;
- } else {
- warning ("invalid mode for 'user-config': %s", mode);
- return USER_CONFIG_INVALID;
- }
-}
-
-static CK_RV
-load_config_files_unlocked (int *user_mode)
+load_registered_modules_unlocked (void)
{
- hash_t *config = NULL;
- hash_t *uconfig = NULL;
- void *key = NULL;
- void *value = NULL;
- char *path;
- int mode;
- CK_RV rv = CKR_GENERAL_ERROR;
hash_iter_t hi;
+ hash_t *configs;
+ void *key;
+ char *name;
+ hash_t *config;
+ int mode;
+ CK_RV rv;
- /* Should only be called after everything has been unloaded */
- assert (!gl.config);
-
- /* Load the main configuration */
- config = conf_parse_file (P11_SYSTEM_CONF, CONF_IGNORE_MISSING, conf_error);
- if (!config) {
- rv = (errno == ENOMEM) ? CKR_HOST_MEMORY : CKR_GENERAL_ERROR;
- goto finished;
- }
-
- /* Whether we should use or override from user directory */
- mode = user_config_mode (config, USER_CONFIG_NONE);
- if (mode == USER_CONFIG_INVALID)
- goto finished;
-
- if (mode != USER_CONFIG_NONE) {
- path = expand_user_path (P11_USER_CONF);
- if (!path)
- goto finished;
-
- /* Load up the user configuration */
- uconfig = conf_parse_file (path, CONF_IGNORE_MISSING, conf_error);
- free (path);
+ if (gl.config)
+ return CKR_OK;
- if (!uconfig) {
- rv = (errno == ENOMEM) ? CKR_HOST_MEMORY : CKR_GENERAL_ERROR;
- goto finished;
- }
+ /* Load the global configuration files */
+ config = _p11_conf_load_globals (P11_SYSTEM_CONF, P11_USER_CONF, &mode);
+ if (config == NULL)
+ return (errno == ENOMEM) ? CKR_HOST_MEMORY : CKR_GENERAL_ERROR;
- /* Figure out what the user mode is */
- mode = user_config_mode (uconfig, mode);
- if (mode == USER_CONFIG_INVALID)
- goto finished;
-
- /* Merge everything into the system config */
- if (mode == USER_CONFIG_MERGE) {
- hash_iterate (uconfig, &hi);
- while (hash_next (&hi, &key, &value)) {
- key = strdup (key);
- if (key == NULL)
- goto finished;
- value = strdup (value);
- if (value == NULL)
- goto finished;
- if (!hash_set (config, key, value))
- goto finished;
- key = NULL;
- value = NULL;
- }
+ assert (mode != CONF_USER_INVALID);
- /* Override the system config */
- } else if (mode == USER_CONFIG_OVERRIDE) {
- hash_free (config);
- config = uconfig;
- uconfig = NULL;
- }
+ configs = _p11_conf_load_modules (mode, P11_SYSTEM_MODULES, P11_USER_MODULES);
+ if (configs == NULL) {
+ rv = (errno == ENOMEM) ? CKR_HOST_MEMORY : CKR_GENERAL_ERROR;
+ hash_free (config);
+ return rv;
}
+ assert (gl.config == NULL);
gl.config = config;
- config = NULL;
- rv = CKR_OK;
-
- if (user_mode)
- *user_mode = mode;
-
-finished:
- hash_free (config);
- hash_free (uconfig);
- free (key);
- free (value);
- return rv;
-}
-
-static CK_RV
-load_registered_modules_unlocked (void)
-{
- char *path;
- int mode;
- CK_RV rv;
- rv = load_config_files_unlocked (&mode);
- if (rv != CKR_OK)
- return rv;
+ /*
+ * Now go through each config and turn it into a module. As we iterate
+ * we steal the values of the config.
+ */
+ hash_iterate (configs, &hi);
+ while (hash_next (&hi, &key, NULL)) {
+ if (!hash_steal (configs, name, (void**)&name, (void**)config))
+ assert (0 && "not reached");
- assert (gl.config);
- assert (mode != USER_CONFIG_INVALID);
+ rv = take_config_and_load_module_unlocked (&name, &config);
- /* Load each module from the main list */
- if (mode != USER_CONFIG_OVERRIDE) {
- rv = load_modules_from_config_unlocked (P11_SYSTEM_MODULES);
- if (rv != CKR_OK);
- return rv;
- }
+ /*
+ * These variables will be cleared if ownership is transeferred
+ * by the above function call.
+ */
+ free (name);
+ hash_free (config);
- /* Load each module from the user list */
- if (mode != USER_CONFIG_NONE) {
- path = expand_user_path (P11_USER_MODULES);
- if (!path)
- rv = CKR_GENERAL_ERROR;
- else
- rv = load_modules_from_config_unlocked (path);
- free (path);
- if (rv != CKR_OK);
+ if (rv != CKR_OK) {
+ hash_free (configs);
return rv;
+ }
}
+ hash_free (configs);
return CKR_OK;
}
diff --git a/p11-kit/private.h b/p11-kit/private.h
index 56d5394..e8abe93 100644
--- a/p11-kit/private.h
+++ b/p11-kit/private.h
@@ -35,12 +35,17 @@
#ifndef __P11_KIT_PRIVATE_H__
#define __P11_KIT_PRIVATE_H__
+#include "pkcs11.h"
+#include "pthread.h"
+
extern pthread_mutex_t _p11_mutex;
#define _p11_lock() pthread_mutex_lock (&_p11_mutex);
#define _p11_unlock() pthread_mutex_unlock (&_p11_mutex);
+void _p11_warning (const char* msg, ...);
+
CK_FUNCTION_LIST_PTR_PTR _p11_kit_registered_modules_unlocked (void);
CK_RV _p11_kit_initialize_registered_unlocked_reentrant (void);
@@ -49,4 +54,10 @@ CK_RV _p11_kit_finalize_registered_unlocked_reentrant (void);
void _p11_kit_proxy_after_fork (void);
+CK_RV _p11_load_config_files_unlocked (const char *system_conf,
+ const char *user_conf,
+ int *user_mode);
+
+const char* _p11_kit_clear_message (void);
+
#endif /* __P11_KIT_PRIVATE_H__ */
diff --git a/p11-kit/util.c b/p11-kit/util.c
index e4b0f49..7ea125f 100644
--- a/p11-kit/util.c
+++ b/p11-kit/util.c
@@ -37,10 +37,13 @@
#include "config.h"
#include "p11-kit.h"
+#include "private.h"
#include "util.h"
#include <assert.h>
+#include <stdarg.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
void*
@@ -123,3 +126,17 @@ p11_kit_space_strdup (const unsigned char *string, size_t max_length)
result[length] = 0;
return result;
}
+
+void
+_p11_warning (const char* msg, ...)
+{
+ char buffer[512];
+ va_list va;
+
+ va_start (va, msg);
+ vsnprintf (buffer, sizeof (buffer) - 1, msg, va);
+ va_end (va);
+
+ buffer[sizeof (buffer) - 1] = 0;
+ fprintf (stderr, "p11-kit: %s\n", buffer);
+}
diff --git a/tests/conf-test.c b/tests/conf-test.c
index d9929aa..2ef4e5c 100644
--- a/tests/conf-test.c
+++ b/tests/conf-test.c
@@ -44,18 +44,12 @@
static int n_errors = 0;
static void
-error_func (const char *buffer)
-{
- ++n_errors;
-}
-
-static void
test_parse_conf_1 (CuTest *tc)
{
hash_t *ht;
const char *value;
- ht = conf_parse_file (SRCDIR "/files/test-1.conf", 0, error_func);
+ ht = _p11_conf_parse_file (SRCDIR "/files/test-1.conf", 0);
CuAssertPtrNotNull (tc, ht);
value = hash_get (ht, "key1");
@@ -79,7 +73,7 @@ test_parse_ignore_missing (CuTest *tc)
hash_t *ht;
n_errors = 0;
- ht = conf_parse_file (SRCDIR "/files/non-existant.conf", CONF_IGNORE_MISSING, error_func);
+ ht = _p11_conf_parse_file (SRCDIR "/files/non-existant.conf", CONF_IGNORE_MISSING);
CuAssertPtrNotNull (tc, ht);
CuAssertIntEquals (tc, 0, hash_count (ht));
@@ -93,11 +87,38 @@ test_parse_fail_missing (CuTest *tc)
hash_t *ht;
n_errors = 0;
- ht = conf_parse_file (SRCDIR "/files/non-existant.conf", 0, error_func);
+ ht = _p11_conf_parse_file (SRCDIR "/files/non-existant.conf", 0);
CuAssertPtrEquals (tc, ht, NULL);
CuAssertIntEquals (tc, 1, n_errors);
}
+static void
+test_merge_defaults (CuTest *tc)
+{
+ hash_t *values;
+ hash_t *defaults;
+
+ values = hash_create (hash_string_hash, hash_string_equal, free, free);
+ defaults = hash_create (hash_string_hash, hash_string_equal, free, free);
+
+ hash_set (values, strdup ("one"), strdup ("real1"));
+ hash_set (values, strdup ("two"), strdup ("real2"));
+
+ hash_set (defaults, strdup ("two"), strdup ("default2"));
+ hash_set (defaults, strdup ("three"), strdup ("default3"));
+
+ if (!_p11_conf_merge_defaults (values, defaults))
+ CuFail (tc, "should not be reached");
+
+ hash_free (defaults);
+
+ CuAssertStrEquals (tc, hash_get (values, "one"), "real1");
+ CuAssertStrEquals (tc, hash_get (values, "two"), "real2");
+ CuAssertStrEquals (tc, hash_get (values, "three"), "default3");
+
+ hash_free (values);
+}
+
int
main (void)
{
@@ -108,6 +129,7 @@ main (void)
SUITE_ADD_TEST (suite, test_parse_conf_1);
SUITE_ADD_TEST (suite, test_parse_ignore_missing);
SUITE_ADD_TEST (suite, test_parse_fail_missing);
+ SUITE_ADD_TEST (suite, test_merge_defaults);
CuSuiteRun (suite);
CuSuiteSummary (suite, output);