diff options
author | Mark Michelson <mmichelson@digium.com> | 2012-12-11 21:52:09 +0000 |
---|---|---|
committer | Mark Michelson <mmichelson@digium.com> | 2012-12-11 21:52:09 +0000 |
commit | f5e9cf5975cf8a58774579f126952be61beb0e54 (patch) | |
tree | ab1796aeb12a5367d1c7e95332be610ef490a5a5 /main/uuid.c | |
parent | 603bf5301f3a950687e41d40eeb02870044f6d0b (diff) |
Add automerge property back after conflict.
git-svn-id: https://origsvn.digium.com/svn/asterisk/team/mmichelson/threadpool@377879 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/uuid.c')
-rw-r--r-- | main/uuid.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/main/uuid.c b/main/uuid.c new file mode 100644 index 000000000..46ac63d20 --- /dev/null +++ b/main/uuid.c @@ -0,0 +1,206 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2012, Digium, Inc. + * + * Mark Michelson <mmmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * \brief Universally unique identifier support + */ + +#include "asterisk.h" +#include <uuid/uuid.h> +#include <fcntl.h> + +#include "asterisk/uuid.h" +#include "asterisk/utils.h" +#include "asterisk/strings.h" +#include "asterisk/logger.h" +#include "asterisk/lock.h" + +AST_MUTEX_DEFINE_STATIC(uuid_lock); + +static int has_dev_urandom; + +struct ast_uuid { + uuid_t uu; +}; + +struct ast_uuid *ast_uuid_generate(void) +{ + struct ast_uuid *uuid = ast_malloc(sizeof(*uuid)); + + if (!uuid) { + return NULL; + } + /* libuuid provides three methods of generating uuids, + * uuid_generate(), uuid_generate_random(), and uuid_generate_time(). + * + * uuid_generate_random() creates a UUID based on random numbers. The method + * attempts to use either /dev/urandom or /dev/random to generate random values. + * If these resources are unavailable, then random numbers will be generated + * using C library calls to generate pseudorandom numbers. + * This method of generating UUIDs corresponds to section 4.4 of RFC 4122. + * + * uuid_generate_time() creates a UUID based on the current time plus + * a system identifier (MAC address of the ethernet interface). This + * method of generating UUIDs corresponds to section 4.2 of RFC 4122. + * + * uuid_generate() will check if /dev/urandom or /dev/random is available to + * use. If so, it will use uuid_generate_random(). Otherwise, it will use + * uuid_generate_time(). The idea is that it avoids using pseudorandom + * numbers if necessary. + * + * For our purposes, we do not use the time-based UUID at all. There are + * several reasons for this: + * + * 1) The time-based algorithm makes use of a daemon process (uuidd) in order + * to ensure that any concurrent requests for UUIDs result in unique results. + * Use of this daemon is a bit dodgy for a few reasons + * + * a) libuuid assumes a hardcoded location for the .pid file of the daemon. + * However, the daemon could already be running on the system in a different + * location than expected. If this is the case, then attempting to connect + * to the daemon will fail, and attempting to launch another instance in + * the expected location will also fail. + * + * b) If the daemon is not running, then the first attempt to create a + * time-based UUID will result in launching the daemon. Because of the hard- + * coded locations that libuuid assumes for the daemon, Asterisk must be + * run with permissions that will allow for the daemon to be launched in + * the expected directories. + * + * c) Once the daemon is running, concurrent requests for UUIDs are thread-safe. + * However, the actual launching of the daemon is not thread-safe since libuuid + * uses no synchronization primitives to ensure that only one thread (or process) + * launches the daemon. + * + * d) When libuuid launches the daemon, it sets an inactivity timer. + * If no UUID generation requests are issued in that time period, + * then the daemon will exit. If a new request should occur after the daemon + * exits, then the daemon will be relaunched. Given point c), we cannot + * necessarily guarantee the thread-safety of time-based UUID generation since + * we cannot necessarily guarantee the daemon is running as we expect. + * We could set up a watchdog thread to generate UUIDs at regular intervals to + * prevent the daemon from exiting, but frankly, that sucks. + * + * 2) Since the MAC address of the Ethernet interface is part of the UUID when + * using the time-based method, there is information leaked. + * + * Given these drawbacks, we stick to only using random UUIDs. The chance of /dev/random + * or /dev/urandom not existing on systems in this age is next to none. + */ + + /* XXX Currently, we only protect this call if the user has no /dev/urandon on their system. + * If it turns out that there are issues with UUID generation despite the presence of + * /dev/urandom, then we may need to make the locking/unlocking unconditional. + */ + if (!has_dev_urandom) { + ast_mutex_lock(&uuid_lock); + } + uuid_generate_random(uuid->uu); + if (!has_dev_urandom) { + ast_mutex_unlock(&uuid_lock); + } + return uuid; +} + +char *ast_uuid_to_str(const struct ast_uuid *uuid, char *buf, size_t size) +{ + ast_assert(size >= AST_UUID_STR_LEN); + uuid_unparse_lower(uuid->uu, buf); + return buf; +} + +struct ast_uuid *ast_str_to_uuid(const char *str) +{ + struct ast_uuid *uuid = ast_malloc(sizeof(*uuid)); + int res; + if (!uuid) { + return NULL; + } + res = uuid_parse(str, uuid->uu); + if (res) { + ast_log(LOG_WARNING, "Unable to convert string %s into a UUID\n", str); + ast_free(uuid); + return NULL; + } + return uuid; +} + +struct ast_uuid *ast_uuid_copy(const struct ast_uuid *src) +{ + struct ast_uuid *dst = ast_malloc(sizeof(*dst)); + if (!dst) { + return NULL; + } + uuid_copy(dst->uu, src->uu); + return dst; +} + +int ast_uuid_compare(const struct ast_uuid *left, const struct ast_uuid *right) +{ + return uuid_compare(left->uu, right->uu); +} + +void ast_uuid_clear(struct ast_uuid *uuid) +{ + uuid_clear(uuid->uu); +} + +int ast_uuid_is_nil(const struct ast_uuid *uuid) +{ + return uuid_is_null(uuid->uu); +} + +void ast_uuid_init(void) +{ + /* This requires some explanation. + * + * libuuid generates UUIDs based on random number generation. This involves + * opening a handle to /dev/urandom or /dev/random in order to get random + * data for the UUIDs. + * + * This is thread-safe, to a point. The problem is that the first attempt + * to generate a UUID will result in opening the random number handle. Once + * the handle is opened, all further generation is thread safe. This + * first generation can be potentially risky if multiple threads attempt + * to generate a UUID at the same time, though, since there is no thread + * synchronization used within libuuid. To get around this potential + * issue, we go ahead and generate a UUID up front so that the underlying + * work is done before we start requesting UUIDs for real. + * + * Think of this along the same lines as initializing a singleton. + */ + uuid_t uu; + int dev_urandom_fd; + + dev_urandom_fd = open("/dev/urandom", O_RDONLY); + if (dev_urandom_fd < 0) { + ast_log(LOG_WARNING, "It appears your system does not have /dev/urandom on it. This\n" + "means that UUID generation will use a pseudorandom number generator. Since\n" + "the thread-safety of your system's random number generator cannot\n" + "be guaranteed, we have to synchronize UUID generation. This may result\n" + "in decreased performance. It is highly recommended that you set up your\n" + "system to have /dev/urandom\n"); + } else { + has_dev_urandom = 1; + close(dev_urandom_fd); + } + uuid_generate_random(uu); + + ast_debug(1, "UUID system initiated\n"); + return; +} |