source: titan/minidlna-1.0.22/uuid.c @ 13567

Last change on this file since 13567 was 13567, checked in by obi, 12 years ago

[titan] add minidlna-1.0.22 first step

File size: 5.6 KB
Line 
1/* MiniDLNA project
2 *
3 * http://sourceforge.net/projects/minidlna/
4 *
5 * Much of this code and ideas for this code have been taken
6 * from Helge Deller's proposed Linux kernel patch (which
7 * apparently never made it upstream), and some from Busybox.
8 *
9 * MiniDLNA media server
10 * Copyright (C) 2009  Justin Maggard
11 *
12 * This file is part of MiniDLNA.
13 *
14 * MiniDLNA is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License version 2 as
16 * published by the Free Software Foundation.
17 *
18 * MiniDLNA is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
25 */
26#include <stdio.h>
27#include <stdlib.h>
28#include <time.h>
29#include <fcntl.h>
30#include <unistd.h>
31#include <sys/syscall.h>
32#include <string.h>
33#include <net/if.h>
34#include <sys/ioctl.h>
35#include <sys/time.h>
36#include <errno.h>
37
38#include "getifaddr.h"
39#include "log.h"
40
41#define ETH_ALEN 6
42#define NSEC_PER_SEC 1000000000L
43#define NSEC_PER_MSEC 1000000L
44
45static u_int32_t clock_seq;
46static const u_int32_t clock_seq_max = 0x3fff; /* 14 bits */
47static int clock_seq_initialized;
48
49unsigned long long
50monotonic_us(void)
51{
52        struct timespec ts;
53
54        syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &ts);
55        return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
56}
57
58int
59read_bootid_node(unsigned char *buf, size_t size)
60{
61        FILE *boot_id;
62
63        if(size != 6)
64                return -1;
65
66        boot_id = fopen("/proc/sys/kernel/random/boot_id", "r");
67        if(!boot_id)
68                return -1;
69        if((fseek(boot_id, 24, SEEK_SET) < 0) ||
70           (fscanf(boot_id, "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
71                   &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]) != 6))
72        {
73                fclose(boot_id);
74                return -1;
75        }
76
77        fclose(boot_id);
78        return 0;
79}
80
81static void
82read_random_bytes(unsigned char *buf, size_t size)
83{
84        int i;
85        pid_t pid;
86
87        i = open("/dev/urandom", O_RDONLY);
88        if(i >= 0)
89        {
90                read(i, buf, size);
91                close(i);
92        }
93        /* Paranoia. /dev/urandom may be missing.
94         * rand() is guaranteed to generate at least [0, 2^15) range,
95         * but lowest bits in some libc are not so "random".  */
96        srand(monotonic_us());
97        pid = getpid();
98        while(1)
99        {
100                for(i = 0; i < size; i++)
101                        buf[i] ^= rand() >> 5;
102                if(pid == 0)
103                        break;
104                srand(pid);
105                pid = 0;
106        }
107}
108
109void
110init_clockseq(void)
111{
112        unsigned char buf[4];
113
114        read_random_bytes(buf, 4);
115        memcpy(&clock_seq, &buf, sizeof(clock_seq));
116        clock_seq &= clock_seq_max;
117        clock_seq_initialized = 1;
118}
119
120int
121generate_uuid(unsigned char uuid_out[16])
122{
123        static u_int64_t last_time_all;
124        static unsigned int clock_seq_started;
125        static char last_node[6] = { 0, 0, 0, 0, 0, 0 };
126
127        struct timespec ts;
128        u_int64_t time_all;
129        int inc_clock_seq = 0;
130
131        unsigned char mac[6];
132        int mac_error;
133
134        memset(&mac, '\0', sizeof(mac));
135        /* Get the spatially unique node identifier */
136
137        mac_error = getsyshwaddr((char *)mac, sizeof(mac));
138
139        if(!mac_error)
140        {
141                memcpy(&uuid_out[10], mac, ETH_ALEN);
142        }
143        else
144        {
145                /* use bootid's nodeID if no network interface found */
146                DPRINTF(E_INFO, L_HTTP, "Could not find MAC.  Use bootid's nodeID.\n");
147                if( read_bootid_node(&uuid_out[10], 6) != 0)
148                {
149                        DPRINTF(E_INFO, L_HTTP, "bootid node not successfully read.\n");
150                        read_random_bytes(&uuid_out[10], 6);
151                }
152        }
153
154        if(memcmp(last_node, uuid_out+10, 6) != 0)
155        {
156                inc_clock_seq = 1;
157                memcpy(last_node, uuid_out+10, 6);
158        }
159
160        /* Determine 60-bit timestamp value. For UUID version 1, this is
161         * represented by Coordinated Universal Time (UTC) as a count of 100-
162         * nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of
163         * Gregorian reform to the Christian calendar).
164         */
165        syscall(__NR_clock_gettime, CLOCK_REALTIME, &ts);
166        time_all = ((u_int64_t)ts.tv_sec) * (NSEC_PER_SEC / 100);
167        time_all += ts.tv_nsec / 100;
168
169        /* add offset from Gregorian Calendar to Jan 1 1970 */
170        time_all += 12219292800000ULL * (NSEC_PER_MSEC / 100);
171        time_all &= 0x0fffffffffffffffULL; /* limit to 60 bits */
172
173        /* Determine clock sequence (max. 14 bit) */
174        if(!clock_seq_initialized)
175        {
176                init_clockseq();
177                clock_seq_started = clock_seq;
178        }
179        else
180        {
181                if(inc_clock_seq || time_all <= last_time_all)
182                {
183                        clock_seq = (clock_seq + 1) & clock_seq_max;
184                        if(clock_seq == clock_seq_started)
185                        {
186                                clock_seq = (clock_seq - 1) & clock_seq_max;
187                        }
188                }
189                else
190                        clock_seq_started = clock_seq;
191        }
192        last_time_all = time_all;
193
194        /* Fill in timestamp and clock_seq values */
195        uuid_out[3] = (u_int8_t)time_all;
196        uuid_out[2] = (u_int8_t)(time_all >> 8);
197        uuid_out[1] = (u_int8_t)(time_all >> 16);
198        uuid_out[0] = (u_int8_t)(time_all >> 24);
199        uuid_out[5] = (u_int8_t)(time_all >> 32);
200        uuid_out[4] = (u_int8_t)(time_all >> 40);
201        uuid_out[7] = (u_int8_t)(time_all >> 48);
202        uuid_out[6] = (u_int8_t)(time_all >> 56);
203
204        uuid_out[8] = clock_seq >> 8;
205        uuid_out[9] = clock_seq & 0xff;
206
207        /* Set UUID version to 1 --- time-based generation */
208        uuid_out[6] = (uuid_out[6] & 0x0F) | 0x10;
209        /* Set the UUID variant to DCE */
210        uuid_out[8] = (uuid_out[8] & 0x3F) | 0x80;
211
212        return 0;
213}
214
215/* Places a null-terminated 37-byte time-based UUID string in the buffer pointer to by buf.
216 * A large enough buffer must already be allocated. */
217int
218get_uuid_string(char *buf)
219{
220        unsigned char uuid[16];
221
222        if( generate_uuid(uuid) != 0 )
223                return -1;
224
225        sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
226                uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8],
227                uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
228        buf[36] = '\0';
229
230        return 0;
231}
Note: See TracBrowser for help on using the repository browser.