summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/uuid.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/slapd/uuid.c')
-rw-r--r--ldap/servers/slapd/uuid.c893
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 (&timestamp, &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 (&timestamp);
+
+ /* 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(&timestamp);
+ 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));
+}