esx_util.c 15.2 KB
Newer Older
1 2

/*
3
 * esx_util.c: utility functions for the VMware ESX driver
4
 *
E
Eric Blake 已提交
5
 * Copyright (C) 2010-2011 Red Hat, Inc.
6
 * Copyright (C) 2009-2011 Matthias Bolte <matthias.bolte@googlemail.com>
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 * 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
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 */

#include <config.h>

#include <netdb.h>

#include "internal.h"
#include "datatypes.h"
#include "qparams.h"
#include "util.h"
#include "memory.h"
#include "logging.h"
#include "uuid.h"
36
#include "vmx.h"
37
#include "esx_private.h"
38 39 40 41
#include "esx_util.h"

#define VIR_FROM_THIS VIR_FROM_ESX

42

43

44
int
M
Matthias Bolte 已提交
45
esxUtil_ParseUri(esxUtil_ParsedUri **parsedUri, xmlURIPtr uri)
46
{
M
Matthias Bolte 已提交
47
    int result = -1;
48 49
    struct qparam_set *queryParamSet = NULL;
    struct qparam *queryParam = NULL;
50 51 52
    int i;
    int noVerify;
    int autoAnswer;
M
Matthias Bolte 已提交
53
    char *tmp;
54

M
Matthias Bolte 已提交
55
    if (parsedUri == NULL || *parsedUri != NULL) {
56 57
        ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
        return -1;
58 59
    }

M
Matthias Bolte 已提交
60
    if (VIR_ALLOC(*parsedUri) < 0) {
61 62
        virReportOOMError();
        return -1;
63 64
    }

65
#ifdef HAVE_XMLURI_QUERY_RAW
66
    queryParamSet = qparam_query_parse(uri->query_raw);
67
#else
68
    queryParamSet = qparam_query_parse(uri->query);
69 70 71
#endif

    if (queryParamSet == NULL) {
72
        goto cleanup;
73 74 75 76 77
    }

    for (i = 0; i < queryParamSet->n; i++) {
        queryParam = &queryParamSet->p[i];

78
        if (STRCASEEQ(queryParam->name, "transport")) {
M
Matthias Bolte 已提交
79
            VIR_FREE((*parsedUri)->transport);
80

M
Matthias Bolte 已提交
81
            (*parsedUri)->transport = strdup(queryParam->value);
82

M
Matthias Bolte 已提交
83
            if ((*parsedUri)->transport == NULL) {
84
                virReportOOMError();
M
Matthias Bolte 已提交
85
                goto cleanup;
86 87
            }

M
Matthias Bolte 已提交
88 89
            if (STRNEQ((*parsedUri)->transport, "http") &&
                STRNEQ((*parsedUri)->transport, "https")) {
90
                ESX_ERROR(VIR_ERR_INVALID_ARG,
91
                          _("Query parameter 'transport' has unexpected value "
92
                            "'%s' (should be http|https)"),
M
Matthias Bolte 已提交
93
                          (*parsedUri)->transport);
M
Matthias Bolte 已提交
94
                goto cleanup;
95
            }
96
        } else if (STRCASEEQ(queryParam->name, "vcenter")) {
M
Matthias Bolte 已提交
97
            VIR_FREE((*parsedUri)->vCenter);
98

M
Matthias Bolte 已提交
99
            (*parsedUri)->vCenter = strdup(queryParam->value);
100

M
Matthias Bolte 已提交
101
            if ((*parsedUri)->vCenter == NULL) {
102
                virReportOOMError();
M
Matthias Bolte 已提交
103
                goto cleanup;
104
            }
105
        } else if (STRCASEEQ(queryParam->name, "no_verify")) {
106 107
            if (virStrToLong_i(queryParam->value, NULL, 10, &noVerify) < 0 ||
                (noVerify != 0 && noVerify != 1)) {
108
                ESX_ERROR(VIR_ERR_INVALID_ARG,
109 110
                          _("Query parameter 'no_verify' has unexpected value "
                            "'%s' (should be 0 or 1)"), queryParam->value);
M
Matthias Bolte 已提交
111
                goto cleanup;
112
            }
113

M
Matthias Bolte 已提交
114
            (*parsedUri)->noVerify = noVerify != 0;
115 116 117
        } else if (STRCASEEQ(queryParam->name, "auto_answer")) {
            if (virStrToLong_i(queryParam->value, NULL, 10, &autoAnswer) < 0 ||
                (autoAnswer != 0 && autoAnswer != 1)) {
118
                ESX_ERROR(VIR_ERR_INVALID_ARG,
119 120
                          _("Query parameter 'auto_answer' has unexpected "
                            "value '%s' (should be 0 or 1)"), queryParam->value);
M
Matthias Bolte 已提交
121
                goto cleanup;
122
            }
123

M
Matthias Bolte 已提交
124
            (*parsedUri)->autoAnswer = autoAnswer != 0;
M
Matthias Bolte 已提交
125 126
        } else if (STRCASEEQ(queryParam->name, "proxy")) {
            /* Expected format: [<type>://]<hostname>[:<port>] */
M
Matthias Bolte 已提交
127 128 129 130
            (*parsedUri)->proxy = true;
            (*parsedUri)->proxy_type = CURLPROXY_HTTP;
            VIR_FREE((*parsedUri)->proxy_hostname);
            (*parsedUri)->proxy_port = 1080;
M
Matthias Bolte 已提交
131 132

            if ((tmp = STRSKIP(queryParam->value, "http://")) != NULL) {
M
Matthias Bolte 已提交
133
                (*parsedUri)->proxy_type = CURLPROXY_HTTP;
M
Matthias Bolte 已提交
134 135
            } else if ((tmp = STRSKIP(queryParam->value, "socks://")) != NULL ||
                       (tmp = STRSKIP(queryParam->value, "socks5://")) != NULL) {
M
Matthias Bolte 已提交
136
                (*parsedUri)->proxy_type = CURLPROXY_SOCKS5;
M
Matthias Bolte 已提交
137
            } else if ((tmp = STRSKIP(queryParam->value, "socks4://")) != NULL) {
M
Matthias Bolte 已提交
138
                (*parsedUri)->proxy_type = CURLPROXY_SOCKS4;
M
Matthias Bolte 已提交
139
            } else if ((tmp = STRSKIP(queryParam->value, "socks4a://")) != NULL) {
M
Matthias Bolte 已提交
140
                (*parsedUri)->proxy_type = CURLPROXY_SOCKS4A;
M
Matthias Bolte 已提交
141 142 143 144 145 146 147 148 149 150 151 152
            } else if ((tmp = strstr(queryParam->value, "://")) != NULL) {
                *tmp = '\0';

                ESX_ERROR(VIR_ERR_INVALID_ARG,
                          _("Query parameter 'proxy' contains unexpected "
                            "type '%s' (should be (http|socks(|4|4a|5))"),
                          queryParam->value);
                goto cleanup;
            } else {
                tmp = queryParam->value;
            }

M
Matthias Bolte 已提交
153
            (*parsedUri)->proxy_hostname = strdup(tmp);
M
Matthias Bolte 已提交
154

M
Matthias Bolte 已提交
155
            if ((*parsedUri)->proxy_hostname == NULL) {
M
Matthias Bolte 已提交
156 157 158 159
                virReportOOMError();
                goto cleanup;
            }

M
Matthias Bolte 已提交
160 161
            if ((tmp = strchr((*parsedUri)->proxy_hostname, ':')) != NULL) {
                if (tmp == (*parsedUri)->proxy_hostname) {
M
Matthias Bolte 已提交
162 163 164 165 166 167 168 169 170
                    ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
                              _("Query parameter 'proxy' doesn't contain a "
                                "hostname"));
                    goto cleanup;
                }

                *tmp++ = '\0';

                if (virStrToLong_i(tmp, NULL, 10,
M
Matthias Bolte 已提交
171 172 173
                                   &(*parsedUri)->proxy_port) < 0 ||
                    (*parsedUri)->proxy_port < 1 ||
                    (*parsedUri)->proxy_port > 65535) {
M
Matthias Bolte 已提交
174 175 176 177 178 179
                    ESX_ERROR(VIR_ERR_INVALID_ARG,
                              _("Query parameter 'proxy' has unexpected port"
                                "value '%s' (should be [1..65535])"), tmp);
                    goto cleanup;
                }
            }
180 181 182 183 184 185
        } else {
            VIR_WARN("Ignoring unexpected query parameter '%s'",
                     queryParam->name);
        }
    }

