/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 *
 * License: GPL (version 3 or any later version).
 * See LICENSE for details.
 * END COPYRIGHT BLOCK **/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/* 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>
#include <sys/types.h>
#include <sys/time.h>

#ifdef HAVE_SYS_SYSINFO_H
#include <sys/sysinfo.h>
#endif

#include <sys/utsname.h>
#include <unistd.h> /* gethostname() */
#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
#define SEED_LENGTH 16

/* 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(void);
/* writes state to dit */
static int write_state_to_entry(PRBool newState);
/* add state entry to the dit */
static int add_state_entry(void);
/* modify state entry in the dit */
static int modify_state_entry(void);
/* generates timestamp for the next uuid - single threaded */
static uuid_time_t update_time(void);
/* 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[], size_t arraylen);

/* 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_err(SLAPI_LOG_ERR, 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_err(SLAPI_LOG_ERR, 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_err(SLAPI_LOG_ERR, 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_err(SLAPI_LOG_WARNING, MODULE,
                          "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_err(SLAPI_LOG_ERR, 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_rel(uuid_update_state, NULL, slapi_current_rel_time_t(),
                            UPDATE_INTERVAL);

    _state.initialized = PR_TRUE;
    return UUID_SUCCESS;
}

/* uuid_cleanup -- saves state, destroys generator data */
void
uuid_cleanup(void)
{
    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.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
            (unsigned int)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] = {0};
    unsigned int hashLen;
    guid_t net_nsid = {0}; /* context UUID in network byte order */

    /* put name space ID in network byte order so it hashes the same
        no matter what endian machine we're on */

    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 = 0;
    unsigned16 clock_seq = 0;

    /* just bumps time sequence number. the actual
     * time calls are made by a uuid_update_state */
    if (update_time_mt(&timestamp, &clock_seq) == UUID_TIME_ERROR) {
        slapi_log_err(SLAPI_LOG_ERR, 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 __attribute__((unused)), void *arg __attribute__((unused)))
{
    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_err(SLAPI_LOG_ERR, 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_err(SLAPI_LOG_ERR, MODULE, "read_state: "
                                                 "memory allocation failed.\n");
            return (UUID_MEMORY_ERROR);
        }

        strcpy(path, STATE_FILE);
    } else {
        path = slapi_ch_smprintf("%s/%s", configDir, STATE_FILE);
        if (path == NULL) {
            slapi_log_err(SLAPI_LOG_ERR, MODULE, "read_state: "
                                                 "memory allocation failed.\n");
            return (UUID_MEMORY_ERROR);
        }
    }

    /* 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_err(SLAPI_LOG_ERR, 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_err(SLAPI_LOG_ERR, 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_err(SLAPI_LOG_ERR, 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_err(SLAPI_LOG_ERR, 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(void)
{
    int rt;

    rt = PR_Seek(_state.fd, 0, PR_SEEK_SET);
    if (rt == -1) {
        PRErrorCode prerr = PR_GetError();
        slapi_log_err(SLAPI_LOG_ERR, 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_err(SLAPI_LOG_ERR, 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(void)
{
    struct berval *vals[2];
    struct berval val;
    Slapi_Entry *e;
    Slapi_PBlock *pb = NULL;
    int rt;

    vals[0] = &val;
    vals[1] = NULL;

    e = slapi_entry_alloc();
    slapi_entry_set_sdn(e, _state.configDN);

    /* 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_err(SLAPI_LOG_ERR, 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_pblock_destroy(pb);
    }

    if (rt != LDAP_SUCCESS) {
        slapi_log_err(SLAPI_LOG_ERR, MODULE, "add_state_entry: "
                                             "add operation failed; LDAP error - %d.\n",
                      rt);
        return UUID_LDAP_ERROR;
    }

    slapi_log_err(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(void)
{
    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_err(SLAPI_LOG_ERR, 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_err(SLAPI_LOG_ERR, MODULE, "modify_state_entry: "
                                             "update operation failed; LDAP error - %d.\n",
                      res);

        return UUID_LDAP_ERROR;
    }

    slapi_log_err(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(void)
{
    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_err(SLAPI_LOG_ERR, 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));
}

/* when converting broken values, we may need to swap the bytes */
#define BSWAP16(x) ((((x) >> 8) & 0xff) | (((x)&0xff) << 8))
#define BSWAP32(x) ((((x)&0xff000000) >> 24) | (((x)&0x00ff0000) >> 8) | \
                    (((x)&0x0000ff00) << 8) | (((x)&0x000000ff) << 24))

/* 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])
{
    char *use_broken_uuid = getenv("USE_BROKEN_UUID");
    /* Construct a version 3 uuid with the (pseudo-)random number
    * plus a few constants. */

    memcpy(uuid, hash, sizeof(guid_t));

    /* when migrating, we skip the ntohl in order to read in old,
       incorrectly formatted uuids */
    if (!use_broken_uuid || (*use_broken_uuid == '0')) {
        /* convert UUID to local byte order */
        uuid->time_low = PR_ntohl(uuid->time_low);
        uuid->time_mid = PR_ntohs(uuid->time_mid);
        uuid->time_hi_and_version = PR_ntohs(uuid->time_hi_and_version);
    } else {
#if defined(IS_BIG_ENDIAN)
        /* convert UUID to b0rken byte order */
        uuid->time_low = BSWAP32(uuid->time_low);
        uuid->time_mid = BSWAP16(uuid->time_mid);
        uuid->time_hi_and_version = BSWAP16(uuid->time_hi_and_version);
#endif
    }

    /* 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[SEED_LENGTH] = {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_LENGTH);
    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 = slapi_current_utc_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 = (uuid_time_t)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[], size_t arraylen)
{
    slapi_rand_array(seed, arraylen);
}
