libvirt/src/esx/esx_util.c

573 lines
16 KiB
C
Raw Normal View History

/*
* esx_util.c: utility functions for the VMware ESX driver
*
build: consistently use C99 varargs macros Prior to this patch, there was an inconsistent mix between GNU and C99. For consistency, and potential portability to other compilers, stick with the C99 vararg macro syntax. * src/conf/cpu_conf.c (virCPUReportError): Use C99 rather than GNU vararg macro syntax. * src/conf/domain_conf.c (virDomainReportError): Likewise. * src/conf/domain_event.c (eventReportError): Likewise. * src/conf/interface_conf.c (virInterfaceReportError): Likewise. * src/conf/network_conf.c (virNetworkReportError): Likewise. * src/conf/node_device_conf.h (virNodeDeviceReportError): Likewise. * src/conf/secret_conf.h (virSecretReportError): Likewise. * src/conf/storage_conf.h (virStorageReportError): Likewise. * src/esx/esx_device_monitor.c (ESX_ERROR): Use C99 rather than GNU vararg macro syntax. * src/esx/esx_driver.c (ESX_ERROR): Likewise. * src/esx/esx_interface_driver.c (ESX_ERROR): Likewise. * src/esx/esx_network_driver.c (ESX_ERROR): Likewise. * src/esx/esx_secret_driver.c (ESX_ERROR): Likewise. * src/esx/esx_storage_driver.c (ESX_ERROR): Likewise. * src/esx/esx_util.c (ESX_ERROR): Likewise. * src/esx/esx_vi.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vi_methods.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vi_types.c (ESX_VI_ERROR): Likewise. * src/esx/esx_vmx.c (ESX_ERROR): Likewise. * src/util/hostusb.c (usbReportError): Use C99 rather than GNU vararg macro syntax. * src/util/json.c (virJSONError): Likewise. * src/util/macvtap.c (ReportError): Likewise. * src/util/pci.c (pciReportError): Likewise. * src/util/stats_linux.c (virStatsError): Likewise. * src/util/util.c (virUtilError): Likewise. * src/util/xml.c (virXMLError): Likewise. * src/xen/proxy_internal.c (virProxyError): Use C99 rather than GNU vararg macro syntax. * src/xen/sexpr.c (virSexprError): Likewise. * src/xen/xen_driver.c (xenUnifiedError): Likewise. * src/xen/xen_hypervisor.c (virXenError): Likewise. * src/xen/xen_inotify.c (virXenInotifyError): Likewise. * src/xen/xend_internal.c (virXendError): Likewise. * src/xen/xm_internal.c (xenXMError): Likewise. * src/xen/xs_internal.c (virXenStoreError): Likewise. * src/cpu/cpu.h (virCPUReportError): Use C99 rather than GNU vararg macro syntax. * src/datatypes.c (virLibConnError): Likewise. * src/interface/netcf_driver.c (interfaceReportError): Likewise. * src/libvirt.c (virLibStreamError): Likewise. * src/lxc/lxc_conf.h (lxcError): Likewise. * src/network/bridge_driver.c (networkReportError): Likewise. * src/nodeinfo.c (nodeReportError): Likewise. * src/opennebula/one_conf.h (oneError): Likewise. * src/openvz/openvz_conf.h (openvzError): Likewise. * src/phyp/phyp_driver.c (PHYP_ERROR): Likewise. * src/qemu/qemu_conf.h (qemuReportError): Likewise. * src/remote/remote_driver.c (errorf): Likewise. * src/security/security_driver.h (virSecurityReportError): Likewise. * src/test/test_driver.c (testError): Likewise. * src/uml/uml_conf.h (umlReportError): Likewise. * src/vbox/vbox_driver.c (vboxError): Likewise. * src/vbox/vbox_tmpl.c (vboxError): Likewise.
2010-03-01 23:38:28 +00:00
* Copyright (C) 2010 Red Hat, Inc.
* Copyright (C) 2009-2011 Matthias Bolte <matthias.bolte@googlemail.com>
* 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"
#include "vmx.h"
#include "esx_private.h"
#include "esx_util.h"
#define VIR_FROM_THIS VIR_FROM_ESX
int
esxUtil_ParseUri(esxUtil_ParsedUri **parsedUri, xmlURIPtr uri)
{
int result = -1;
struct qparam_set *queryParamSet = NULL;
struct qparam *queryParam = NULL;
int i;
int noVerify;
int autoAnswer;
char *tmp;
char *saveptr;
if (parsedUri == NULL || *parsedUri != NULL) {
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
return -1;
}
if (VIR_ALLOC(*parsedUri) < 0) {
virReportOOMError();
return -1;
}
#ifdef HAVE_XMLURI_QUERY_RAW
queryParamSet = qparam_query_parse(uri->query_raw);
#else
queryParamSet = qparam_query_parse(uri->query);
#endif
if (queryParamSet == NULL) {
goto cleanup;
}
for (i = 0; i < queryParamSet->n; i++) {
queryParam = &queryParamSet->p[i];
if (STRCASEEQ(queryParam->name, "transport")) {
VIR_FREE((*parsedUri)->transport);
(*parsedUri)->transport = strdup(queryParam->value);
if ((*parsedUri)->transport == NULL) {
virReportOOMError();
goto cleanup;
}
if (STRNEQ((*parsedUri)->transport, "http") &&
STRNEQ((*parsedUri)->transport, "https")) {
ESX_ERROR(VIR_ERR_INVALID_ARG,
_("Query parameter 'transport' has unexpected value "
"'%s' (should be http|https)"),
(*parsedUri)->transport);
goto cleanup;
}
} else if (STRCASEEQ(queryParam->name, "vcenter")) {
VIR_FREE((*parsedUri)->vCenter);
(*parsedUri)->vCenter = strdup(queryParam->value);
if ((*parsedUri)->vCenter == NULL) {
virReportOOMError();
goto cleanup;
}
} else if (STRCASEEQ(queryParam->name, "no_verify")) {
if (virStrToLong_i(queryParam->value, NULL, 10, &noVerify) < 0 ||
(noVerify != 0 && noVerify != 1)) {
ESX_ERROR(VIR_ERR_INVALID_ARG,
_("Query parameter 'no_verify' has unexpected value "
"'%s' (should be 0 or 1)"), queryParam->value);
goto cleanup;
}
(*parsedUri)->noVerify = noVerify != 0;
} else if (STRCASEEQ(queryParam->name, "auto_answer")) {
if (virStrToLong_i(queryParam->value, NULL, 10, &autoAnswer) < 0 ||
(autoAnswer != 0 && autoAnswer != 1)) {
ESX_ERROR(VIR_ERR_INVALID_ARG,
_("Query parameter 'auto_answer' has unexpected "
"value '%s' (should be 0 or 1)"), queryParam->value);
goto cleanup;
}
(*parsedUri)->autoAnswer = autoAnswer != 0;
} else if (STRCASEEQ(queryParam->name, "proxy")) {
/* Expected format: [<type>://]<hostname>[:<port>] */
(*parsedUri)->proxy = true;
(*parsedUri)->proxy_type = CURLPROXY_HTTP;
VIR_FREE((*parsedUri)->proxy_hostname);
(*parsedUri)->proxy_port = 1080;
if ((tmp = STRSKIP(queryParam->value, "http://")) != NULL) {
(*parsedUri)->proxy_type = CURLPROXY_HTTP;
} else if ((tmp = STRSKIP(queryParam->value, "socks://")) != NULL ||
(tmp = STRSKIP(queryParam->value, "socks5://")) != NULL) {
(*parsedUri)->proxy_type = CURLPROXY_SOCKS5;
} else if ((tmp = STRSKIP(queryParam->value, "socks4://")) != NULL) {
(*parsedUri)->proxy_type = CURLPROXY_SOCKS4;
} else if ((tmp = STRSKIP(queryParam->value, "socks4a://")) != NULL) {
(*parsedUri)->proxy_type = CURLPROXY_SOCKS4A;
} 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;
}
(*parsedUri)->proxy_hostname = strdup(tmp);
if ((*parsedUri)->proxy_hostname == NULL) {
virReportOOMError();
goto cleanup;
}
if ((tmp = strchr((*parsedUri)->proxy_hostname, ':')) != NULL) {
if (tmp == (*parsedUri)->proxy_hostname) {
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,
&(*parsedUri)->proxy_port) < 0 ||
(*parsedUri)->proxy_port < 1 ||
(*parsedUri)->proxy_port > 65535) {
ESX_ERROR(VIR_ERR_INVALID_ARG,
_("Query parameter 'proxy' has unexpected port"
"value '%s' (should be [1..65535])"), tmp);
goto cleanup;
}
}
} else {
VIR_WARN("Ignoring unexpected query parameter '%s'",
queryParam->name);
}
}
/* Expected format: [/]<datacenter>/<computeresource>[/<hostsystem>] */
if (uri->path != NULL) {
tmp = strdup(uri->path);
if (tmp == NULL) {
virReportOOMError();
goto cleanup;
}
if (esxVI_String_DeepCopyValue(&(*parsedUri)->path_datacenter,
strtok_r(tmp, "/", &saveptr)) < 0 ||
esxVI_String_DeepCopyValue(&(*parsedUri)->path_computeResource,
strtok_r(NULL, "/", &saveptr)) < 0 ||
esxVI_String_DeepCopyValue(&(*parsedUri)->path_hostSystem,
strtok_r(NULL, "", &saveptr)) < 0) {
VIR_FREE(tmp);
goto cleanup;
}
VIR_FREE(tmp);
}
if ((*parsedUri)->transport == NULL) {
(*parsedUri)->transport = strdup("https");
if ((*parsedUri)->transport == NULL) {
virReportOOMError();
goto cleanup;
}
}
result = 0;
cleanup:
if (result < 0) {
esxUtil_FreeParsedUri(parsedUri);
}
if (queryParamSet != NULL) {
free_qparam_set(queryParamSet);
}
return result;
}
void
esxUtil_FreeParsedUri(esxUtil_ParsedUri **parsedUri)
{
if (parsedUri == NULL || *parsedUri == NULL) {
return;
}
VIR_FREE((*parsedUri)->transport);
VIR_FREE((*parsedUri)->vCenter);
VIR_FREE((*parsedUri)->proxy_hostname);
VIR_FREE((*parsedUri)->path_datacenter);
VIR_FREE((*parsedUri)->path_computeResource);
VIR_FREE((*parsedUri)->path_hostSystem);
VIR_FREE(*parsedUri);
}
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;
}
int
esxUtil_ParseDatastorePath(const char *datastorePath, char **datastoreName,
char **directoryName, char **directoryAndFileName)
{
int result = -1;
char *copyOfDatastorePath = NULL;
char *tmp = NULL;
char *saveptr = NULL;
char *preliminaryDatastoreName = NULL;
char *preliminaryDirectoryAndFileName = NULL;
if ((datastoreName != NULL && *datastoreName != NULL) ||
(directoryName != NULL && *directoryName != NULL) ||
(directoryAndFileName != NULL && *directoryAndFileName != NULL)) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
return -1;
}
if (esxVI_String_DeepCopyValue(&copyOfDatastorePath, datastorePath) < 0) {
goto cleanup;
}
/* Expected format: '[<datastore>] <path>' where <path> is optional */
if ((tmp = STRSKIP(copyOfDatastorePath, "[")) == NULL || *tmp == ']' ||
(preliminaryDatastoreName = strtok_r(tmp, "]", &saveptr)) == NULL) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Datastore path '%s' doesn't have expected format "
"'[<datastore>] <path>'"), datastorePath);
goto cleanup;
}
if (datastoreName != NULL &&
esxVI_String_DeepCopyValue(datastoreName,
preliminaryDatastoreName) < 0) {
goto cleanup;
}
preliminaryDirectoryAndFileName = strtok_r(NULL, "", &saveptr);
if (preliminaryDirectoryAndFileName == NULL) {
preliminaryDirectoryAndFileName = (char *)"";
} else {
preliminaryDirectoryAndFileName +=
strspn(preliminaryDirectoryAndFileName, " ");
}
if (directoryAndFileName != NULL &&
esxVI_String_DeepCopyValue(directoryAndFileName,
preliminaryDirectoryAndFileName) < 0) {
goto cleanup;
}
if (directoryName != NULL) {
/* Split <path> into <directory>/<file> and remove /<file> */
tmp = strrchr(preliminaryDirectoryAndFileName, '/');
if (tmp != NULL) {
*tmp = '\0';
}
if (esxVI_String_DeepCopyValue(directoryName,
preliminaryDirectoryAndFileName) < 0) {
goto cleanup;
}
}
result = 0;
cleanup:
if (result < 0) {
if (datastoreName != NULL) {
VIR_FREE(*datastoreName);
}
if (directoryName != NULL) {
VIR_FREE(*directoryName);
}
if (directoryAndFileName != NULL) {
VIR_FREE(*directoryAndFileName);
}
}
VIR_FREE(copyOfDatastorePath);
return result;
}
int
esxUtil_ResolveHostname(const char *hostname,
char *ipAddress, size_t ipAddress_length)
{
struct addrinfo hints;
struct addrinfo *result = NULL;
int errcode;
memset(&hints, 0, sizeof (hints));
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) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("IP address lookup for host '%s' failed: %s"), hostname,
gai_strerror(errcode));
return -1;
}
if (result == NULL) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("No IP address for host '%s' found: %s"), hostname,
gai_strerror(errcode));
return -1;
}
errcode = getnameinfo(result->ai_addr, result->ai_addrlen, ipAddress,
ipAddress_length, NULL, 0, NI_NUMERICHOST);
if (errcode != 0) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Formating IP address for host '%s' failed: %s"), hostname,
gai_strerror(errcode));
freeaddrinfo(result);
return -1;
}
freeaddrinfo(result);
return 0;
}
int
esxUtil_ReformatUuid(const char *input, char *output)
{
unsigned char uuid[VIR_UUID_BUFLEN];
if (virUUIDParse(input, uuid) < 0) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Could not parse UUID from string '%s'"),
input);
return -1;
}
virUUIDFormat(uuid, output);
return 0;
}
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);
escaped1 = virVMXEscapeHexPercent(replaced);
if (escaped1 == NULL) {
goto cleanup;
}
escaped2 = esxUtil_EscapeBase64(escaped1);
cleanup:
VIR_FREE(replaced);
VIR_FREE(escaped1);
return escaped2;
}
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);
}