]> granicus.if.org Git - p11-kit/commitdiff
Refactor configuration
authorStef Walter <stefw@collabora.co.uk>
Wed, 8 Jun 2011 19:21:54 +0000 (21:21 +0200)
committerStef Walter <stefw@collabora.co.uk>
Wed, 8 Jun 2011 19:21:54 +0000 (21:21 +0200)
 * Move configuration loading into conf.c
 * Have user modules with same name merge/override modules in system.

p11-kit/Makefile.am
p11-kit/conf.c
p11-kit/conf.h
p11-kit/hash.c
p11-kit/hash.h
p11-kit/modules.c
p11-kit/private.h
p11-kit/util.c
tests/conf-test.c

index 99d7d1a3e9c061339598caa19814651c2b1fe6cb..6f2fc1924187a54dbc738bd5c1f0d49c6c7d4203 100644 (file)
@@ -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 \
index 51fe5793e134b9c4e057ee02472f82945e814545..8d4703e0e104d3d423e73b49a24d4ce169f501fe 100644 (file)
@@ -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>
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
+#include <pwd.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #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)
 {
@@ -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;
+}
index 81e0fb8057a187a1bd51079b64be97a86e45ef8c..dc482105ccfc2439105e72d4ab53371721502613 100644 (file)
@@ -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__ */
index cfa6bf7b163e5990fcbe8afa382157d60c41bdde..d116916455b599a63fe0e802f3a06442f675294b 100644 (file)
@@ -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)
 {
index 649b98bb12a16da71277cb304473b53f284c4610..8c3060a1379aebff4278557212976527e8e120eb 100644 (file)
@@ -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;
@@ -139,6 +138,15 @@ int                hash_set                    (hash_t* ht,
 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
index 268a3d5a966e05c9d8c62ddae9e0107682899022..999770dc20dda18db6991791232176a47a0fa740 100644 (file)
@@ -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>
@@ -129,71 +128,6 @@ static struct _Shared {
        hash_t *config;
 } 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;
 }
 
index 56d53941652a75828278668249cfec9447882eb6..e8abe93af9eee38c7366b890173e0f174303b489 100644 (file)
 #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__ */
index e4b0f49978f9079c0f4be76c374136a1e05003ce..7ea125f57876eea19ddce09d2fc46b502ada9e22 100644 (file)
 #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);
+}
index d9929aa762c95c528f7b615170a0b7568e47dffb..2ef4e5c8c329ea6062fee2a4d640f06cd11dc457 100644 (file)
 
 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);