esx_util.c 13.8 KB
Newer Older
1
/*
2
 * esx_util.c: utility functions for the VMware ESX driver
3
 *
4
 * Copyright (C) 2010-2012 Red Hat, Inc.
5
 * Copyright (C) 2009-2011 Matthias Bolte <matthias.bolte@googlemail.com>
6 7 8 9 10 11 12 13 14 15 16 17 18
 * Copyright (C) 2009 Maximilian Wilhelm <max@rfc2324.org>
 *
 * 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
19
 * License along with this library.  If not, see
O
Osier Yang 已提交
20
 * <http://www.gnu.org/licenses/>.
21 22 23 24 25 26 27 28 29
 *
 */

#include <config.h>

#include <netdb.h>

#include "internal.h"
#include "datatypes.h"
30
#include "viralloc.h"
31
#include "virlog.h"
32
#include "viruuid.h"
33
#include "vmx.h"
34
#include "esx_private.h"
35
#include "esx_util.h"
36
#include "virstring.h"
37 38 39

#define VIR_FROM_THIS VIR_FROM_ESX

40
VIR_LOG_INIT("esx.esx_util");
41

42
int
M
Martin Kletzander 已提交
43
esxUtil_ParseUri(esxUtil_ParsedUri **parsedUri, virURIPtr uri)
44
{
M
Matthias Bolte 已提交
45
    int result = -1;
46
    size_t i;
47 48
    int noVerify;
    int autoAnswer;
M
Matthias Bolte 已提交
49
    char *tmp;
50

51
    if (!parsedUri || *parsedUri) {
52
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
53
        return -1;
54 55
    }

56
    if (VIR_ALLOC(*parsedUri) < 0)
57
        return -1;
58

59 60
    for (i = 0; i < uri->paramsCount; i++) {
        virURIParamPtr queryParam = &uri->params[i];
61

62
        if (STRCASEEQ(queryParam->name, "transport")) {
M
Matthias Bolte 已提交
63
            VIR_FREE((*parsedUri)->transport);
64

65
            if (VIR_STRDUP((*parsedUri)->transport, queryParam->value) < 0)
M
Matthias Bolte 已提交
66
                goto cleanup;
67

M
Matthias Bolte 已提交
68 69
            if (STRNEQ((*parsedUri)->transport, "http") &&
                STRNEQ((*parsedUri)->transport, "https")) {
70 71 72 73
                virReportError(VIR_ERR_INVALID_ARG,
                               _("Query parameter 'transport' has unexpected value "
                                 "'%s' (should be http|https)"),
                               (*parsedUri)->transport);
M
Matthias Bolte 已提交
74
                goto cleanup;
75
            }
76
        } else if (STRCASEEQ(queryParam->name, "vcenter")) {
M
Matthias Bolte 已提交
77
            VIR_FREE((*parsedUri)->vCenter);
78

79
            if (VIR_STRDUP((*parsedUri)->vCenter, queryParam->value) < 0)
M
Matthias Bolte 已提交
80
                goto cleanup;
81
        } else if (STRCASEEQ(queryParam->name, "no_verify")) {
82 83
            if (virStrToLong_i(queryParam->value, NULL, 10, &noVerify) < 0 ||
                (noVerify != 0 && noVerify != 1)) {
84 85 86
                virReportError(VIR_ERR_INVALID_ARG,
                               _("Query parameter 'no_verify' has unexpected value "
                                 "'%s' (should be 0 or 1)"), queryParam->value);
M
Matthias Bolte 已提交
87
                goto cleanup;
88
            }
89

M
Matthias Bolte 已提交
90
            (*parsedUri)->noVerify = noVerify != 0;
91 92 93
        } else if (STRCASEEQ(queryParam->name, "auto_answer")) {
            if (virStrToLong_i(queryParam->value, NULL, 10, &autoAnswer) < 0 ||
                (autoAnswer != 0 && autoAnswer != 1)) {
94 95 96
                virReportError(VIR_ERR_INVALID_ARG,
                               _("Query parameter 'auto_answer' has unexpected "
                                 "value '%s' (should be 0 or 1)"), queryParam->value);
M
Matthias Bolte 已提交
97
                goto cleanup;
98
            }
99

M
Matthias Bolte 已提交
100
            (*parsedUri)->autoAnswer = autoAnswer != 0;
M
Matthias Bolte 已提交
101 102
        } else if (STRCASEEQ(queryParam->name, "proxy")) {
            /* Expected format: [<type>://]<hostname>[:<port>] */
M
Matthias Bolte 已提交
103 104 105 106
            (*parsedUri)->proxy = true;
            (*parsedUri)->proxy_type = CURLPROXY_HTTP;
            VIR_FREE((*parsedUri)->proxy_hostname);
            (*parsedUri)->proxy_port = 1080;
M
Matthias Bolte 已提交
107

108
            if ((tmp = STRSKIP(queryParam->value, "http://"))) {
M
Matthias Bolte 已提交
109
                (*parsedUri)->proxy_type = CURLPROXY_HTTP;
110 111
            } else if ((tmp = STRSKIP(queryParam->value, "socks://")) ||
                       (tmp = STRSKIP(queryParam->value, "socks5://"))) {
M
Matthias Bolte 已提交
112
                (*parsedUri)->proxy_type = CURLPROXY_SOCKS5;
113
            } else if ((tmp = STRSKIP(queryParam->value, "socks4://"))) {
M
Matthias Bolte 已提交
114
                (*parsedUri)->proxy_type = CURLPROXY_SOCKS4;
115
            } else if ((tmp = STRSKIP(queryParam->value, "socks4a://"))) {
M
Matthias Bolte 已提交
116
                (*parsedUri)->proxy_type = CURLPROXY_SOCKS4A;
117
            } else if ((tmp = strstr(queryParam->value, "://"))) {
M
Matthias Bolte 已提交
118 119
                *tmp = '\0';

120 121 122 123
                virReportError(VIR_ERR_INVALID_ARG,
                               _("Query parameter 'proxy' contains unexpected "
                                 "type '%s' (should be (http|socks(|4|4a|5))"),
                               queryParam->value);
M
Matthias Bolte 已提交
124 125 126 127 128
                goto cleanup;
            } else {
                tmp = queryParam->value;
            }

129
            if (VIR_STRDUP((*parsedUri)->proxy_hostname, tmp) < 0)
M
Matthias Bolte 已提交
130 131
                goto cleanup;

132
            if ((tmp = strchr((*parsedUri)->proxy_hostname, ':'))) {
M
Matthias Bolte 已提交
133
                if (tmp == (*parsedUri)->proxy_hostname) {
134 135 136
                    virReportError(VIR_ERR_INVALID_ARG, "%s",
                                   _("Query parameter 'proxy' doesn't contain a "
                                     "hostname"));
M
Matthias Bolte 已提交
137 138 139 140 141 142
                    goto cleanup;
                }

                *tmp++ = '\0';

                if (virStrToLong_i(tmp, NULL, 10,
M
Matthias Bolte 已提交
143 144 145
                                   &(*parsedUri)->proxy_port) < 0 ||
                    (*parsedUri)->proxy_port < 1 ||
                    (*parsedUri)->proxy_port > 65535) {
146
                    virReportError(VIR_ERR_INVALID_ARG,
147 148 149
                                   _("Query parameter 'proxy' has unexpected "
                                     "port value '%s' (should be [1..65535])"),
                                   tmp);
M
Matthias Bolte 已提交
150 151 152
                    goto cleanup;
                }
            }
153 154 155 156 157 158
        } else {
            VIR_WARN("Ignoring unexpected query parameter '%s'",
                     queryParam->name);
        }
    }

159 160
    if (VIR_STRDUP((*parsedUri)->path, uri->path) < 0)
        goto cleanup;
M
Matthias Bolte 已提交
161

162 163 164
    if (!(*parsedUri)->transport &&
        VIR_STRDUP((*parsedUri)->transport, "https") < 0)
        goto cleanup;
165

M
Matthias Bolte 已提交
166
    result = 0;
167

168
 cleanup:
169
    if (result < 0)
M
Matthias Bolte 已提交
170
        esxUtil_FreeParsedUri(parsedUri);
171

M
Matthias Bolte 已提交
172
    return result;
173 174 175 176
}



