Wrap up version 1.3.3.
[minidlna.git] / uuid.c
blob6e6a9ec23882fd422f3e048458eee2a0f347d640
1 /* MiniDLNA project
3 * http://sourceforge.net/projects/minidlna/
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.
9 * MiniDLNA media server
10 * Copyright (C) 2009 Justin Maggard
12 * This file is part of MiniDLNA.
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.
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.
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/>.
26 #include "config.h"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <time.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <sys/ioctl.h>
34 #include <sys/time.h>
35 #include <errno.h>
36 #if HAVE_MACH_MACH_TIME_H
37 #include <mach/mach_time.h>
38 #elif HAVE_CLOCK_GETTIME_SYSCALL
39 #include <sys/syscall.h>
40 #endif
42 #include "event.h"
43 #include "uuid.h"
44 #include "getifaddr.h"
45 #include "log.h"
47 static uint32_t clock_seq;
48 static const uint32_t clock_seq_max = 0x3fff; /* 14 bits */
49 static int clock_seq_initialized;
51 #ifndef CLOCK_MONOTONIC
52 #define CLOCK_MONOTONIC CLOCK_REALTIME
53 #endif
55 unsigned long long
56 monotonic_us(void)
58 struct timespec ts;
60 #if HAVE_CLOCK_GETTIME
61 clock_gettime(CLOCK_MONOTONIC, &ts);
62 #elif HAVE_CLOCK_GETTIME_SYSCALL
63 syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &ts);
64 #elif HAVE_MACH_MACH_TIME_H
65 return mach_absolute_time();
66 #else
67 struct timeval tv;
68 gettimeofday(&tv, 0);
69 TIMEVAL_TO_TIMESPEC(&tv, &ts);
70 #endif
71 return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
74 int
75 read_bootid_node(unsigned char *buf, size_t size)
77 FILE *boot_id;
79 if(size != 6)
80 return -1;
82 boot_id = fopen("/proc/sys/kernel/random/boot_id", "r");
83 if(!boot_id)
84 return -1;
85 if((fseek(boot_id, 24, SEEK_SET) < 0) ||
86 (fscanf(boot_id, "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
87 &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]) != 6))
89 fclose(boot_id);
90 return -1;
93 fclose(boot_id);
94 return 0;
97 static void
98 read_random_bytes(unsigned char *buf, size_t size)
100 int i;
101 pid_t pid;
103 i = open("/dev/urandom", O_RDONLY);
104 if(i >= 0)
106 if (read(i, buf, size) == -1)
107 DPRINTF(E_MAXDEBUG, L_GENERAL, "Failed to read random bytes\n");
108 close(i);
110 /* Paranoia. /dev/urandom may be missing.
111 * rand() is guaranteed to generate at least [0, 2^15) range,
112 * but lowest bits in some libc are not so "random". */
113 srand(monotonic_us());
114 pid = getpid();
115 while(1)
117 for(i = 0; i < size; i++)
118 buf[i] ^= rand() >> 5;
119 if(pid == 0)
120 break;
121 srand(pid);
122 pid = 0;
126 void
127 init_clockseq(void)
129 unsigned char buf[4];
131 read_random_bytes(buf, 4);
132 memcpy(&clock_seq, &buf, sizeof(clock_seq));
133 clock_seq &= clock_seq_max;
134 clock_seq_initialized = 1;
138 generate_uuid(unsigned char uuid_out[16])
140 static uint64_t last_time_all;
141 static unsigned int clock_seq_started;
142 static char last_node[6] = { 0, 0, 0, 0, 0, 0 };
144 struct timespec ts;
145 uint64_t time_all;
146 int inc_clock_seq = 0;
148 unsigned char mac[6];
149 int mac_error;
151 memset(&mac, '\0', sizeof(mac));
152 /* Get the spatially unique node identifier */
154 mac_error = getsyshwaddr((char *)mac, sizeof(mac));
156 if(!mac_error)
158 memcpy(&uuid_out[10], mac, ETH_ALEN);
160 else
162 /* use bootid's nodeID if no network interface found */
163 DPRINTF(E_INFO, L_HTTP, "Could not find MAC. Use bootid's nodeID.\n");
164 if( read_bootid_node(&uuid_out[10], 6) != 0)
166 DPRINTF(E_INFO, L_HTTP, "bootid node not successfully read.\n");
167 read_random_bytes(&uuid_out[10], 6);
171 if(memcmp(last_node, uuid_out+10, 6) != 0)
173 inc_clock_seq = 1;
174 memcpy(last_node, uuid_out+10, 6);
177 /* Determine 60-bit timestamp value. For UUID version 1, this is
178 * represented by Coordinated Universal Time (UTC) as a count of 100-
179 * nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of
180 * Gregorian reform to the Christian calendar).
182 #if HAVE_CLOCK_GETTIME
183 clock_gettime(CLOCK_REALTIME, &ts);
184 #elif HAVE_CLOCK_GETTIME_SYSCALL
185 syscall(__NR_clock_gettime, CLOCK_REALTIME, &ts);
186 #else
187 struct timeval tv;
188 gettimeofday(&tv, 0);
189 TIMEVAL_TO_TIMESPEC(&tv, &ts);
190 #endif
191 time_all = ((uint64_t)ts.tv_sec) * (NSEC_PER_SEC / 100);
192 time_all += ts.tv_nsec / 100;
194 /* add offset from Gregorian Calendar to Jan 1 1970 */
195 time_all += 12219292800000ULL * (NSEC_PER_MSEC / 100);
196 time_all &= 0x0fffffffffffffffULL; /* limit to 60 bits */
198 /* Determine clock sequence (max. 14 bit) */
199 if(!clock_seq_initialized)
201 init_clockseq();
202 clock_seq_started = clock_seq;
204 else
206 if(inc_clock_seq || time_all <= last_time_all)
208 clock_seq = (clock_seq + 1) & clock_seq_max;
209 if(clock_seq == clock_seq_started)
211 clock_seq = (clock_seq - 1) & clock_seq_max;
214 else
215 clock_seq_started = clock_seq;
217 last_time_all = time_all;
219 /* Fill in timestamp and clock_seq values */
220 uuid_out[3] = (uint8_t)time_all;
221 uuid_out[2] = (uint8_t)(time_all >> 8);
222 uuid_out[1] = (uint8_t)(time_all >> 16);
223 uuid_out[0] = (uint8_t)(time_all >> 24);
224 uuid_out[5] = (uint8_t)(time_all >> 32);
225 uuid_out[4] = (uint8_t)(time_all >> 40);
226 uuid_out[7] = (uint8_t)(time_all >> 48);
227 uuid_out[6] = (uint8_t)(time_all >> 56);
229 uuid_out[8] = clock_seq >> 8;
230 uuid_out[9] = clock_seq & 0xff;
232 /* Set UUID version to 1 --- time-based generation */
233 uuid_out[6] = (uuid_out[6] & 0x0F) | 0x10;
234 /* Set the UUID variant to DCE */
235 uuid_out[8] = (uuid_out[8] & 0x3F) | 0x80;
237 return 0;
240 /* Places a null-terminated 37-byte time-based UUID string in the buffer pointer to by buf.
241 * A large enough buffer must already be allocated. */
243 get_uuid_string(char *buf)
245 unsigned char uuid[16];
247 if( generate_uuid(uuid) != 0 )
248 return -1;
250 sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
251 uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8],
252 uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
253 buf[36] = '\0';
255 return 0;