Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Enduro/X Cluster ID generator
0003  *
0004  * @file cid.c
0005  */
0006 /* -----------------------------------------------------------------------------
0007  * Enduro/X Middleware Platform for Distributed Transaction Processing
0008  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0009  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0010  * This software is released under one of the following licenses:
0011  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0012  * See LICENSE file for full text.
0013  * -----------------------------------------------------------------------------
0014  * AGPL license:
0015  *
0016  * This program is free software; you can redistribute it and/or modify it under
0017  * the terms of the GNU Affero General Public License, version 3 as published
0018  * by the Free Software Foundation;
0019  *
0020  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0021  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0022  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0023  * for more details.
0024  *
0025  * You should have received a copy of the GNU Affero General Public License along 
0026  * with this program; if not, write to the Free Software Foundation, Inc.,
0027  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0028  *
0029  * -----------------------------------------------------------------------------
0030  * A commercial use license is available from Mavimax, Ltd
0031  * contact@mavimax.com
0032  * -----------------------------------------------------------------------------
0033  */
0034 
0035 /*---------------------------Includes-----------------------------------*/
0036 
0037 
0038 #include <ndrstandard.h>
0039 #include <sys/stat.h>
0040 #include <unistd.h>
0041 #include <fcntl.h>
0042 #include <stdlib.h>
0043 
0044 #include <ndebug.h>
0045 #include <sys/time.h>
0046 #include <nstdutil.h>
0047 /*---------------------------Externs------------------------------------*/
0048 /*---------------------------Macros-------------------------------------*/
0049 /*---------------------------Enums--------------------------------------*/
0050 /*---------------------------Typedefs-----------------------------------*/
0051 /*---------------------------Globals------------------------------------*/
0052 /*---------------------------Statics------------------------------------*/
0053 
0054 exprivate MUTEX_LOCKDECL(M_uuid_lock); /**< Lock the random generator */
0055 exprivate volatile unsigned int M_seedp;
0056 exprivate volatile unsigned int M_counter;
0057 exprivate volatile unsigned int M_init_done=EXFALSE;
0058 /*---------------------------Prototypes---------------------------------*/
0059 
0060 /**
0061  * Get random bytes... (if available)
0062  * @param output where to unload good random
0063  * @param number bytes to unload
0064  * @return EXSUCCEED/EXFAIL
0065  */
0066 exprivate int ndrx_get_rnd_bytes(unsigned char *output, size_t len)
0067 {
0068     int ret = EXSUCCEED;
0069     int fd=EXFAIL, flags, i;
0070 
0071     fd = open("/dev/urandom", O_RDONLY);
0072     
0073     if (fd == EXFAIL)
0074     {
0075         fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
0076     }
0077     
0078     if (fd >= 0)
0079     {
0080         flags = fcntl(fd, F_GETFD);
0081         
0082         if (flags >= 0)
0083         {
0084             fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
0085         }
0086     }
0087     else
0088     {
0089         EXFAIL_OUT(ret);
0090     }
0091     
0092     for (i=0; i<len; i++)
0093     {
0094         output[i]=0;
0095         if (EXSUCCEED!=read(fd, output+i, 1))
0096         {
0097             /* nothing todo... */
0098         }
0099         
0100     }
0101     
0102 out:
0103     
0104     if (EXFAIL!=fd)
0105     {
0106         close(fd);
0107     }
0108 
0109     return ret;
0110 }
0111 
0112 /**
0113  * Initialize random seed
0114  */
0115 expublic void ndrx_rand_seedset(unsigned int *seed)
0116 {
0117     struct timeval  tv;
0118     unsigned char buf[sizeof(*seed)];
0119     int i;
0120     char *p;
0121 
0122     gettimeofday(&tv, 0);
0123     *seed = (getpid() << 16) ^ getuid() ^ (unsigned int)tv.tv_sec ^ (unsigned int)tv.tv_usec;
0124 
0125     /* Randomize seed counter.. */
0126     if (EXSUCCEED==ndrx_get_rnd_bytes(buf, sizeof(*seed)))
0127     {
0128         p = (char *)seed;
0129         for (i=0; i<sizeof(*seed); i++)
0130         {
0131             p[i] = p[i] ^ buf[i];
0132         }
0133     }
0134 }
0135 
0136 /**
0137  * Init UUID generator (once!)
0138  */
0139 exprivate void ndrx_cid_init(void)
0140 {
0141     unsigned int locl_seedp;
0142     unsigned char buf[sizeof(unsigned int)];
0143     unsigned char *p;
0144     int i;
0145     
0146     ndrx_rand_seedset(&locl_seedp);
0147     M_counter = rand_r(&locl_seedp);
0148     
0149     /* Randomize counter.. */
0150     if (EXSUCCEED==ndrx_get_rnd_bytes(buf, sizeof(unsigned int)))
0151     {
0152         p = (unsigned char *)&M_counter;
0153         for (i=0; i<sizeof(unsigned int); i++)
0154         {
0155             p[i] = p[i] ^ buf[i];
0156         }
0157     }
0158     M_seedp = locl_seedp;
0159 }
0160 
0161 /**
0162  * Generate random CID (cluster ID)
0163  * @param prefix Node id prefix
0164  * @param out value to fill
0165  */
0166 expublic void ndrx_cid_generate(unsigned char prefix, exuuid_t out)
0167 {
0168     int i;
0169     unsigned int counter;
0170     unsigned short rnd;
0171     char *out_p = (char *)out;
0172     unsigned pid = getpid();
0173     struct timeval tv;
0174     unsigned int locl_seedp;
0175     
0176     /* perform init... */
0177     if (!M_init_done)
0178     {
0179         MUTEX_LOCK_V(M_uuid_lock);
0180         if (!M_init_done)
0181         {
0182             ndrx_cid_init();
0183             M_init_done = EXTRUE;
0184         }
0185         MUTEX_UNLOCK_V(M_uuid_lock);
0186     }
0187     
0188     /* So node ID 1 byte: */
0189     out_p[0] = prefix;
0190     
0191     /* PID 4x bytes: */
0192     out_p[1] = (pid >>24) & 0xff;
0193     out_p[2] = (pid >>16) & 0xff;
0194     out_p[3] = (pid >>8) & 0xff;
0195     out_p[4] = (pid) & 0xff;
0196     
0197     /* Counter  */
0198     MUTEX_LOCK_V(M_uuid_lock);
0199     
0200     M_counter++;
0201     counter = M_counter;
0202 
0203     locl_seedp=M_seedp;
0204     
0205     rnd=0;
0206     for (i=0; i<sizeof(rnd); i++)
0207     {
0208         rnd<<=8;
0209         rnd|=rand_r(&locl_seedp) & 0xff;
0210     }
0211     
0212     M_seedp=locl_seedp;
0213     
0214     MUTEX_UNLOCK_V(M_uuid_lock);
0215     
0216     /* UTC: time section: */
0217     gettimeofday(&tv, 0);
0218 
0219     /* 16M - counter section */
0220 /*
0221     out_p[5] = (counter >>24) & 0xff;
0222 */
0223     /* added usec randomization: */
0224     out_p[5] = (tv.tv_usec >>7) & 0xff;
0225     out_p[6] = (counter >>16) & 0xff;
0226     out_p[7] = (counter >>8) & 0xff;
0227     out_p[8] = (counter) & 0xff;
0228     
0229     /* UTC: time section: */
0230     gettimeofday(&tv, 0);
0231         
0232     /* Load usec in oldest 7bits with usec, as 1 bit is to solve 2038 problem, 
0233      * so 136 years we guarantee that cid is unique
0234      */
0235     out_p[9] =  (tv.tv_usec & 0xfe) | ((tv.tv_sec >>32) & 0xff);
0236     out_p[10] = (tv.tv_sec >>24) & 0xff;
0237     out_p[11] = (tv.tv_sec >>16) & 0xff;
0238     out_p[12] = (tv.tv_sec >>8) & 0xff;
0239     out_p[13] = (tv.tv_sec) & 0xff;
0240 
0241     /* random number section: */
0242     out_p[14] = (rnd >> 8) & 0xff;
0243     out_p[15] = (rnd) & 0xff;
0244 }
0245 
0246 /* vim: set ts=4 sw=4 et smartindent: */