M
Matthias Bolte 已提交
186
    if (uri->path != NULL) {
187
        (*parsedUri)->path = strdup(uri->path);
188

189
        if ((*parsedUri)->path == NULL) {
M
Matthias Bolte 已提交
190 191 192 193 194 195 196 197 198
            virReportOOMError();
            goto cleanup;
        }
    }

    if ((*parsedUri)->transport == NULL) {
        (*parsedUri)->transport = strdup("https");

        if ((*parsedUri)->transport == NULL) {
199
            virReportOOMError();
M
Matthias Bolte 已提交
200
            goto cleanup;
201 202 203
        }
    }

M
Matthias Bolte 已提交
204
    result = 0;
205

M
Matthias Bolte 已提交
206 207
  cleanup:
    if (result < 0) {
M
Matthias Bolte 已提交
208
        esxUtil_FreeParsedUri(parsedUri);
209 210
    }

M
Matthias Bolte 已提交
211 212
    if (queryParamSet != NULL) {
        free_qparam_set(queryParamSet);
213 214
    }

M
Matthias Bolte 已提交
215
    return result;
216 217 218 219
}



220 221

void
M
Matthias Bolte 已提交
222
esxUtil_FreeParsedUri(esxUtil_ParsedUri **parsedUri)
223
{
M
Matthias Bolte 已提交
224
    if (parsedUri == NULL || *parsedUri == NULL) {
225 226 227
        return;
    }

M
Matthias Bolte 已提交
228 229 230
    VIR_FREE((*parsedUri)->transport);
    VIR_FREE((*parsedUri)->vCenter);
    VIR_FREE((*parsedUri)->proxy_hostname);
231
    VIR_FREE((*parsedUri)->path);
232

M
Matthias Bolte 已提交
233
    VIR_FREE(*parsedUri);
234 235 236 237
}



