mirror of https://github.com/asterisk/asterisk
git-svn-id: https://origsvn.digium.com/svn/asterisk/team/mmichelson/threadpool@377879 65c4cc65-6c06-0410-ace0-fbb531ad65f3changes/78/78/1
parent
603bf5301f
commit
f5e9cf5975
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ASTERISK_UUID_H
|
||||||
|
#define _ASTERISK_UUID_H
|
||||||
|
|
||||||
|
/* Size of an RFC 4122 UUID string plus terminating null byte */
|
||||||
|
#define AST_UUID_STR_LEN 37
|
||||||
|
|
||||||
|
struct ast_uuid;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Initialize the UUID system
|
||||||
|
*/
|
||||||
|
void ast_uuid_init(void);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Generate a UUID
|
||||||
|
*
|
||||||
|
* This function allocates memory on the heap. The returned
|
||||||
|
* pointer must be freed using ast_free()
|
||||||
|
*
|
||||||
|
* \retval NULL Generation failed
|
||||||
|
* \retval non-NULL heap-allocated UUID
|
||||||
|
*/
|
||||||
|
struct ast_uuid *ast_uuid_generate(void);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Convert a UUID to a string
|
||||||
|
*
|
||||||
|
* \param uuid The UUID to convert to a string
|
||||||
|
* \param[out] buf The buffer where the UUID string will be stored
|
||||||
|
* \param size The size of the buffer. Must be at least AST_UUID_STR_LEN.
|
||||||
|
* \returns The UUID string (a pointer to buf)
|
||||||
|
*/
|
||||||
|
char *ast_uuid_to_str(const struct ast_uuid *uuid, char *buf, size_t size);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Convert a string to a UUID
|
||||||
|
*
|
||||||
|
* This function allocates memory on the heap. The returned
|
||||||
|
* pointer must be freed using ast_free()
|
||||||
|
*
|
||||||
|
* \param str The string to convert to a UUID
|
||||||
|
* \retval NULL Failed to convert
|
||||||
|
* \retval non-NULL The heap-allocated converted UUID
|
||||||
|
*/
|
||||||
|
struct ast_uuid *ast_str_to_uuid(const char *str);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Make a copy of a UUID
|
||||||
|
*
|
||||||
|
* This function allocates memory on the heap. The returned
|
||||||
|
* pointer must be freed using ast_free()
|
||||||
|
*
|
||||||
|
* \param src The source UUID to copy
|
||||||
|
* \retval NULL Failed to copy
|
||||||
|
* \retval non-NULL The heap-allocated duplicate UUID
|
||||||
|
*/
|
||||||
|
struct ast_uuid *ast_uuid_copy(const struct ast_uuid *src);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Compare two UUIDs
|
||||||
|
*
|
||||||
|
* \param left First UUID to compare
|
||||||
|
* \param right Second UUID to compare
|
||||||
|
* \retval <0 left is lexicographically less than right
|
||||||
|
* \retval 0 left and right are the same
|
||||||
|
* \retval >0 left is lexicographically greater than right
|
||||||
|
*/
|
||||||
|
int ast_uuid_compare(const struct ast_uuid *left, const struct ast_uuid *right);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Clear a UUID by setting it to be a nil UUID (all 0s)
|
||||||
|
*
|
||||||
|
* \param uuid UUID to clear
|
||||||
|
*/
|
||||||
|
void ast_uuid_clear(struct ast_uuid *uuid);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Check if a UUID is a nil UUID (all 0s)
|
||||||
|
*
|
||||||
|
* \param uuid UUID to check
|
||||||
|
* \retval 0 The UUID is not nil
|
||||||
|
* \retval non-zero The UUID is nil
|
||||||
|
*/
|
||||||
|
int ast_uuid_is_nil(const struct ast_uuid *uuid);
|
||||||
|
#endif
|
@ -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;
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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 tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*** MODULEINFO
|
||||||
|
<depend>TEST_FRAMEWORK</depend>
|
||||||
|
<support_level>core</support_level>
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include "asterisk.h"
|
||||||
|
#include "asterisk/test.h"
|
||||||
|
#include "asterisk/uuid.h"
|
||||||
|
#include "asterisk/module.h"
|
||||||
|
|
||||||
|
AST_TEST_DEFINE(uuid)
|
||||||
|
{
|
||||||
|
struct ast_uuid *uuid1 = NULL;
|
||||||
|
struct ast_uuid *uuid2 = NULL;
|
||||||
|
struct ast_uuid *uuid3 = NULL;
|
||||||
|
char uuid_str[AST_UUID_STR_LEN];
|
||||||
|
enum ast_test_result_state res = AST_TEST_FAIL;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case TEST_INIT:
|
||||||
|
info->name = "uuid";
|
||||||
|
info->category = "/main/uuid/";
|
||||||
|
info->summary = "UUID unit test";
|
||||||
|
info->description =
|
||||||
|
"This tests basic UUID operations to ensure they work properly";
|
||||||
|
return AST_TEST_NOT_RUN;
|
||||||
|
case TEST_EXECUTE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First, make sure that we can generate a UUID */
|
||||||
|
uuid1 = ast_uuid_generate();
|
||||||
|
if (!uuid1) {
|
||||||
|
ast_test_status_update(test, "Unable to generate a UUID\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure we're not generating nil UUIDs */
|
||||||
|
if (ast_uuid_is_nil(uuid1)) {
|
||||||
|
ast_test_status_update(test, "We generated a nil UUID. Something is wrong\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert it to a string */
|
||||||
|
ast_uuid_to_str(uuid1, uuid_str, sizeof(uuid_str));
|
||||||
|
|
||||||
|
if (strlen(uuid_str) != (AST_UUID_STR_LEN - 1)) {
|
||||||
|
ast_test_status_update(test, "Failed to convert the UUID to a string\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_test_status_update(test, "Converted uuid to string, got %s\n", uuid_str);
|
||||||
|
|
||||||
|
/* Now convert the string back to a UUID */
|
||||||
|
uuid2 = ast_str_to_uuid(uuid_str);
|
||||||
|
if (!uuid2) {
|
||||||
|
ast_test_status_update(test, "Unable to convert string %s to UUID\n", uuid_str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure the UUIDs are identical */
|
||||||
|
if (ast_uuid_compare(uuid1, uuid2) != 0) {
|
||||||
|
ast_test_status_update(test, "UUIDs that should be identical are different\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try copying a UUID */
|
||||||
|
uuid3 = ast_uuid_copy(uuid1);
|
||||||
|
if (!uuid3) {
|
||||||
|
ast_test_status_update(test, "Unable to copy UUID\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure copied UUIDs are identical */
|
||||||
|
if (ast_uuid_compare(uuid1, uuid3) != 0) {
|
||||||
|
ast_test_status_update(test, "UUIDs that should be identical are different\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_uuid_compare(uuid2, uuid3) != 0) {
|
||||||
|
ast_test_status_update(test, "UUIDs that should be identical are different\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear a UUID and ensure that it registers as nil */
|
||||||
|
ast_uuid_clear(uuid1);
|
||||||
|
|
||||||
|
if (!ast_uuid_is_nil(uuid1)) {
|
||||||
|
ast_test_status_update(test, "UUID that was cleared does not appear to be nil\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = AST_TEST_PASS;
|
||||||
|
|
||||||
|
end:
|
||||||
|
ast_free(uuid1);
|
||||||
|
ast_free(uuid2);
|
||||||
|
ast_free(uuid3);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unload_module(void)
|
||||||
|
{
|
||||||
|
AST_TEST_UNREGISTER(uuid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int load_module(void)
|
||||||
|
{
|
||||||
|
AST_TEST_REGISTER(uuid);
|
||||||
|
return AST_MODULE_LOAD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "UUID test module");
|
Loading…
Reference in new issue