177 178

void
M
Matthias Bolte 已提交
179
esxUtil_FreeParsedUri(esxUtil_ParsedUri **parsedUri)
180
{
181
    if (!parsedUri || !(*parsedUri))
182 183
        return;

M
Matthias Bolte 已提交
184 185 186
    VIR_FREE((*parsedUri)->transport);
    VIR_FREE((*parsedUri)->vCenter);
    VIR_FREE((*parsedUri)->proxy_hostname);
187
    VIR_FREE((*parsedUri)->path);
188

M
Matthias Bolte 已提交
189
    VIR_FREE(*parsedUri);
190 191 192 193
}



194 195 196 197
int
esxUtil_ParseVirtualMachineIDString(const char *id_string, int *id)
{
    /* Try to parse an integer from the complete string. */
198
    if (virStrToLong_i(id_string, NULL, 10, id) == 0)
199 200 201 202 203 204 205
        return 0;

    /*
     * If that fails try to parse an integer from the string tail
     * assuming the naming scheme Virtual Center seems to use.
     */
    if (STRPREFIX(id_string, "vm-")) {
206
        if (virStrToLong_i(id_string + 3, NULL, 10, id) == 0)
207 208 209 210 211 212 213 214
            return 0;
    }

    return -1;
}



M
Matthias Bolte 已提交
215
int
216
esxUtil_ParseDatastorePath(const char *datastorePath, char **datastoreName,
217
                           char **directoryName, char **directoryAndFileName)