238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
int
esxUtil_ParseVirtualMachineIDString(const char *id_string, int *id)
{
    /* Try to parse an integer from the complete string. */
    if (virStrToLong_i(id_string, NULL, 10, id) == 0) {
        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-")) {
        if (virStrToLong_i(id_string + 3, NULL, 10, id) == 0) {
            return 0;
        }
    }

    return -1;
}



M
Matthias Bolte 已提交
261
int
262
esxUtil_ParseDatastorePath(const char *datastorePath, char **datastoreName,
263
                           char **directoryName, char **directoryAndFileName)
M
Matthias Bolte 已提交
264
{
M
Matthias Bolte 已提交
265
    int result = -1;
266
    char *copyOfDatastorePath = NULL;
267 268 269
    char *tmp = NULL;
    char *saveptr = NULL;
    char *preliminaryDatastoreName = NULL;
270
    char *preliminaryDirectoryAndFileName = NULL;
M
Matthias Bolte 已提交
271

272 273 274
    if ((datastoreName != NULL && *datastoreName != NULL) ||
        (directoryName != NULL && *directoryName != NULL) ||
        (directoryAndFileName != NULL && *directoryAndFileName != NULL)) {
275
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
276
        return -1;
M
Matthias Bolte 已提交
277 278
    }

279
    if (esxVI_String_DeepCopyValue(&copyOfDatastorePath, datastorePath) < 0) {
M
Matthias Bolte 已提交
280
        goto cleanup;
281 282
    }

283 284 285
    /* Expected format: '[<datastore>] <path>' where <path> is optional */
    if ((tmp = STRSKIP(copyOfDatastorePath, "[")) == NULL || *tmp == ']' ||
        (preliminaryDatastoreName = strtok_r(tmp, "]", &saveptr)) == NULL) {
286
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
287 288
                  _("Datastore path '%s' doesn't have expected format "
                    "'[<datastore>] <path>'"), datastorePath);
M
Matthias Bolte 已提交
289
        goto cleanup;
M
Matthias Bolte 已提交
290 291
    }

292 293
    if (datastoreName != NULL &&
        esxVI_String_DeepCopyValue(datastoreName,
294
                                   preliminaryDatastoreName) < 0) {
M
Matthias Bolte 已提交
295
        goto cleanup;
296 297
    }

298
    preliminaryDirectoryAndFileName = strtok_r(NULL, "", &saveptr);
299

300 301 302 303 304 305
    if (preliminaryDirectoryAndFileName == NULL) {
        preliminaryDirectoryAndFileName = (char *)"";
    } else {
        preliminaryDirectoryAndFileName +=
          strspn(preliminaryDirectoryAndFileName, " ");
    }
M
Matthias Bolte 已提交
306

307 308 309 310 311
    if (directoryAndFileName != NULL &&
        esxVI_String_DeepCopyValue(directoryAndFileName,
                                   preliminaryDirectoryAndFileName) < 0) {
        goto cleanup;
    }
M
Matthias Bolte 已提交
312

