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