source: rtems/cpukit/libmisc/uuid/gen_uuid.c @ 0893220

4.104.115
Last change on this file since 0893220 was 0893220, checked in by Ralf Corsepius <ralf.corsepius@…>, on 11/29/09 at 12:12:39

Whitespace removal.

  • Property mode set to 100644
File size: 14.8 KB
Line 
1/*
2 * gen_uuid.c --- generate a DCE-compatible uuid
3 *
4 * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, and the entire permission notice in its entirety,
12 *    including the disclaimer of warranties.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote
17 *    products derived from this software without specific prior
18 *    written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
21 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
23 * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
30 * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
31 * DAMAGE.
32 * %End-Header%
33 */
34
35#if HAVE_CONFIG_H
36#include "config.h"
37#endif
38
39/*
40 * Force inclusion of SVID stuff since we need it if we're compiling in
41 * gcc-wall wall mode
42 */
43#define _SVID_SOURCE
44
45#ifdef _WIN32
46#define _WIN32_WINNT 0x0500
47#include <windows.h>
48#define UUID MYUUID
49#endif
50#include <stdio.h>
51#include <limits.h> /* for CHAR_BIT */
52#ifdef HAVE_UNISTD_H
53#include <unistd.h>
54#endif
55#ifdef HAVE_STDLIB_H
56#include <stdlib.h>
57#endif
58#include <string.h>
59#include <fcntl.h>
60#include <errno.h>
61#include <sys/types.h>
62#ifdef HAVE_SYS_TIME_H
63#include <sys/time.h>
64#endif
65#include <sys/wait.h>
66#include <sys/stat.h>
67#ifdef HAVE_SYS_FILE_H
68#include <sys/file.h>
69#endif
70#ifdef HAVE_SYS_IOCTL_H
71#include <sys/ioctl.h>
72#endif
73#ifdef HAVE_SYS_SOCKET_H
74#include <sys/socket.h>
75#endif
76#ifdef HAVE_SYS_UN_H
77#include <sys/un.h>
78#endif
79#ifdef HAVE_SYS_SOCKIO_H
80#include <sys/sockio.h>
81#endif
82#ifdef HAVE_NET_IF_H
83#include <net/if.h>
84#endif
85#ifdef HAVE_NETINET_IN_H
86#include <netinet/in.h>
87#endif
88#ifdef HAVE_NET_IF_DL_H
89#include <net/if_dl.h>
90#endif
91#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
92#include <sys/syscall.h>
93#endif
94#ifdef HAVE_SYS_RESOURCE_H
95#include <sys/resource.h>
96#endif
97
98#include "uuidP.h"
99#include "uuidd.h"
100
101#ifdef HAVE_SRANDOM
102#define srand(x)        srandom(x)
103#define rand()          random()
104#endif
105
106#ifdef TLS
107#define THREAD_LOCAL static TLS
108#else
109#define THREAD_LOCAL static
110#endif
111
112#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
113#define DO_JRAND_MIX
114THREAD_LOCAL unsigned short jrand_seed[3];
115#endif
116
117#ifdef _WIN32
118static void gettimeofday (struct timeval *tv, void *dummy)
119{
120        FILETIME        ftime;
121        uint64_t        n;
122
123        GetSystemTimeAsFileTime (&ftime);
124        n = (((uint64_t) ftime.dwHighDateTime << 32)
125             + (uint64_t) ftime.dwLowDateTime);
126        if (n) {
127                n /= 10;
128                n -= ((369 * 365 + 89) * (uint64_t) 86400) * 1000000;
129        }
130
131        tv->tv_sec = n / 1000000;
132        tv->tv_usec = n % 1000000;
133}
134
135static int getuid (void)
136{
137        return 1;
138}
139#endif
140
141static int get_random_fd(void)
142{
143        struct timeval  tv;
144        static int      fd = -2;
145        int             i;
146
147        if (fd == -2) {
148                gettimeofday(&tv, 0);
149#ifndef _WIN32
150                fd = open("/dev/urandom", O_RDONLY);
151                if (fd == -1)
152                        fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
153                if (fd >= 0) {
154                        i = fcntl(fd, F_GETFD);
155                        if (i >= 0)
156                                fcntl(fd, F_SETFD, i | FD_CLOEXEC);
157                }
158#endif
159                srand((getpid() << ((sizeof(pid_t)*CHAR_BIT)>>1)) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
160#ifdef DO_JRAND_MIX
161                jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
162                jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
163                jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
164#endif
165        }
166        /* Crank the random number generator a few times */
167        gettimeofday(&tv, 0);
168        for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
169                rand();
170        return fd;
171}
172
173
174/*
175 * Generate a series of random bytes.  Use /dev/urandom if possible,
176 * and if not, use srandom/random.
177 */
178static void get_random_bytes(void *buf, int nbytes)
179{
180        int i, n = nbytes, fd = get_random_fd();
181        int lose_counter = 0;
182        unsigned char *cp = (unsigned char *) buf;
183#ifdef DO_JRAND_MIX
184        unsigned short tmp_seed[3];
185#endif
186
187        if (fd >= 0) {
188                while (n > 0) {
189                        i = read(fd, cp, n);
190                        if (i <= 0) {
191                                if (lose_counter++ > 16)
192                                        break;
193                                continue;
194                        }
195                        n -= i;
196                        cp += i;
197                        lose_counter = 0;
198                }
199        }
200
201        /*
202         * We do this all the time, but this is the only source of
203         * randomness if /dev/random/urandom is out to lunch.
204         */
205        for (cp = buf, i = 0; i < nbytes; i++)
206                *cp++ ^= (rand() >> 7) & 0xFF;
207#ifdef DO_JRAND_MIX
208        memcpy(tmp_seed, jrand_seed, sizeof(tmp_seed));
209        jrand_seed[2] = jrand_seed[2] ^ syscall(__NR_gettid);
210        for (cp = buf, i = 0; i < nbytes; i++)
211                *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
212        memcpy(jrand_seed, tmp_seed,
213               sizeof(jrand_seed)-sizeof(unsigned short));
214#endif
215
216        return;
217}
218
219/*
220 * Get the ethernet hardware address, if we can find it...
221 *
222 * XXX for a windows version, probably should use GetAdaptersInfo:
223 * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451
224 * commenting out get_node_id just to get gen_uuid to compile under windows
225 * is not the right way to go!
226 */
227static int get_node_id(unsigned char *node_id)
228{
229#ifdef HAVE_NET_IF_H
230        int             sd;
231        struct ifreq    ifr, *ifrp;
232        struct ifconf   ifc;
233        char buf[1024];
234        int             n, i;
235        unsigned char   *a;
236#ifdef HAVE_NET_IF_DL_H
237        struct sockaddr_dl *sdlp;
238#endif
239
240/*
241 * BSD 4.4 defines the size of an ifreq to be
242 * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
243 * However, under earlier systems, sa_len isn't present, so the size is
244 * just sizeof(struct ifreq)
245 */
246#ifdef HAVE_SA_LEN
247#ifndef max
248#define max(a,b) ((a) > (b) ? (a) : (b))
249#endif
250#define ifreq_size(i) max(sizeof(struct ifreq),\
251     sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
252#else
253#define ifreq_size(i) sizeof(struct ifreq)
254#endif /* HAVE_SA_LEN*/
255
256        sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
257        if (sd < 0) {
258                return -1;
259        }
260        memset(buf, 0, sizeof(buf));
261        ifc.ifc_len = sizeof(buf);
262        ifc.ifc_buf = buf;
263        if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
264                close(sd);
265                return -1;
266        }
267        n = ifc.ifc_len;
268        for (i = 0; i < n; i+= ifreq_size(*ifrp) ) {
269                ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
270                strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
271#ifdef SIOCGIFHWADDR
272                if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
273                        continue;
274                a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
275#else
276#ifdef SIOCGENADDR
277                if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
278                        continue;
279                a = (unsigned char *) ifr.ifr_enaddr;
280#else
281#ifdef HAVE_NET_IF_DL_H
282                sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr;
283                if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6))
284                        continue;
285                a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen];
286#else
287                /*
288                 * XXX we don't have a way of getting the hardware
289                 * address
290                 */
291                close(sd);
292                return 0;
293#endif /* HAVE_NET_IF_DL_H */
294#endif /* SIOCGENADDR */
295#endif /* SIOCGIFHWADDR */
296                if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
297                        continue;
298                if (node_id) {
299                        memcpy(node_id, a, 6);
300                        close(sd);
301                        return 1;
302                }
303        }
304        close(sd);
305#endif
306        return 0;
307}
308
309/* Assume that the gettimeofday() has microsecond granularity */
310#define MAX_ADJUSTMENT 10
311
312static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
313                     uint16_t *ret_clock_seq, int *num)
314{
315        THREAD_LOCAL int                adjustment = 0;
316        THREAD_LOCAL struct timeval     last = {0, 0};
317        THREAD_LOCAL int                state_fd = -2;
318        THREAD_LOCAL FILE               *state_f;
319        THREAD_LOCAL uint16_t           clock_seq;
320        struct timeval                  tv;
321        struct flock                    fl;
322        uint64_t                        clock_reg;
323        mode_t                          save_umask;
324        int                             len;
325
326        if (state_fd == -2) {
327                save_umask = umask(0);
328                state_fd = open("/var/lib/libuuid/clock.txt",
329                                O_RDWR|O_CREAT, 0660);
330                (void) umask(save_umask);
331                state_f = fdopen(state_fd, "r+");
332                if (!state_f) {
333                        close(state_fd);
334                        state_fd = -1;
335                }
336        }
337        fl.l_type = F_WRLCK;
338        fl.l_whence = SEEK_SET;
339        fl.l_start = 0;
340        fl.l_len = 0;
341        fl.l_pid = 0;
342        if (state_fd >= 0) {
343                rewind(state_f);
344                while (fcntl(state_fd, F_SETLKW, &fl) < 0) {
345                        if ((errno == EAGAIN) || (errno == EINTR))
346                                continue;
347                        fclose(state_f);
348                        close(state_fd);
349                        state_fd = -1;
350                        break;
351                }
352        }
353        if (state_fd >= 0) {
354                unsigned int cl;
355                unsigned long tv1, tv2;
356                int a;
357
358                if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n",
359                           &cl, &tv1, &tv2, &a) == 4) {
360                        clock_seq = cl & 0x3FFF;
361                        last.tv_sec = tv1;
362                        last.tv_usec = tv2;
363                        adjustment = a;
364                }
365        }
366
367        if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
368                get_random_bytes(&clock_seq, sizeof(clock_seq));
369                clock_seq &= 0x3FFF;
370                gettimeofday(&last, 0);
371                last.tv_sec--;
372        }
373
374try_again:
375        gettimeofday(&tv, 0);
376        if ((tv.tv_sec < last.tv_sec) ||
377            ((tv.tv_sec == last.tv_sec) &&
378             (tv.tv_usec < last.tv_usec))) {
379                clock_seq = (clock_seq+1) & 0x3FFF;
380                adjustment = 0;
381                last = tv;
382        } else if ((tv.tv_sec == last.tv_sec) &&
383            (tv.tv_usec == last.tv_usec)) {
384                if (adjustment >= MAX_ADJUSTMENT)
385                        goto try_again;
386                adjustment++;
387        } else {
388                adjustment = 0;
389                last = tv;
390        }
391
392        clock_reg = tv.tv_usec*10 + adjustment;
393        clock_reg += ((uint64_t) tv.tv_sec)*10000000;
394        clock_reg += (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
395
396        if (num && (*num > 1)) {
397                adjustment += *num - 1;
398                last.tv_usec += adjustment / 10;
399                adjustment = adjustment % 10;
400                last.tv_sec += last.tv_usec / 1000000;
401                last.tv_usec = last.tv_usec % 1000000;
402        }
403
404        if (state_fd > 0) {
405                rewind(state_f);
406                len = fprintf(state_f,
407                              "clock: %04x tv: %016lu %08lu adj: %08d\n",
408                              clock_seq, last.tv_sec, last.tv_usec, adjustment);
409                fflush(state_f);
410                if (ftruncate(state_fd, len) < 0) {
411                        fprintf(state_f, "                   \n");
412                        fflush(state_f);
413                }
414                rewind(state_f);
415                fl.l_type = F_UNLCK;
416                fcntl(state_fd, F_SETLK, &fl);
417        }
418
419        *clock_high = clock_reg >> 32;
420        *clock_low = clock_reg;
421        *ret_clock_seq = clock_seq;
422        return 0;
423}
424
425/* unused */
426#if defined(USE_UUIDD) && defined(HAVE_SYS_UN_H)
427static ssize_t read_all(int fd, char *buf, size_t count)
428{
429        ssize_t ret;
430        ssize_t c = 0;
431
432        memset(buf, 0, count);
433        while (count > 0) {
434                ret = read(fd, buf, count);
435                if (ret < 0) {
436                        if ((errno == EAGAIN) || (errno == EINTR))
437                                continue;
438                        return -1;
439                }
440                count -= ret;
441                buf += ret;
442                c += ret;
443        }
444        return c;
445}
446#endif
447
448#if defined(USE_UUIDD) && defined(HAVE_SYS_UN_H)
449
450/*
451 * Close all file descriptors
452 */
453static void close_all_fds(void)
454{
455        int i, max;
456
457#if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
458        max = sysconf(_SC_OPEN_MAX);
459#elif defined(HAVE_GETDTABLESIZE)
460        max = getdtablesize();
461#elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
462        struct rlimit rl;
463
464        getrlimit(RLIMIT_NOFILE, &rl);
465        max = rl.rlim_cur;
466#else
467        max = OPEN_MAX;
468#endif
469
470        for (i=0; i < max; i++)
471                close(i);
472}
473
474#endif
475
476
477/*
478 * Try using the uuidd daemon to generate the UUID
479 *
480 * Returns 0 on success, non-zero on failure.
481 */
482static int get_uuid_via_daemon(int op, uuid_t out, int *num)
483{
484#if defined(USE_UUIDD) && defined(HAVE_SYS_UN_H)
485        char op_buf[64];
486        int op_len;
487        int s;
488        ssize_t ret;
489        int32_t reply_len = 0, expected = 16;
490        struct sockaddr_un srv_addr;
491        pid_t pid;
492        static const char *uuidd_path = UUIDD_PATH;
493        static int access_ret = -2;
494        static int start_attempts = 0;
495
496        if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
497                return -1;
498
499        srv_addr.sun_family = AF_UNIX;
500        strcpy(srv_addr.sun_path, UUIDD_SOCKET_PATH);
501
502        if (connect(s, (const struct sockaddr *) &srv_addr,
503                    sizeof(struct sockaddr_un)) < 0) {
504                if (access_ret == -2)
505                        access_ret = access(uuidd_path, X_OK);
506                if (access_ret == 0 && start_attempts++ < 5) {
507                        if ((pid = fork()) == 0) {
508                                close_all_fds();
509                                execl(uuidd_path, "uuidd", "-qT", "300",
510                                      (char *) NULL);
511                                exit(1);
512                        }
513                        (void) waitpid(pid, 0, 0);
514                        if (connect(s, (const struct sockaddr *) &srv_addr,
515                                    sizeof(struct sockaddr_un)) < 0)
516                                goto fail;
517                } else
518                        goto fail;
519        }
520        op_buf[0] = op;
521        op_len = 1;
522        if (op == UUIDD_OP_BULK_TIME_UUID) {
523                memcpy(op_buf+1, num, sizeof(*num));
524                op_len += sizeof(*num);
525                expected += sizeof(*num);
526        }
527
528        ret = write(s, op_buf, op_len);
529        if (ret < 1)
530                goto fail;
531
532        ret = read_all(s, (char *) &reply_len, sizeof(reply_len));
533        if (ret < 0)
534                goto fail;
535
536        if (reply_len != expected)
537                goto fail;
538
539        ret = read_all(s, op_buf, reply_len);
540
541        if (op == UUIDD_OP_BULK_TIME_UUID)
542                memcpy(op_buf+16, num, sizeof(int));
543
544        memcpy(out, op_buf, 16);
545
546        close(s);
547        return ((ret == expected) ? 0 : -1);
548
549fail:
550        close(s);
551#endif
552        return -1;
553}
554
555void uuid__generate_time(uuid_t out, int *num)
556{
557        static unsigned char node_id[6];
558        static int has_init = 0;
559        struct uuid uu;
560        uint32_t        clock_mid;
561
562        if (!has_init) {
563                if (get_node_id(node_id) <= 0) {
564                        get_random_bytes(node_id, 6);
565                        /*
566                         * Set multicast bit, to prevent conflicts
567                         * with IEEE 802 addresses obtained from
568                         * network cards
569                         */
570                        node_id[0] |= 0x01;
571                }
572                has_init = 1;
573        }
574        get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
575        uu.clock_seq |= 0x8000;
576        uu.time_mid = (uint16_t) clock_mid;
577        uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
578        memcpy(uu.node, node_id, 6);
579        uuid_pack(&uu, out);
580}
581
582void uuid_generate_time(uuid_t out)
583{
584#ifdef TLS
585        THREAD_LOCAL int                num = 0;
586        THREAD_LOCAL struct uuid        uu;
587        THREAD_LOCAL time_t             last_time = 0;
588        time_t                          now;
589
590        if (num > 0) {
591                now = time(0);
592                if (now > last_time+1)
593                        num = 0;
594        }
595        if (num <= 0) {
596                num = 1000;
597                if (get_uuid_via_daemon(UUIDD_OP_BULK_TIME_UUID,
598                                        out, &num) == 0) {
599                        last_time = time(0);
600                        uuid_unpack(out, &uu);
601                        num--;
602                        return;
603                }
604                num = 0;
605        }
606        if (num > 0) {
607                uu.time_low++;
608                if (uu.time_low == 0) {
609                        uu.time_mid++;
610                        if (uu.time_mid == 0)
611                                uu.time_hi_and_version++;
612                }
613                num--;
614                uuid_pack(&uu, out);
615                return;
616        }
617#else
618        if (get_uuid_via_daemon(UUIDD_OP_TIME_UUID, out, 0) == 0)
619                return;
620#endif
621
622        uuid__generate_time(out, 0);
623}
624
625
626void uuid__generate_random(uuid_t out, int *num)
627{
628        uuid_t  buf;
629        struct uuid uu;
630        int i, n;
631
632        if (!num || !*num)
633                n = 1;
634        else
635                n = *num;
636
637        for (i = 0; i < n; i++) {
638                get_random_bytes(buf, sizeof(buf));
639                uuid_unpack(buf, &uu);
640
641                uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
642                uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF)
643                        | 0x4000;
644                uuid_pack(&uu, out);
645                out += sizeof(uuid_t);
646        }
647}
648
649void uuid_generate_random(uuid_t out)
650{
651        int     num = 1;
652        /* No real reason to use the daemon for random uuid's -- yet */
653
654        uuid__generate_random(out, &num);
655}
656
657
658/*
659 * This is the generic front-end to uuid_generate_random and
660 * uuid_generate_time.  It uses uuid_generate_random only if
661 * /dev/urandom is available, since otherwise we won't have
662 * high-quality randomness.
663 */
664void uuid_generate(uuid_t out)
665{
666        if (get_random_fd() >= 0)
667                uuid_generate_random(out);
668        else
669                uuid_generate_time(out);
670}
Note: See TracBrowser for help on using the repository browser.