M
Matthias Bolte 已提交
218
{
M
Matthias Bolte 已提交
219
    int result = -1;
220
    char *copyOfDatastorePath = NULL;
221 222 223
    char *tmp = NULL;
    char *saveptr = NULL;
    char *preliminaryDatastoreName = NULL;
224
    char *preliminaryDirectoryAndFileName = NULL;
M
Matthias Bolte 已提交
225

226 227 228
    if ((datastoreName && *datastoreName) ||
        (directoryName && *directoryName) ||
        (directoryAndFileName && *directoryAndFileName)) {
229
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
230
        return -1;
M
Matthias Bolte 已提交
231 232
    }

233
    if (VIR_STRDUP(copyOfDatastorePath, datastorePath) < 0)
M
Matthias Bolte 已提交
234
        goto cleanup;
235

236
    /* Expected format: '[<datastore>] <path>' where <path> is optional */
237 238
    if (!(tmp = STRSKIP(copyOfDatastorePath, "[")) || *tmp == ']' ||
        !(preliminaryDatastoreName = strtok_r(tmp, "]", &saveptr))) {
239 240 241
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Datastore path '%s' doesn't have expected format "
                         "'[<datastore>] <path>'"), datastorePath);
M
Matthias Bolte 已提交
242
        goto cleanup;
M
Matthias Bolte 已提交
243 244
    }

245
    if (datastoreName &&
246
        VIR_STRDUP(*datastoreName, preliminaryDatastoreName) < 0) {
M
Matthias Bolte 已提交
247
        goto cleanup;
248 249
    }

250
    preliminaryDirectoryAndFileName = strtok_r(NULL, "", &saveptr);
251

252
    if (!preliminaryDirectoryAndFileName) {
253 254 255 256 257
        preliminaryDirectoryAndFileName = (char *)"";
    } else {
        preliminaryDirectoryAndFileName +=
          strspn(preliminaryDirectoryAndFileName, " ");
    }
M
Matthias Bolte 已提交
258

259
    if (directoryAndFileName &&
260
        VIR_STRDUP(*directoryAndFileName, preliminaryDirectoryAndFileName) < 0) {
261 262
        goto cleanup;
    }
M
Matthias Bolte 已提交
263

264
    if (directoryName) {
265 266
        /* Split <path> into <directory>/<file> and remove /<file> */
        tmp = strrchr(preliminaryDirectoryAndFileName, '/');
267

268
        if (tmp)
269
            *tmp = '\0';
M
Matthias Bolte 已提交
270

271
        if (VIR_STRDUP(*directoryName, preliminaryDirectoryAndFileName) < 0)
M
Matthias Bolte 已提交
272
            goto cleanup;
M
Matthias Bolte 已提交
273 274
    }

M
Matthias Bolte 已提交
275 276
    result = 0;

277
 cleanup:
M
Matthias Bolte 已提交
278
    if (result < 0) {
279
        if (datastoreName)
280 281
            VIR_FREE(*datastoreName);

282
        if (directoryName)
283 284
            VIR_FREE(*directoryName);

285
        if (directoryAndFileName)
286
            VIR_FREE(*directoryAndFileName);
M
Matthias Bolte 已提交
287 288
    }

289
    VIR_FREE(copyOfDatastorePath);
M
Matthias Bolte 已提交
290 291 292 293 294 295

    return result;
}



296
int
297
esxUtil_ResolveHostname(const char *hostname,
M
Matthias Bolte 已提交
298
                        char *ipAddress, size_t ipAddress_length)
299 300 301 302 303
{
    struct addrinfo hints;
    struct addrinfo *result = NULL;
    int errcode;

304
    memset(&hints, 0, sizeof(hints));
305 306 307 308 309 310 311 312 313

    hints.ai_flags = AI_ADDRCONFIG;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;

    errcode = getaddrinfo(hostname, NULL, &hints, &result);

    if (errcode != 0) {
314 315 316
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("IP address lookup for host '%s' failed: %s"), hostname,
                       gai_strerror(errcode));
317 318 319
        return -1;
    }