313
    if (directoryName != NULL) {
314 315
        /* Split <path> into <directory>/<file> and remove /<file> */
        tmp = strrchr(preliminaryDirectoryAndFileName, '/');
316

317 318
        if (tmp != NULL) {
            *tmp = '\0';
M
Matthias Bolte 已提交
319 320
        }

321
        if (esxVI_String_DeepCopyValue(directoryName,
322
                                       preliminaryDirectoryAndFileName) < 0) {
M
Matthias Bolte 已提交
323
            goto cleanup;
324
        }
M
Matthias Bolte 已提交
325 326
    }

M
Matthias Bolte 已提交
327 328
    result = 0;

M
Matthias Bolte 已提交
329
  cleanup:
M
Matthias Bolte 已提交
330
    if (result < 0) {
331 332 333 334 335 336 337 338 339 340 341
        if (datastoreName != NULL) {
            VIR_FREE(*datastoreName);
        }

        if (directoryName != NULL) {
            VIR_FREE(*directoryName);
        }

        if (directoryAndFileName != NULL) {
            VIR_FREE(*directoryAndFileName);
        }
M
Matthias Bolte 已提交
342 343
    }

344
    VIR_FREE(copyOfDatastorePath);
M
Matthias Bolte 已提交
345 346 347 348 349 350

    return result;
}



351
int
352
esxUtil_ResolveHostname(const char *hostname,
M
Matthias Bolte 已提交
353
                        char *ipAddress, size_t ipAddress_length)
354 355 356 357 358
{
    struct addrinfo hints;
    struct addrinfo *result = NULL;
    int errcode;

M
Matthias Bolte 已提交
359
    memset(&hints, 0, sizeof (hints));
360 361 362 363 364 365 366 367 368

    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) {
369
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
370
                  _("IP address lookup for host '%s' failed: %s"), hostname,
371 372 373 374 375
                  gai_strerror(errcode));
        return -1;
    }

    if (result == NULL) {
376
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
377
                  _("No IP address for host '%s' found: %s"), hostname,
378 379 380 381
                  gai_strerror(errcode));
        return -1;
    }

M
Matthias Bolte 已提交
382 383
    errcode = getnameinfo(result->ai_addr, result->ai_addrlen, ipAddress,
                          ipAddress_length, NULL, 0, NI_NUMERICHOST);
384 385

    if (errcode != 0) {
386
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
E
Eric Blake 已提交
387
                  _("Formatting IP address for host '%s' failed: %s"), hostname,
388 389 390 391 392 393 394 395 396 397 398 399
                  gai_strerror(errcode));
        freeaddrinfo(result);
        return -1;
    }

    freeaddrinfo(result);

    return 0;
}



400 401 402 403 404 405
int
esxUtil_ReformatUuid(const char *input, char *output)
{
    unsigned char uuid[VIR_UUID_BUFLEN];

    if (virUUIDParse(input, uuid) < 0) {
406 407 408
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
                  _("Could not parse UUID from string '%s'"),
                  input);
409 410 411 412 413 414 415
        return -1;
    }

    virUUIDFormat(uuid, output);

    return 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 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523



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]);

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

                if (length > 2) {
                    virBufferAddChar(&buffer, base64[c3 & 0x3f]);
                }

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

            if (*tmp1 != '\0') {
                virBufferAddChar(&buffer, '-');
            }
        }
    }

    if (virBufferError(&buffer)) {
        virReportOOMError();
        virBufferFreeAndReset(&buffer);

        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;
        }

        if (*tmp != '\0') {
            ++tmp;
        }
    }
}



char *
esxUtil_EscapeDatastoreItem(const char *string)
{
    char *replaced = strdup(string);
    char *escaped1;
    char *escaped2 = NULL;

    if (replaced == NULL) {
        virReportOOMError();
        return NULL;
    }

    esxUtil_ReplaceSpecialWindowsPathChars(replaced);

524
    escaped1 = virVMXEscapeHexPercent(replaced);
525 526 527 528 529 530 531 532 533 534 535 536 537

    if (escaped1 == NULL) {
        goto cleanup;
    }

    escaped2 = esxUtil_EscapeBase64(escaped1);

  cleanup:
    VIR_FREE(replaced);
    VIR_FREE(escaped1);

    return escaped2;
}
M
Matthias Bolte 已提交
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556



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

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

    if (virBufferError(&buffer)) {
        virReportOOMError();
        virBufferFreeAndReset(&buffer);

        return NULL;
    }

    return virBufferContentAndReset(&buffer);
}