f4629c39a8ec051861f505fc847652e11c04887b
[openafs.git] / src / util / uuid.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #ifdef KERNEL
12 #include "../afs/param.h"
13 #else
14 #include <afs/param.h>
15 #endif
16
17 RCSID("$Header$");
18
19 #ifdef KERNEL
20 #include "../afs/sysincludes.h"
21 #include "../afs/afsincludes.h"
22 #define uuid_memcmp(A,B,C)      memcmp(A, B, C)
23 #define uuid_memcpy(A,B,C)      memcpy(A, B, C)
24 #else /* KERNEL */
25 #include <stdio.h>
26 #include <errno.h>
27 #ifdef AFS_NT40_ENV
28 #include <winsock2.h>
29 #include <process.h>
30 #else
31 #include <sys/file.h>
32 #include <netinet/in.h>
33 #include <netdb.h>
34 #include <sys/ioctl.h>
35 #include <sys/socket.h>
36 #ifndef ITIMER_REAL
37 #include <sys/time.h>
38 #endif /* ITIMER_REAL */
39 #include <net/if.h>
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #else
43 #ifdef HAVE_STRINGS_H
44 #include <strings.h>
45 #endif
46 #endif
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 #include <stdlib.h>
51 #endif
52 #include <sys/stat.h>
53 #include <fcntl.h>
54 #if !defined(AFS_NT40_ENV) && !defined(AFS_LINUX20_ENV)
55 #include <netinet/if_ether.h>
56 #endif
57 #include "afsutil.h"
58
59 #define uuid_memcmp(A,B,C)      memcmp(A,B,C)
60 #define uuid_memcpy(A,B,C)      memcpy(A,B,C)
61 #endif /* KERNEL */
62
63
64 typedef struct {
65     char eaddr[6];      /* 6 bytes of ethernet hardware address */
66 } uuid_address_t, *uuid_address_p_t;
67
68
69 typedef struct {
70     afs_uint32  lo;
71     afs_uint32  hi;
72 } uuid_time_t, *uuid_time_p_t;
73
74 static int uuid_get_address (uuid_address_p_t addr);
75 void uuid__get_os_time (uuid_time_t *os_time);
76
77 /*
78  * |<------------------------- 32 bits -------------------------->|
79  *
80  * +--------------------------------------------------------------+
81  * |                     low 32 bits of time                      |  0-3  .time_low
82  * +-------------------------------+-------------------------------
83  * |     mid 16 bits of time       |  4-5               .time_mid
84  * +-------+-----------------------+
85  * | vers. |   hi 12 bits of time  |  6-7               .time_hi_and_version
86  * +-------+-------+---------------+
87  * |Res|  clkSeqHi |  8                                 .clock_seq_hi_and_reserved
88  * +---------------+
89  * |   clkSeqLow   |  9                                 .clock_seq_low
90  * +---------------+----------...-----+
91  * |            node ID               |  8-16           .node
92  * +--------------------------...-----+
93  */
94
95 afsUUID afs_uuid_g_nil_uuid = { 0 };
96 static uuid_time_t time_now, time_last;
97 static u_short uuid_time_adjust, clock_seq;
98 static afs_uint32 rand_m, rand_ia, rand_ib, rand_irand, uuid_init_done = 0;
99
100 #define uuid_create_nil(uuid) memset(uuid, 0, sizeof(afsUUID))
101 afs_int32 afs_uuid_equal(u1, u2) afsUUID *u1, *u2;  { return(uuid_memcmp((void *)u1, (void *)u2, sizeof (afsUUID)) == 0); }
102 afs_int32 afs_uuid_is_nil(u1) afsUUID *u1; { 
103     if (!u1) return 1;
104     return(uuid_memcmp((void *)u1, (void *)&afs_uuid_g_nil_uuid, sizeof (afsUUID)) == 0); 
105 }
106
107
108 void afs_htonuuid(uuidp)
109 afsUUID *uuidp; {
110     uuidp->time_low = htonl(uuidp->time_low);
111     uuidp->time_mid = htons(uuidp->time_mid);
112     uuidp->time_hi_and_version = htons(uuidp->time_hi_and_version);
113 }
114
115 void afs_ntohuuid(uuidp)
116 afsUUID *uuidp; {
117     uuidp->time_low = ntohl(uuidp->time_low);
118     uuidp->time_mid = ntohs(uuidp->time_mid);
119     uuidp->time_hi_and_version = ntohs(uuidp->time_hi_and_version);
120 }
121  
122 static u_short true_random () {
123     rand_m += 7;
124     rand_ia += 1907;
125     rand_ib += 73939;
126     if (rand_m >= 9973) rand_m -= 9871;
127     if (rand_ia >= 99991) rand_ia -= 89989;
128     if (rand_ib >= 224729) rand_ib -= 96233;
129     rand_irand = (rand_irand * rand_m) + rand_ia + rand_ib;
130     return (((rand_irand) >> 16) ^ (rand_irand & 0x3fff));
131 }
132
133
134 static afs_int32 time_cmp (time1, time2)
135 uuid_time_p_t           time1;
136 uuid_time_p_t           time2; {
137     if (time1->hi < time2->hi) return (-1);
138     if (time1->hi > time2->hi) return (1);
139     if (time1->lo < time2->lo) return (-1);
140     if (time1->lo > time2->lo) return (1);
141     return (0);
142 }
143
144 afs_int32 afs_uuid_create (uuid)
145 afsUUID *uuid; {
146     uuid_address_t eaddr;
147     afs_int32 got_no_time = 0, code;
148
149     if (!uuid_init_done) {
150         uuid_time_t t;
151         u_short *seedp, seed=0;
152         rand_m = 971;;
153         rand_ia = 11113;
154         rand_ib = 104322;
155         rand_irand = 4181;
156         /*
157          * Generating our 'seed' value
158          *
159          * We start with the current time, but, since the resolution of clocks is
160          * system hardware dependent (eg. Ultrix is 10 msec.) and most likely
161          * coarser than our resolution (10 usec) we 'mixup' the bits by xor'ing
162          * all the bits together.  This will have the effect of involving all of
163          * the bits in the determination of the seed value while remaining system
164          * independent.  Then for good measure to ensure a unique seed when there
165          * are multiple processes creating UUID's on a system, we add in the PID.
166          */
167         uuid__get_os_time(&t);
168         seedp = (u_short *)(&t);
169         seed ^= *seedp++;
170         seed ^= *seedp++;
171         seed ^= *seedp++;
172         seed ^= *seedp++;
173         rand_irand += seed + (afs_uint32)getpid();
174         uuid__get_os_time (&time_last);
175         clock_seq = true_random();
176 #ifdef AFS_NT40_ENV
177         if (afs_winsockInit()<0) {
178             return WSAGetLastError();
179         }
180 #endif  
181         uuid_init_done = 1;
182     }
183     if ((code = uuid_get_address (&eaddr))) return code;     /* get our hardware network address */
184     do {
185         /* get the current time */
186         uuid__get_os_time (&time_now);
187         /*
188          * check that our clock hasn't gone backwards and handle it
189          *    accordingly with clock_seq
190          * check that we're not generating uuid's faster than we
191          *    can accommodate with our uuid_time_adjust fudge factor
192          */
193         if ((code = time_cmp (&time_now, &time_last)) == -1) {
194             /* A clock_seq value of 0 indicates that it hasn't been initialized. */
195             if (clock_seq == 0) {
196                 clock_seq = true_random();
197             }
198             clock_seq = (clock_seq + 1) & 0x3fff;
199             if (clock_seq == 0) clock_seq = clock_seq + 1;
200             uuid_time_adjust = 0;
201         } else if (code == 1) {
202             uuid_time_adjust = 0;
203         } else {
204             if (uuid_time_adjust == 0x7fff) /* spin while we wait for the clock to tick */
205                 got_no_time = 1;
206             else
207                 uuid_time_adjust++;
208         }
209     } while (got_no_time);
210     time_last.lo = time_now.lo;
211     time_last.hi = time_now.hi;
212     if (uuid_time_adjust != 0) {
213         if (time_now.lo & 0x80000000) {
214             time_now.lo += uuid_time_adjust;
215             if (!(time_now.lo & 0x80000000)) time_now.hi++;
216         } else
217             time_now.lo += uuid_time_adjust;
218     }
219     uuid->time_low = time_now.lo;
220     uuid->time_mid = time_now.hi & 0x0000ffff;
221     uuid->time_hi_and_version = (time_now.hi & 0x0fff0000) >> 16;
222     uuid->time_hi_and_version |= (1 << 12);
223     uuid->clock_seq_low = clock_seq & 0xff;
224     uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3f00) >> 8;
225     uuid->clock_seq_hi_and_reserved |= 0x80;
226     uuid_memcpy ((void *)uuid->node, (void *)&eaddr, sizeof (uuid_address_t));
227     return 0;
228 }
229
230 u_short afs_uuid_hash (uuid)
231 afsUUID *uuid; {
232     short               c0=0, c1=0, x, y;
233     char             *next_uuid = (char *) uuid;
234
235     /*
236      * For speed lets unroll the following loop:
237      *
238      *   for (i = 0; i < UUID_K_LENGTH; i++)
239      *   {
240      *       c0 = c0 + *next_uuid++;
241      *       c1 = c1 + c0;
242      *   }
243      */
244     c0 = c0 + *next_uuid++;
245     c1 = c1 + c0;
246     c0 = c0 + *next_uuid++;
247     c1 = c1 + c0;
248     c0 = c0 + *next_uuid++;
249     c1 = c1 + c0;
250     c0 = c0 + *next_uuid++;
251     c1 = c1 + c0;
252     c0 = c0 + *next_uuid++;
253     c1 = c1 + c0;
254     c0 = c0 + *next_uuid++;
255     c1 = c1 + c0;
256     c0 = c0 + *next_uuid++;
257     c1 = c1 + c0;
258     c0 = c0 + *next_uuid++;
259     c1 = c1 + c0;
260     c0 = c0 + *next_uuid++;
261     c1 = c1 + c0;
262     c0 = c0 + *next_uuid++;
263     c1 = c1 + c0;
264     c0 = c0 + *next_uuid++;
265     c1 = c1 + c0;
266     c0 = c0 + *next_uuid++;
267     c1 = c1 + c0;
268     c0 = c0 + *next_uuid++;
269     c1 = c1 + c0;
270     c0 = c0 + *next_uuid++;
271     c1 = c1 + c0;
272     c0 = c0 + *next_uuid++;
273     c1 = c1 + c0;
274     c0 = c0 + *next_uuid++;
275     c1 = c1 + c0;
276     /*  Calculate the value for "First octet" of the hash  */
277     x = -c1 % 255;
278     if (x < 0) {
279         x = x + 255;
280     }
281     /*  Calculate the value for "second octet" of the hash */
282     y = (c1 - c0) % 255;
283     if (y < 0) {
284         y = y + 255;
285     }
286     return ((y * 256) + x);
287 }
288
289 #ifdef KERNEL
290
291 extern struct interfaceAddr afs_cb_interface;
292
293 static int uuid_get_address (uuid_address_p_t addr)
294 {
295     uuid_memcpy((void *)addr->eaddr, (void *)&afs_cb_interface.addr_in[0], 4);
296     addr->eaddr[4] = 0xaa;
297     addr->eaddr[5] = 0x77;
298     return 0;
299 }
300
301 void uuid__get_os_time (uuid_time_t *os_time)
302 {
303     struct timeval      tp;
304
305     osi_GetTime(&tp);
306     os_time->hi = tp.tv_sec;
307     os_time->lo = tp.tv_usec*10;
308 }
309
310 #else /* KERNEL */
311
312 char hostName1[128] = "localhost";
313 static int uuid_get_address (uuid_address_p_t addr)
314 {
315     afs_int32 code;
316     afs_uint32 addr1;
317     struct hostent *he;
318
319     code = gethostname(hostName1, 64);
320     if (code) {
321         printf("gethostname() failed\n");
322 #ifdef AFS_NT40_ENV
323         return ENOENT;
324 #else
325         return errno;
326 #endif
327     }
328     he = gethostbyname(hostName1);
329     if (!he) {
330         printf("Can't find address for '%s'\n", hostName1);
331 #ifdef AFS_NT40_ENV
332         return ENOENT;
333 #else
334         return errno;
335 #endif
336     } else {
337       uuid_memcpy(&addr1, he->h_addr_list[0], 4);
338       addr1 = ntohl(addr1);
339       uuid_memcpy(addr->eaddr,  &addr1, 4);
340       addr->eaddr[4] = 0xaa;
341       addr->eaddr[5] = 0x77;
342 #ifdef  UUID_DEBUG
343       printf ("uuid_get_address: %02x-%02x-%02x-%02x-%02x-%02x\n",
344                 addr->eaddr[0], addr->eaddr[1], addr->eaddr[2],
345                 addr->eaddr[3], addr->eaddr[4], addr->eaddr[5]);
346 #endif
347     }
348     return 0;
349 }
350
351 void uuid__get_os_time (uuid_time_t *os_time)
352 {
353     struct timeval      tp;
354
355     if (gettimeofday (&tp, (struct timezone *) 0)) {
356         perror ("uuid__get_time");
357         exit (-1);
358     }
359     os_time->hi = tp.tv_sec;
360     os_time->lo = tp.tv_usec*10;
361 }
362
363 #endif /* KERNEL */