viruuid.c 7.2 KB
Newer Older
1
/*
2
 * viruuid.c: helper APIs for dealing with UUIDs
3
 *
4
 * Copyright (C) 2007-2014 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library.  If not, see
O
Osier Yang 已提交
18
 * <http://www.gnu.org/licenses/>.
19 20 21 22 23
 *
 * Authors:
 *     Mark McLoughlin <markmc@redhat.com>
 */

24
#include <config.h>
25

26
#include "viruuid.h"
27 28 29 30

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
31
#include <stdio.h>
32 33 34 35 36 37
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

J
Jim Meyering 已提交
38
#include "c-ctype.h"
39
#include "internal.h"
40
#include "virutil.h"
41
#include "virerror.h"
42
#include "virlog.h"
43
#include "viralloc.h"
E
Eric Blake 已提交
44
#include "virfile.h"
45
#include "virrandom.h"
46

47 48
VIR_LOG_INIT("util.uuid");

49 50
static unsigned char host_uuid[VIR_UUID_BUFLEN];

51
static int
D
Daniel P. Berrange 已提交
52 53
virUUIDGeneratePseudoRandomBytes(unsigned char *buf,
                                 int buflen)
54 55
{
    while (buflen > 0) {
56
        *buf++ = virRandomBits(8);
57 58 59 60 61 62
        buflen--;
    }

    return 0;
}

63 64
/**
 * virUUIDGenerate:
65
 * @uuid: array of VIR_UUID_BUFLEN bytes to store the new UUID
66 67 68 69 70
 *
 * Generates a randomized unique identifier.
 *
 * Returns 0 in case of success and -1 in case of failure
 */
71
int
D
Daniel P. Berrange 已提交
72
virUUIDGenerate(unsigned char *uuid)
73 74 75
{
    int err;

76
    if (uuid == NULL)
77
        return -1;
78

J
John Ferlan 已提交
79
    if ((err = virRandomBytes(uuid, VIR_UUID_BUFLEN))) {
80
        char ebuf[1024];
81 82
        VIR_WARN("Falling back to pseudorandom UUID,"
                 " failed to generate random bytes: %s",
83
                 virStrerror(err, ebuf, sizeof(ebuf)));
84
        err = virUUIDGeneratePseudoRandomBytes(uuid, VIR_UUID_BUFLEN);
85
    }
86

M
Milos Vyletel 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
    /*
     * Make UUID RFC 4122 compliant. Following form will be used:
     *
     * xxxxxxxx-xxxx-Axxx-Bxxx-xxxxxxxxxxxx
     *
     * where
     * A is version defined in 4.1.3 of RFC
     *  Msb0  Msb1  Msb2  Msb3   Version  Description
     *   0     1     0     0        4     The randomly or pseudo-
     *                                    randomly generated version
     *                                    specified in this document.
     *
     * B is variant defined in 4.1.1 of RFC
     *  Msb0  Msb1  Msb2  Description
     *   1     0     x    The variant specified in this document.
     */
    uuid[6] = (uuid[6] & 0x0F) | (4 << 4);
    uuid[8] = (uuid[8] & 0x3F) | (2 << 6);

106
    return err;
107 108
}

109 110
/**
 * virUUIDParse:
111 112
 * @uuidstr: zero terminated string representation of the UUID
 * @uuid: array of VIR_UUID_BUFLEN bytes to store the raw UUID
113 114 115 116 117 118
 *
 * Parses the external string representation, allowing spaces and '-'
 * character in the sequence, and storing the result as a raw UUID
 *
 * Returns 0 in case of success and -1 in case of error.
 */
119
int
120 121
virUUIDParse(const char *uuidstr, unsigned char *uuid)
{
122
    const char *cur;
123
    size_t i;
124 125 126

    /*
     * do a liberal scan allowing '-' and ' ' anywhere between character
127 128
     * pairs, and surrounding whitespace, as long as there are exactly
     * 32 hexadecimal digits the end.
129
     */
130
    cur = uuidstr;
131 132 133
    while (c_isspace(*cur))
        cur++;

134
    for (i = 0; i < VIR_UUID_BUFLEN;) {
135
        uuid[i] = 0;
136 137 138 139 140 141
        if (*cur == 0)
            goto error;
        if ((*cur == '-') || (*cur == ' ')) {
            cur++;
            continue;
        }
J
Jim Meyering 已提交
142
        if (!c_isxdigit(*cur))
143
            goto error;
144
        uuid[i] = virHexToBin(*cur);
145
        uuid[i] *= 16;
146 147 148
        cur++;
        if (*cur == 0)
            goto error;
J
Jim Meyering 已提交
149
        if (!c_isxdigit(*cur))
150
            goto error;
151
        uuid[i] += virHexToBin(*cur);
152 153 154 155
        i++;
        cur++;
    }

156 157 158 159 160 161
    while (*cur) {
        if (!c_isspace(*cur))
            goto error;
        cur++;
    }

162 163 164 165 166 167
    return 0;

 error:
    return -1;
}

