diff options
Diffstat (limited to 'ldap/servers/slapd/uuid.c')
-rw-r--r-- | ldap/servers/slapd/uuid.c | 893 |
1 files changed, 893 insertions, 0 deletions
diff --git a/ldap/servers/slapd/uuid.c b/ldap/servers/slapd/uuid.c new file mode 100644 index 00000000..47576559 --- /dev/null +++ b/ldap/servers/slapd/uuid.c @@ -0,0 +1,893 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* uuid.c */ + +/* +** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & +** Digital Equipment Corporation, Maynard, Mass. +** Copyright (c) 1998 Microsoft. +** To anyone who acknowledges that this file is provided "AS IS" +** without any express or implied warranty: permission to use, copy, +** modify, and distribute this file for any purpose is hereby +** granted without fee, provided that the above copyright notices and +** this notice appears in all source code copies, and that none of +** the names of Open Software Foundation, Inc., Hewlett-Packard +** Company, or Digital Equipment Corporation be used in advertising +** or publicity pertaining to distribution of the software without +** specific, written prior permission. Neither Open Software +** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment +* Corporation makes any representations about the suitability of +** this software for any purpose. +*/ + + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <pk11func.h> + +#ifdef _WIN32 +#include <sys/stat.h> /* for S_IREAD and S_IWRITE */ +#else +#include <sys/types.h> +#include <sys/time.h> +#include <sys/sysinfo.h> +#include <sys/utsname.h> +#include <unistd.h> /* gethostname() */ +#endif + + +#include "slap.h" +#include "uuid.h" +#include "sechash.h" + +#define SEQ_PER_SEC 10000000 /* number of 100ns intervals in a sec */ +#define STATE_FILE "state" /* file that contains generator's state */ +#define STATE_ATTR "nsState" /* attribute that stores state info */ +#define MODULE "uuid" /* for logging */ +#define UPDATE_INTERVAL 60000 /* 1 minute */ +#define NEED_TIME_UPDATE -1 + +/* generates uuid in singlethreaded environment */ +static int uuid_create_st(guid_t *uuid); +/* generates uuid in multithreaded environment */ +static int uuid_create_mt(guid_t *uuid); +/* periodically called to update generator's state - mt case only */ +static void uuid_update_state (time_t when, void *arg); +/* creates uuid in v1 format using current state info */ +static void format_uuid_v1(guid_t *uuid, uuid_time_t timestamp, unsigned16 clock_seq); +/* generates uuid in v3 format */ +static void format_uuid_v3(guid_t *uuid, unsigned char hash[16]); +/* reads state from a file or DIT entry */ +static int read_state (const char *configDir, const Slapi_DN *configDN, PRBool *newState); +/* reads state from a file */ +static int read_state_from_file (const char *configDir); +/* read state information from DIT */ +static int read_state_from_entry (const Slapi_DN *configDN); +/* writes state to persistent store: file or dit */ +static int write_state(PRBool newState); +/* writes state to a file */ +static int write_state_to_file(); +/* writes state to dit */ +static int write_state_to_entry(PRBool newState); +/* add state entry to the dit */ +static int add_state_entry (); +/* modify state entry in the dit */ +static int modify_state_entry (); +/* generates timestamp for the next uuid - single threaded */ +static uuid_time_t update_time(); +/* generates timestamp for the next uuid - multithreaded threaded */ +static int update_time_mt(uuid_time_t *timestamp, unsigned16 *clock_seq); +/* retrieves or generates nodeid */ +static int get_node_identifier(uuid_node_t *node); +/* returns current time in the UTC format */ +static void get_system_time(uuid_time_t *uuid_time); +/* generates random value - used to set clock sequence */ +static unsigned16 true_random(void); +/* generate random info buffer to generate nodeid */ +static void get_random_info(unsigned char seed[16]); + +/* UUID generator state stored persistently */ +typedef struct +{ + uuid_time_t timestamp; /* saved timestamp */ + uuid_node_t node; /* saved node ID */ + unsigned16 clockseq; /* saved clock sequence */ + unsigned8 last_update;/* flags the update made during server sutdown */ +} uuid_gen_state; + +/* generator state plus data to support it */ +typedef struct +{ + uuid_gen_state genstate; /* generator state */ + int time_seq; /* sequence number to account for clock + granularity; not written to disk */ + PRBool initialized; /* uniqueid successfully initialized */ + PRBool mtGen; /* multithreaded generation */ + PRLock *lock; /* lock to protect state */ + PRFileDesc *fd; /* fd for the state file */ + Slapi_DN *configDN; /* db entry that contains state info */ +} uuid_state; + +static unsigned int uuid_seed = 0; /* seed for the random generator */ + + uuid_state _state; /* generator's state */ + +/* uuid_init -- initializes uuid layer */ +int uuid_init (const char *configDir, const Slapi_DN *configDN, PRBool mtGen) +{ + int rt; + PRBool newState = PR_FALSE; + + if (_state.initialized) + { + slapi_log_error (SLAPI_LOG_FATAL, MODULE, + "uuid_init: generator is already initialized\n"); + return UUID_SUCCESS; + } + + memset (&_state, 0, sizeof (_state)); + + /* get saved state */ + rt = read_state(configDir, configDN, &newState); + if (rt != UUID_SUCCESS) + { + slapi_log_error (SLAPI_LOG_FATAL, MODULE, + "uuid_init: failed to get generator's state\n"); + uuid_cleanup (); + return rt; + } + + _state.mtGen = mtGen; + + /* this is multithreaded generation - create lock */ + if (_state.mtGen) + { + _state.lock = PR_NewLock(); + if (!_state.lock) + { + PRErrorCode prerr = PR_GetError(); + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uuid_init: " + "failed to create state lock; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s).\n", + prerr, slapd_pr_strerror(prerr)); + uuid_cleanup (); + return UUID_LOCK_ERROR; + } + } + + /* save the state */ + rt = write_state(newState); + /* can't proceede if the state can't be written */ + if (rt != UUID_SUCCESS) + { + if (slapi_config_get_readonly() && + (rt == UUID_LDAP_ERROR)) { + /* + * If server is readonly and error is UUID_LDAP_ERROR + * we can continue. + */ + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "Warning: " + "The server is in read-only mode, therefore the unique ID generator cannot be used. " + "Do not use this server in any replication agreement\n"); + } + else { + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uuid_init: " + "failed to save generator's state.\n"); + uuid_cleanup (); + return rt; + } + } + + /* schedule update task for multithreaded generation */ + if (_state.mtGen) + slapi_eq_repeat(uuid_update_state, NULL, (time_t)0, UPDATE_INTERVAL); + + _state.initialized = PR_TRUE; + return UUID_SUCCESS; +} + +/* uuid_cleanup -- saves state, destroys generator data */ +void uuid_cleanup () +{ + if (_state.initialized) + { + _state.genstate.last_update = 1; + write_state (PR_FALSE); + } + + if (_state.lock) + PR_DestroyLock (_state.lock); + + if (_state.fd) + PR_Close (_state.fd); + + if (_state.configDN) + slapi_sdn_free(&_state.configDN); + + memset (&_state, 0, sizeof (_state)); +} + +/* uuid_create - main generating function */ +int uuid_create(guid_t *uuid) +{ + if (_state.mtGen) + return uuid_create_mt(uuid); + else + return uuid_create_st(uuid); +} + +/* uuid_compare -- Compare two UUID's "lexically" and return + -1 u1 is lexically before u2 + 0 u1 is equal to u2 + 1 u1 is lexically after u2 + + Note: lexical ordering is not temporal ordering! +*/ +#define CHECK(f1, f2) if (f1 != f2) return f1 < f2 ? -1 : 1; +int uuid_compare(const guid_t *u1, const guid_t *u2) +{ + int i; + + CHECK(u1->time_low, u2->time_low); + CHECK(u1->time_mid, u2->time_mid); + CHECK(u1->time_hi_and_version, u2->time_hi_and_version); + CHECK(u1->clock_seq_hi_and_reserved, u2->clock_seq_hi_and_reserved); + CHECK(u1->clock_seq_low, u2->clock_seq_low) + for (i = 0; i < 6; i++) + { + if (u1->node[i] < u2->node[i]) + return -1; + if (u1->node[i] > u2->node[i]) + return 1; + } + return 0; +} + +/* uuid_format -- converts UUID to its string representation + buffer should be at list 40 bytes long +*/ + +void uuid_format(const guid_t *u, char *buff) +{ + sprintf(buff, "%8.8lx-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", + u->time_low, u->time_mid, u->time_hi_and_version, + u->clock_seq_hi_and_reserved, u->clock_seq_low, u->node[0], + u->node[1], u->node[2], u->node[3], u->node[4], u->node[5]); +} + +/* uuid_create_from_name -- create a UUID using a "name" from a "name space" + */ +void uuid_create_from_name(guid_t * uuid, /* resulting UUID */ + const guid_t nsid, /* UUID to serve as context, so identical + names from different name spaces generate + different UUIDs */ + const void * name, /* the name from which to generate a UUID */ + int namelen) /* the length of the name */ +{ + PK11Context *c = NULL; + + unsigned char hash[16]; + unsigned int hashLen; + guid_t net_nsid; /* context UUID in network byte order */ + + memset(hash, 0, 16); + + /* put name space ID in network byte order so it hashes the same + no matter what endian machine we're on */ + + memset(&net_nsid, 0, sizeof(guid_t)); + net_nsid.time_low = PR_htonl(nsid.time_low); + net_nsid.time_mid = PR_htons(nsid.time_mid); + net_nsid.time_hi_and_version = PR_htons(nsid.time_hi_and_version); + net_nsid.clock_seq_hi_and_reserved=nsid.clock_seq_hi_and_reserved; + net_nsid.clock_seq_low=nsid.clock_seq_low; + strncpy((char *)net_nsid.node, (char *)nsid.node, 6); + + c = PK11_CreateDigestContext(SEC_OID_MD5); + if (c != NULL) { + PK11_DigestBegin(c); + PK11_DigestOp(c, (unsigned char *)&net_nsid, sizeof(net_nsid)); + PK11_DigestOp(c, (unsigned char *)name, namelen); + PK11_DigestFinal(c, hash, &hashLen, 16); + + /* the hash is in network byte order at this point */ + format_uuid_v3(uuid, hash); + + PK11_DestroyContext(c, PR_TRUE); + } + else { /* Probably desesperate but at least deterministic... */ + memset(&uuid, 0, sizeof(uuid)); + } +} + +/* Helper Functions */ + +/* uuid_create_st -- singlethreaded generation */ +static int uuid_create_st(guid_t *uuid) +{ + uuid_time_t timestamp; + + /* generate new time and save it in the state */ + timestamp = update_time (); + + /* stuff fields into the UUID */ + format_uuid_v1(uuid, timestamp, _state.genstate.clockseq); + + return UUID_SUCCESS; +} + +/* uuid_create -- multithreaded generation */ +static int uuid_create_mt(guid_t *uuid) +{ + uuid_time_t timestamp; + unsigned16 clock_seq; + + /* just bumps time sequence number. the actual + time calls are made by a uuid_update_state */ + update_time_mt (×tamp, &clock_seq); + + if (timestamp == NEED_TIME_UPDATE) + { + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uuid_create_mt: generator ran " + "out of sequence numbers.\n"); + return UUID_TIME_ERROR; + } + + /* stuff fields into UUID */ + format_uuid_v1(uuid, timestamp, clock_seq); + + return UUID_SUCCESS; +} + +/* uuid_update_state -- called periodically to update generator's state + (multithreaded case only) + */ +static void uuid_update_state (time_t when, void *arg) +{ + uuid_time_t timestamp; + + get_system_time (×tamp); + + /* time has not changed since last call - return */ + if (timestamp == _state.genstate.timestamp) + return; + + PR_Lock (_state.lock); + /* clock was set backward - insure uuid uniquness by incrementing clock sequence */ + if (timestamp < _state.genstate.timestamp) + _state.genstate.clockseq ++; + + _state.genstate.timestamp = timestamp; + _state.time_seq = 0; + + PR_Unlock (_state.lock); +} + +/* read_state -- read UUID generator state from non-volatile store. +*/ +static int read_state(const char *configDir, const Slapi_DN *configDN, PRBool *newState) +{ + uuid_time_t timestamp; + int rt; + + if (configDN) + rt = read_state_from_entry (configDN); + else + rt = read_state_from_file (configDir); + + if (rt == UUID_NOTFOUND) + *newState = PR_TRUE; + else + *newState = PR_FALSE; + + if (rt != UUID_SUCCESS && rt != UUID_NOTFOUND) /* fatal error - bail out */ + { + slapi_log_error (SLAPI_LOG_FATAL, MODULE, + "read_state: failed to get generator's state\n"); + return rt; + } + + /* get current time and nodeid */ + get_system_time(×tamp); + if (*newState) /* state info is missing - generate */ + { + get_node_identifier (&_state.genstate.node); + _state.genstate.clockseq = true_random(); + } + else if(_state.genstate.last_update != 1) + { + /* clock sequence should be randomized and not just incremented + because server's clock could have been set back before the + server crashed in which case clock sequence was incremented */ + _state.genstate.clockseq = true_random(); + } + else if (timestamp <= _state.genstate.timestamp) + { + _state.genstate.clockseq ++; + } + + _state.genstate.timestamp = timestamp; + _state.time_seq = 0; + /* need to clear this field so that we know if the state information + is written during shutdown (in which case this flag is set to 1 */ + _state.genstate.last_update = 0; + + return UUID_SUCCESS; +} + +/* read_state_from_file -- read generator state from file. +*/ +static int read_state_from_file (const char *configDir) +{ + char *path; + int rt; + + if (configDir == NULL || configDir[0] == '\0') + { /* this directory */ + path = (char*)slapi_ch_malloc(strlen (STATE_FILE) + 1); + if (path == NULL) + { + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state: " + "memory allocation failed.\n"); + return (UUID_MEMORY_ERROR); + } + + strcpy (path, STATE_FILE); + } + else + { + path = (char*)slapi_ch_malloc(strlen(configDir) + strlen(STATE_FILE) + 2); + if (path == NULL) + { + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state: " + "memory allocation failed.\n"); + return (UUID_MEMORY_ERROR); + } + sprintf (path, "%s/%s", configDir, STATE_FILE); + } + + /* open or create state file for read/write and keep it in sync */ + _state.fd = PR_Open(path, PR_RDWR | PR_CREATE_FILE | PR_SYNC, + SLAPD_DEFAULT_FILE_MODE); + slapi_ch_free ((void**)&path); + if (!_state.fd) + { + PRErrorCode prerr = PR_GetError(); + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state: " + "failed to open state file - %s; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s).\n", + path, prerr, slapd_pr_strerror(prerr)); + return (UUID_IO_ERROR); + } + + rt = PR_Read (_state.fd, &_state.genstate, sizeof(uuid_gen_state)); + if (rt == 0) /* new state */ + { + return UUID_NOTFOUND; + } + + if (rt == -1) + { + PRErrorCode prerr = PR_GetError(); + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state: " + "failed to read state information; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s).\n", + prerr, slapd_pr_strerror(prerr)); + return (UUID_IO_ERROR); + } + + return(UUID_SUCCESS); +} + +/* read_state_from_entry -- read generator state from DIT. +*/ +static int read_state_from_entry (const Slapi_DN *configDN) +{ + Slapi_PBlock *pb; + int res, rt; + Slapi_Entry **entries; + Slapi_Attr *attr; + Slapi_Value *value; + const struct berval *bv; + + _state.configDN = slapi_sdn_dup (configDN); + pb = slapi_search_internal(slapi_sdn_get_ndn (configDN), LDAP_SCOPE_BASE, + "objectclass=*", NULL, NULL, 0); + + if (pb == NULL) + { + /* the only time NULL pb is returned is when memory allocation fails */ + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state_from_entry: " + "NULL pblock returned from search\n"); + return UUID_MEMORY_ERROR; + } + + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res); + + if (res == LDAP_NO_SUCH_OBJECT) + { + rt = UUID_NOTFOUND; + goto done; + } + + if (res != LDAP_SUCCESS) + { + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state_from_entry: " + "search operation failed; LDAP error - %d\n", res); + rt = UUID_LDAP_ERROR; + goto done; + } + + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); + if (entries == NULL || entries[0] == NULL) + { + rt = UUID_UNKNOWN_ERROR; + goto done; + } + + /* get state info */ + rt = slapi_entry_attr_find (entries[0], STATE_ATTR, &attr); + if (rt != LDAP_SUCCESS) + { + rt = UUID_FORMAT_ERROR; + goto done; + } + + slapi_attr_first_value(attr,&value); + if (value == NULL) + { + rt = UUID_FORMAT_ERROR; + goto done; + } + + bv = slapi_value_get_berval(value); + if (bv == NULL || bv->bv_val == NULL || bv->bv_len != sizeof (_state.genstate)) + { + rt = UUID_FORMAT_ERROR; + goto done; + } + + memcpy (&(_state.genstate), bv->bv_val, bv->bv_len); + +done:; + if (pb) + { + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); + } + + return rt; +} + +/* write_state -- save UUID generator state back to non-volatile + storage. Writes immediately to the disk +*/ + +static int write_state (PRBool newState) +{ + if (_state.configDN) /* write to DIT */ + return write_state_to_entry (newState); + else /* write to a file */ + return write_state_to_file (); +} + +/* write_state_to_file -- stores state to state file +*/ +static int write_state_to_file() +{ + int rt; + + rt = PR_Seek (_state.fd, 0, PR_SEEK_SET); + if (rt == -1) + { + PRErrorCode prerr = PR_GetError(); + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "write_state: " + "failed to rewind state file; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s).\n", + prerr, slapd_pr_strerror(prerr)); + return UUID_IO_ERROR; + } + + rt = PR_Write (_state.fd, &_state.genstate, sizeof (uuid_gen_state)); + if (rt == -1) + { + PRErrorCode prerr = PR_GetError(); + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "write_state: " + "failed to update state file; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s).\n", + prerr, slapd_pr_strerror(prerr)); + + return (UUID_IO_ERROR); + } + + return (UUID_SUCCESS); +} + +/* write_state_to_entry -- stores state to state file +*/ +static int write_state_to_entry(PRBool newState) { + if (newState) + return add_state_entry (); + else + return modify_state_entry (); +} + +/* add_state_entry -- add state entry to the dit */ +static int add_state_entry () +{ + struct berval *vals[2]; + struct berval val; + Slapi_Entry *e; + Slapi_PBlock *pb = NULL; + const char *dn = slapi_sdn_get_ndn (_state.configDN); + int rt; + + vals[0] = &val; + vals[1] = NULL; + + e = slapi_entry_alloc(); + slapi_entry_set_dn(e, slapi_ch_strdup(dn)); + + /* Set the objectclass attribute */ + val.bv_val = "top"; + val.bv_len = strlen (val.bv_val); + slapi_entry_add_values(e, "objectclass", vals); + + val.bv_val = "extensibleObject"; + val.bv_len = strlen (val.bv_val); + slapi_entry_add_values(e, "objectclass", vals); + + /* Set state attribute */ + val.bv_val = (char*)&(_state.genstate); + val.bv_len = sizeof (_state.genstate); + slapi_entry_add_values(e, STATE_ATTR, vals); + + /* this operation frees the entry */ + pb = slapi_add_entry_internal(e, 0, 0 /* log_change */); + if (pb == NULL) + { + /* the only time NULL pb is returned is when memory allocation fails */ + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "add_state_entry: " + "NULL pblock returned from search\n"); + return UUID_MEMORY_ERROR; + } + else + { + slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rt); + slapi_ch_free((void **) &pb); + } + + if (rt != LDAP_SUCCESS) + { + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "add_state_entry: " + "add operation failed; LDAP error - %d.\n", rt); + return UUID_LDAP_ERROR; + } + + slapi_log_error (SLAPI_LOG_HOUSE, MODULE, "add_state_entry: " + "successfully added generator's state entry"); + + return UUID_SUCCESS; +} + +/* modify_state_entry -- modify state entry in the dit */ +static int modify_state_entry () +{ + int res; + Slapi_Mods mods; + struct berval *vals[2]; + struct berval val; + Slapi_PBlock *pb; + + val.bv_val = (char*)&(_state.genstate); + val.bv_len = sizeof (_state.genstate); + vals[0] = &val; + vals[1] = NULL; + + slapi_mods_init (&mods, 1); + slapi_mods_add_modbvps(&mods, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, STATE_ATTR, vals); + pb = slapi_modify_internal(slapi_sdn_get_ndn (_state.configDN), + slapi_mods_get_ldapmods_byref(&mods), NULL, 0); + slapi_mods_done(&mods); + + if (pb == NULL) + { + /* the only time NULL pb is returned is when memory allocation fails */ + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "modify_state_entry: " + "NULL pblock returned from search\n"); + return UUID_MEMORY_ERROR; + } + + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res); + slapi_pblock_destroy(pb); + if (res != LDAP_SUCCESS) + { + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "modify_state_entry: " + "update operation failed; LDAP error - %d.\n", res); + + return UUID_LDAP_ERROR; + } + + slapi_log_error (SLAPI_LOG_HOUSE, MODULE, "modify_state_entry: " + "successfully updated generator's state entry"); + return UUID_SUCCESS; +} + +/* update_time -- updates time portion of the generators state + for singlethreaded generation +*/ +static uuid_time_t update_time() +{ + uuid_time_t time_now; + + get_system_time(&time_now); + + /* time was turned back - need to change clocksequence */ + if (time_now < _state.genstate.timestamp) + { + _state.genstate.clockseq ++; + _state.genstate.timestamp = time_now; + _state.time_seq = 0; + return _state.genstate.timestamp; + } + + /* go into loop if the time has not changed since last call */ + while (time_now == _state.genstate.timestamp) + { + /* if we still have sequence numbers to give to the + timestamp, use it and get out of the loop */ + if (_state.time_seq < SEQ_PER_SEC - 1) + { + _state.time_seq ++; + break; + } + + /* this should never happen because we don't generate more that 10 mln ids/sec */ + DS_Sleep (PR_MillisecondsToInterval(500)); + get_system_time(&time_now); + } + + /* system time has changed - clear sequence number and + update last time */ + if (time_now > _state.genstate.timestamp) + { + _state.time_seq = 0; + _state.genstate.timestamp = time_now; + } + + return _state.genstate.timestamp + _state.time_seq; +} + +/* update_time_mt -- this function updates time sequence part of generators state. + This function should be used in the multithreaded environment + only. +*/ +static int update_time_mt (uuid_time_t *timestamp, unsigned16 *clock_seq) +{ + + PR_Lock (_state.lock); + + /* we ran out time sequence numbers because + uuid_update_state function is not called + frequently enough */ + if (_state.time_seq >= SEQ_PER_SEC - 1) + { + _state.time_seq = NEED_TIME_UPDATE; + slapi_log_error (SLAPI_LOG_FATAL, MODULE, "update_time_mt: " + "ran out of time sequence numbers; " + "uuid_update_state must be called\n"); + + PR_Unlock (_state.lock); + return (UUID_TIME_ERROR); + } + + _state.time_seq++; + + *timestamp = _state.genstate.timestamp + _state.time_seq; + *clock_seq = _state.genstate.clockseq; + + PR_Unlock (_state.lock); + + return UUID_SUCCESS; +} + +/* format_uuid_v1 -- make a UUID from the timestamp, clockseq, + and node ID +*/ +static void format_uuid_v1(guid_t * uuid, uuid_time_t timestamp, unsigned16 clock_seq) +{ + /* Construct a version 1 uuid with the information we've gathered + * plus a few constants. */ + + uuid->time_low = (unsigned32)(timestamp & 0xFFFFFFFF); + uuid->time_mid = (unsigned16)((timestamp >> 32) & 0xFFFF); + uuid->time_hi_and_version = (unsigned16) + ((timestamp >> 48) & 0x0FFF); + uuid->time_hi_and_version |= (1 << 12); + uuid->clock_seq_low = clock_seq & 0xFF; + uuid->clock_seq_hi_and_reserved = (unsigned8)(clock_seq & 0x3F00) >> 8; + uuid->clock_seq_hi_and_reserved |= 0x80; + memcpy(&uuid->node, &_state.genstate.node, sizeof (uuid->node)); +} + +/* format_uuid_v3 -- make a UUID from a (pseudo)random 128 bit number +*/ +static void format_uuid_v3(guid_t * uuid, unsigned char hash[16]) +{ + /* Construct a version 3 uuid with the (pseudo-)random number + * plus a few constants. */ + + memcpy(uuid, hash, sizeof(guid_t)); + + /* convert UUID to local byte order */ + ntohl(uuid->time_low); + ntohs(uuid->time_mid); + ntohs(uuid->time_hi_and_version); + + /* put in the variant and version bits */ + uuid->time_hi_and_version &= 0x0FFF; + uuid->time_hi_and_version |= (3 << 12); + uuid->clock_seq_hi_and_reserved &= 0x3F; + uuid->clock_seq_hi_and_reserved |= 0x80; +} + +/* system dependent call to get IEEE node ID. + This sample implementation generates a random node ID + Assumes that configDir was tested for validity by + the higher layer +*/ +static int get_node_identifier (uuid_node_t *node) +{ + unsigned char seed[16]= {0}; + +#ifdef USE_NIC + /* ONREPL - code to use NIC address would go here; Currently, we use + cryptographic random number to avoid state sharing among + servers running on the same host. See UniqueID Generator + docs for more info. + */ +#endif + get_random_info(seed); + seed[0] |= 0x80; + memcpy (node, seed, sizeof (uuid_node_t)); + + return UUID_SUCCESS; +} + +/* call to get the current system time. Returned as 100ns ticks + since Oct 15, 1582, but resolution may be less than 100ns. +*/ + +static void get_system_time(uuid_time_t *uuid_time) +{ + time_t cur_time; + + cur_time = current_time (); + + /* Offset between UUID formatted times and time() formatted times. + UUID UTC base time is October 15, 1582. time() base time is January 1, 1970.*/ + *uuid_time = cur_time * SEQ_PER_SEC + I64(0x01B21DD213814000); +} + +/* ONREPL */ + +/* true_random -- generate a crypto-quality random number. +*/ +static unsigned16 true_random(void) +{ + static int inited = 0; + + if (!inited) + { + uuid_seed = slapi_rand(); + inited = 1; + } + + return (slapi_rand_r(&uuid_seed)); +} + +static void get_random_info(unsigned char seed[16]) +{ + slapi_rand_array(seed, sizeof(seed)); +} |