source: rtems/cpukit/libmisc/uuid/gen_uuid.c @ 430f6ff

4.104.115
Last change on this file since 430f6ff was 430f6ff, checked in by Joel Sherrill <joel.sherrill@…>, on 05/06/09 at 16:36:46

2009-05-06 Joel Sherrill <joel.sherrill@…>

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