168 169 170 171 172 173 174 175 176
/**
 * virUUIDFormat:
 * @uuid: array of VIR_UUID_RAW_LEN bytes to store the raw UUID
 * @uuidstr: array of VIR_UUID_STRING_BUFLEN bytes to store the
 * string representation of the UUID in. The resulting string
 * will be NULL terminated.
 *
 * Converts the raw UUID into printable format, with embedded '-'
 *
177
 * Returns a pointer to the resulting character string.
178
 */
179 180
const char *
virUUIDFormat(const unsigned char *uuid, char *uuidstr)
181 182 183 184 185 186 187 188
{
    snprintf(uuidstr, VIR_UUID_STRING_BUFLEN,
             "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
             uuid[0], uuid[1], uuid[2], uuid[3],
             uuid[4], uuid[5], uuid[6], uuid[7],
             uuid[8], uuid[9], uuid[10], uuid[11],
             uuid[12], uuid[13], uuid[14], uuid[15]);
    uuidstr[VIR_UUID_STRING_BUFLEN-1] = '\0';
189
    return uuidstr;
190
}
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206



/**
 * virUUIDIsValid
 *
 * @uuid: The UUID to test
 *
 * Do some basic tests to check whether the given UUID is
 * valid as a host UUID.
 * Basic tests:
 *  - Not all of the digits may be equal
 */
int
virUUIDIsValid(unsigned char *uuid)
{
207 208
    size_t i;
    unsigned int ctr = 1;
209 210 211 212 213 214 215 216 217 218 219
    unsigned char c;

    if (!uuid)
        return 0;

    c = uuid[0];

    for (i = 1; i < VIR_UUID_BUFLEN; i++)
        if (uuid[i] == c)
            ctr++;

220
    return ctr != VIR_UUID_BUFLEN;
221 222 223 224 225
}

static int
getDMISystemUUID(char *uuid, int len)
{
226
    size_t i = 0;
227 228 229 230 231 232 233 234
    const char *paths[] = {
        "/sys/devices/virtual/dmi/id/product_uuid",
        "/sys/class/dmi/id/product_uuid",
        NULL
    };

    while (paths[i]) {
        int fd = open(paths[i], O_RDONLY);
E
Eric Blake 已提交
235
        if (fd >= 0) {
236 237
            if (saferead(fd, uuid, len - 1) == len - 1) {
                uuid[len - 1] = '\0';
238
                VIR_FORCE_CLOSE(fd);
239 240
                return 0;
            }
241
            VIR_FORCE_CLOSE(fd);
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
        }
        i++;
    }

    return -1;
}


/**
 * setHostUUID
 *
 * @host_uuid: UUID that the host is supposed to have
 *
 * Set the UUID of the host if it hasn't been set, yet
 * Returns 0 in case of success, an error code in case of error.
 */
int
virSetHostUUIDStr(const char *uuid)
{
    int rc;
    char dmiuuid[VIR_UUID_STRING_BUFLEN];

    if (virUUIDIsValid(host_uuid))
        return EEXIST;

    if (!uuid) {
C
Chris Lalancette 已提交
268
        memset(dmiuuid, 0, sizeof(dmiuuid));
269
        if (!getDMISystemUUID(dmiuuid, sizeof(dmiuuid))) {
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
            if (!virUUIDParse(dmiuuid, host_uuid))
                return 0;
        }

        if (!virUUIDIsValid(host_uuid))
            return virUUIDGenerate(host_uuid);
    } else {
        rc = virUUIDParse(uuid, host_uuid);
        if (rc)
            return rc;
        if (!virUUIDIsValid(host_uuid))
            return EINVAL;
    }

    return 0;
}

/**
 * getHostUUID:
 *
 * @host_uuid: memory to store the host_uuid into
 *
 * Get the UUID of the host. Returns 0 in case of success,
 * an error code otherwise.
 * Returns 0 in case of success, an error code in case of error.
 */
int virGetHostUUID(unsigned char *uuid)
{
    int ret = 0;

    if (!virUUIDIsValid(host_uuid))
        ret = virSetHostUUIDStr(NULL);

    memcpy(uuid, host_uuid, sizeof(host_uuid));

    return ret;
}