320
    if (!result) {
321 322 323
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("No IP address for host '%s' found: %s"), hostname,
                       gai_strerror(errcode));
324 325 326
        return -1;
    }

M
Matthias Bolte 已提交
327 328
    errcode = getnameinfo(result->ai_addr, result->ai_addrlen, ipAddress,
                          ipAddress_length, NULL, 0, NI_NUMERICHOST);
329 330

    if (errcode != 0) {
331 332 333
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Formatting IP address for host '%s' failed: %s"), hostname,
                       gai_strerror(errcode));
334 335 336 337 338 339 340 341 342 343 344
        freeaddrinfo(result);
        return -1;
    }

    freeaddrinfo(result);

    return 0;
}



345 346 347 348 349 350
int
esxUtil_ReformatUuid(const char *input, char *output)
{
    unsigned char uuid[VIR_UUID_BUFLEN];

    if (virUUIDParse(input, uuid) < 0) {
351 352 353
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not parse UUID from string '%s'"),
                       input);
354 355 356 357 358 359 360
        return -1;
    }

    virUUIDFormat(uuid, output);

    return 0;
}
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400



char *
esxUtil_EscapeBase64(const char *string)
{
    /* 'normal' characters don't get base64 encoded */
    static const char *normal =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'(),. _-";

    /* VMware uses ',' instead of the path separator '/' in the base64 alphabet */
    static const char *base64 =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";

    virBuffer buffer = VIR_BUFFER_INITIALIZER;
    const char *tmp1 = string;
    size_t length;
    unsigned char c1, c2, c3;

    /* Escape sequences of non-'normal' characters as base64 without padding */
    while (*tmp1 != '\0') {
        length = strspn(tmp1, normal);

        if (length > 0) {
            virBufferAdd(&buffer, tmp1, length);

            tmp1 += length;
        } else {
            length = strcspn(tmp1, normal);

            virBufferAddChar(&buffer, '+');

            while (length > 0) {
                c1 = *tmp1++;
                c2 = length > 1 ? *tmp1++ : 0;
                c3 = length > 2 ? *tmp1++ : 0;

                virBufferAddChar(&buffer, base64[(c1 >> 2) & 0x3f]);
                virBufferAddChar(&buffer, base64[((c1 << 4) + (c2 >> 4)) & 0x3f]);

401
                if (length > 1)
402 403
                    virBufferAddChar(&buffer, base64[((c2 << 2) + (c3 >> 6)) & 0x3f]);

404
                if (length > 2)
405 406 407 408 409
                    virBufferAddChar(&buffer, base64[c3 & 0x3f]);

                length -= length > 3 ? 3 : length;
            }

410
            if (*tmp1 != '\0')
411 412 413 414
                virBufferAddChar(&buffer, '-');
        }
    }

415
    if (virBufferCheckError(&buffer) < 0)
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
        return NULL;

    return virBufferContentAndReset(&buffer);
}



void
esxUtil_ReplaceSpecialWindowsPathChars(char *string)
{
    /* '/' and '\\' are missing on purpose */
    static const char *specials = "\"*<>:|?";

    char *tmp = string;
    size_t length;

    while (*tmp != '\0') {
        length = strspn(tmp, specials);

        while (length > 0) {
            *tmp++ = '_';
            --length;
        }

440
        if (*tmp != '\0')
441 442 443 444 445 446 447 448 449
            ++tmp;
    }
}



char *
esxUtil_EscapeDatastoreItem(const char *string)
{
450
    char *replaced;
451 452 453
    char *escaped1;
    char *escaped2 = NULL;

454
    if (VIR_STRDUP(replaced, string) < 0)
455 456 457 458
        return NULL;

    esxUtil_ReplaceSpecialWindowsPathChars(replaced);

459
    escaped1 = virVMXEscapeHexPercent(replaced);
460

461
    if (!escaped1)
462 463 464 465
        goto cleanup;

    escaped2 = esxUtil_EscapeBase64(escaped1);

466
 cleanup:
467 468 469 470 471
    VIR_FREE(replaced);
    VIR_FREE(escaped1);

    return escaped2;
}
M
Matthias Bolte 已提交
472 473 474 475 476 477 478 479 480 481



char *
esxUtil_EscapeForXml(const char *string)
{
    virBuffer buffer = VIR_BUFFER_INITIALIZER;

    virBufferEscapeString(&buffer, "%s", string);

482
    if (virBufferCheckError(&buffer) < 0)
M
Matthias Bolte 已提交
483 484 485 486
        return NULL;

    return virBufferContentAndReset(&buffer);
}